Este artículo analizará la asignación de programación 3 y 4 en redes neuronales del Curso de aprendizaje automático de Andrew Ng. Este es también el primer algoritmo no lineal complejo que hemos encontrado hasta ahora en el curso. No sé sobre usted, pero definitivamente hay una curva de aprendizaje empinada para esta tarea para mí. La red neuronal constituye la base del aprendizaje profundo que tiene una aplicación generalizada, como la visión por computadora o el procesamiento del lenguaje natural. Como tal, es importante obtener el derecho fundamental y codificar estas asignaciones en python es una forma de garantizarlo.
Antes de ingresar a las redes neuronales, completemos la última sección para regresión logística: Regresión logística multiclase.
Esta serie de ejercicios hace uso de un conjunto de datos de dígitos manuscritos que consta de 5000 ejemplos de entrenamiento, donde cada ejemplo es una imagen en escala de grises de 20 píxeles por 20 píxeles.
Cargando el conjunto de datos
import numpy como np import pandas como pd import matplotlib.pyplot by plt from scipy.io import loadmat
# Usar loadmat para cargar archivos matlab mat = loadmat ("ex3data1.mat") X = mat ["X"] y = mat ["y"]
Como el conjunto de datos se proporcionó en formato .mat en lugar del formato .txt habitual, tuve que usar la función scipy loadmat para el trabajo. La documentación oficial se puede encontrar aquí . Ya que loadmat carga archivos .mat como un diccionario con nombres de variables como claves, asignar X e y es tan simple como acceder al dict con las claves de las variables.
X.shape, y.shape
Para entender mejor el conjunto de datos , teniendo la forma de los datos nos dice la dimensión de los datos. X tiene una forma de 5000,400 que corresponde a 5000 ejemplos de entrenamiento, cada uno con 400 características de sus 20 X 20 píxeles. y tiene una forma de 5000,1, en la que cada ejemplo de entrenamiento tiene una etiqueta que varía de 1 a 10 (el dígito ‘0’ está etiquetado como ’10’ en este conjunto de datos).
Visualización de los datos
import matplotlib. imagen como mpimg fig, axis = plt.subplots (10,10, figsize = (8,8)) from i en el rango (10): from j en el rango (10): eje [i,j] .imshow (X [np.random.randint(0,5001),:] .reshape (20,20, order = "F"), cmap = "hot") #reshape de nuevo a 20 píxeles por 20 píxeles eje [i,j] .axis ("off")
El bloque de código de arriba construye 100 subparcelas y visualiza aleatoriamente 100 de los 5000 ejemplos de entrenamiento usando plt.imshow . Tenga en cuenta que tenemos que cambiar la forma del ejemplo de entrenamiento de nuevo a 20 X 20 píxeles antes de poder visualizarlo y agregar orden = “F” como un parámetro en la función de reforma. Asegúrese de que la orientación de la imagen sea vertical.
Cálculo del costo función y gradiente
def sigmoid (z): "" " devuelve el sigmoide de z " "" return 1 / (1 + np.exp (-z)) [19659007] def lrCostFunction (theta, X, y, Lambda): "" " Toma una gran variedad de theta, X, y, y float lambda para calcular la función de costo logístico regularizado " "" m = len (y) predicciones = sigmoide (X @ theta) error = (-y * np.log (predicciones)) - ((1-y) * np.log (1- predictions)) cost = 1 / m * sum (error) regCost = cost + Lambda / (2 * m) * sum (theta [1:] ** 2) # compute gradient j_0 = 1 / m * (X.transpose () @ (predicciones - y)) [0] j_1 = 1 / m * (X.transpose () @ (predicciones - y)) [1:] + (Lambda / m) * theta [1:] grad = np.vstack ((j_0 [:,np.newaxis]j_1)) return regCost [0]grad
Esto es similar a la función de costo que usamos de vuelta en la Regresión logística asignación.
theta_t = np.array ([-2,-1,1,2]). reshape (4,1) X_t = np.array ([np.linspace(0.1,1.5,15)]). reshape (3 , 5) .T X_t = np.hstack ((np.ones ((5,1)), X_t)) y_t = np.array ([1,0,1,0,1]). Reformar (5,1) J, grad = lrCostFunction (theta_t, X_t, y_t, 3) print ("Costo:", J, "Costo esperado: 2.534819") print ("Gradients: n", grad, " n Gradientes esperados: n 0.146561 n -0.548558 n 0.724722 n 1.398003 ")
Ahora para la tarea de clasificación. Ya que tenemos más de una clase, tendremos que entrenar múltiples clasificadores de regresión logística utilizando el método de clasificación uno-contra-todo (un clasificador para cada clase).
def gradientDescent (X, y, theta, alpha, num_iters, Lambda ): "" " Tome la matriz numpy X, y y theta y actualice theta tomando pasos gradiente de num_iters con tasa de aprendizaje de alfa devuelva theta y la lista del costo de theta durante cada iteración "" " m = len (y) J_history = [] from i en rango (num_iters): costo, grad = lrCostFunction (theta, X, y , Lambda) theta = theta - (alfa * grad) J_history.append (costo) return theta, J_history
def oneVsAll (X, y, num_labels, Lambda): "" " Toma una matriz numpy de X, y, int num_labels y float lambda para entrenar clasificadores de regresión logística múltiple según el número o f num_labels utilizando pendiente de gradiente. Devuelve una matriz de theta, donde la fila i-th corresponde al clasificador de la etiqueta i "" m, n = X.shape [0]X.shape [1] initial_theta = np.zeros ((n + 1,1)) all_theta = [] all_J = [] # agregar términos de intercepción X = np.hstack ((np.ones ((m, 1)), X)) para i en el rango (1, num_labels + 1): theta, J_history = gradientDescent (X, np.where (y == i, 1,0), initial_theta, 1,300, Lambda) all_theta.extend (theta) all_J.extend (J_history) return np.array (all_theta) .reshape (num_labels, n + 1), all_J
La función gradientDescent es la función de optimización habitual que lo habíamos implementado previamente. En cuanto a oneVsAll, itera a través de todas las clases y entrenó un conjunto de theta para cada clase usando gradiente de pendiente (se utilizó la función fmincg en la asignación). all_theta luego captura todo el theta optimizado en una lista y regresa como una matriz numpy, remodelada en una matriz de theta donde la fila i-th corresponde al clasificador de la etiqueta i. np.where es útil aquí para obtener un vector de y con 1/0 para cada clase para llevar a cabo nuestra tarea de clasificación binaria dentro de cada iteración.
Representación de la función de costo solo para garantizar que el descenso del gradiente funcione según lo previsto
.plot (all_J [0:300]) plt.xlabel ("Iteration") plt.ylabel ("$ J ( Theta) $") plt.title ("Función de costo usando gradiente de pendiente" )
Para hacer una predicción, se calculó la probabilidad de x (i) para cada clase y la predicción es la clase con la mayor probabilidad
def predictOneVsAll (all_theta , X): "" " Usando all_theta, calcula la probabilidad de X (i) para cada clase y predice la etiqueta devuelve un vector de predicción " "" m = X.shape [0] X = np.hstack ((np.ones ((m, 1)), X)) predictions = X @ all_theta.T return np.argmax (predictions, axis = 1 ) +1
pred = predictOneVsAll (all_theta, X) print ("Precisión del conjunto de entrenamiento:", suma (pred [:,np.newaxis] == y) [0] / 5000 * 100, "%")
La impresión del extracto de impresión: Precisión del conjunto de entrenamiento: 91.46% [19659003] Finalmente, tiempo para redes neuronales. Con el mismo conjunto de datos, nuestro objetivo fue lograr una mayor precisión utilizando un algoritmo más complejo, como la red neuronal. Para la primera parte del ejercicio, se nos dieron los valores theta optimizados y se supone que debemos implementar la propagación feedforward para obtener la predicción y la precisión del modelo.
Carga del theta optimizado
mat2 = loadmat ("ex3weights". mat ") Theta1 = mat2 ["Theta1"] # Theta1 tiene un tamaño de 25 x 401 Theta2 = mat2 ["Theta2"] # Theta2 tiene un tamaño de 10 x 26
Usando la propagación avanzada para la predicción
def predic (Theta1 , Theta2, X): "" " Predice la etiqueta de una entrada dada una red neuronal entrenada " "" m = X.shape [0] X = np.hstack (( np.ones ((m, 1)), X)) a1 = sigmoide (X @ Theta1.T) a1 = np.hstack ((np.ones ((m, 1)), a1) ) # capa oculta a2 = sigmoide (a1 @ Theta2.T) # capa de salida devuelve np.argmax (a2, axis = 1) +1
pred2 = predecir (Theta1, Theta2, X) print ("Training Set Accuracy:", sum (pred2 [:,np.newaxis] == y) [0] / 5000 * 100, "%")
El estado de impresión Impresión: conjunto de entrenamiento de precisión: 97.52%. ¡Una precisión mucho mayor en comparación con la regresión logística multiclase!
En la asignación 4, trabajamos para implementar una red neuronal desde cero. Comenzamos calculando la función de costo y el gradiente de theta.
def sigmoidGradient (z): "" calcula el gradiente de la función sigmoide "" sigmoid = 1 / (1 + np.exp (-z)) return sigmoid * (1-sigmoid)
def nnCostFunction (nn_params, input_layer_size, hidden_layer_size, num_labels, X, y, Lambda): "" " nn_params contiene los parámetros desenrollados en un vector calcula el costo y el gradiente de la red neuronal " "" # Remodela nn_params de nuevo en los parámetros Theta1 y Theta2 Theta1 = nn_params [:((input_layer_size+1) * hidden_layer_size)] .reshape (hidden_layer_size, input_layer_size + 1) Theta2 = nn_params [((input_layer_size +1)* hidden_layer_size ):] .reshape (num_labels, hidden_layer_size + 1) m = X.shape [0] np.hstack ((np.ones ((m, 1)), X)) y10 = np.zeros ((m, num_labels)) a1 = sigmoide (X @ Theta1.T) a1 = np.hstack ((np.ones ((m, 1)), a1)) # capa oculta a2 = sigmoide (a1 @ Theta2.T) # capa de salida para i en el rango (1, num_labels + 1): y10 [:,i-1][:,np.newaxis] = np.where (y == i, 1,0) para j en rango (num_labels): J = suma + (-y10 [:,j] * np.log (a2 [:,j]) - (1-y10 [:,j]) * np.log (1-a2 [:,j])) costo = 1 / m * J reg_J = costo + Lambda / (2 * m) * (np.sum (Theta1 [:,1:] * * 2) + np.sum (Theta2 [:,1:] ** 2)) # Implementar el algoritmo de propagación hacia atrás para calcular los gradientes grad1 = np.zeros ((Theta1.shape)) grad2 = np .zeros ((Theta2.shape)) para i en el rango (m): xi = X [i,:] # 1 X 401 a1i = a1 [i,:] # 1 X 26 a2i = a2 [i,:] # 1 X 10 d2 = a2i - y10 [i,:] d1 = Theta2.T @ d2.T * sigmoidGradient (np.hstack ((1, xi @ Theta1.T))) grad1 = grad1 + d1 [1:][:,np.newaxis] @ xi [:,np.newaxis] .T grad2 = grad2 + d2.T [:,np.newaxis] @ a1i [:,np.newaxis] .T grad1 = 1 / m * grad1 grad2 = 1 / m * grad2 grad1_reg = grad1 + (Lambda / m) * np.hstack ((np.zeros ((Theta1.shape [0]1)), Theta1 [:,1:])) grad2_reg = grad2 + (Lambda / m) * np.hstack ((np.zeros ((Theta2.shape [0]1)), Theta2 [:,1:])) costo de devolución, grad1, grad2, reg_J, grad1_reg, grad2_reg
La asignación recorre todo el proceso paso a paso, calculando el costo primero, seguido del costo regularizado, el gradiente y finalmente el gradiente regularizado. Si desea seguir adelante, modifiqué el código para que devuelva valores para los pasos intermedios siempre que utilice la indexación correcta.
input_layer_size = 400 hidden_layer_size = 25 num_labels = 10 nn_params = np.append (Theta1.flatten (), Theta2.flatten ()) J, reg_J = nnCostFunction (nn_params, input_layer_size, hidden_layer_size, num_labels, X, y, 1) [0:4:3] print ("costo en los parámetros (no regularizado): ", J", nEl costo en los parámetros (Regularizado): ", reg_J)
aquí la función aplanar () colapsó la matriz en una dimensión y np.appende” desenrollar “los parámetros en un vector.
El tema de la simetría para theta inicial se discutió en la conferencia. Para romper dicha simetría, se necesita una inicialización aleatoria.
def randInitializeWeights (L_in, L_out): "" inicializa aleatoriamente los pesos de una capa con L_in conexiones entrantes y L_out conexiones salientes. "" " epi = (6 ** 1/2) / (L_in + L_out) ** 1/2 W = np.random.rand (L_out, L_in +1) * (2 * epi) -epi return W
initial_Theta1 Vistas de los derechos de las partes de los ingresos de las partes de los derechos de las partes de los derechos de las partes de los buques de las partes
Finalmente, es nuestro turno de optimizar los valores theta utilizando la propagación de avance y la propagación hacia atrás. El algoritmo de optimización que estoy usando es, una vez más, el mismo antiguo gradiente de gradiente.
def gradientDescentnn (X, y, initial_nn_params, alpha, num_iters, Lambda, input_layer_size, hidden_layer_size, num_labels): "" en la matriz numpy X, y y theta y actualice theta tomando num_iters gradient steps con una tasa de aprendizaje de alfa devuelva theta y la lista del costo de theta durante cada iteración "" " Theta1 = initial_nn_params [:((input_layer_size+1) * hidden_layer_size)] .reshape (hidden_layer_size, input_layer_size + 1) Theta2 = initial_nn_params [((input_layer_size +1)* hidden_layer_size ):] .reshape (num_lab en el servicio de correo) 19659017] para i en el rango (número_itros): nn_params = np.append (Theta1.flatten (), Theta2.flatten ()) cost, grad1, grad_layer_pest.ny.png X, y, Lambda) [3:] Theta1 = Theta1 - (alfa * grad1) Theta2 = Theta2 - (alpha * grad2) J_history.append (cost) nn_paramsFinal = np.append (Theta1.flatten (), Theta2.flatten ()) 19199009] return nn_paramsFinal, J_history [19659007)nnJ_history=gradientDescentnn(Xyinitial_nn_params088001input_layer_sizehidden_layer_sizenum_labels)[0] [nremodelar(num_labelshidden_layer_size+1)
Una advertencia para quienes ejecutan el código. Tomará un poco de tiempo calcularlo en función de su potencia de cómputo, e incluso más si está optimizando sus valores alfa y num_iters. Utilizo 0.8 para alfa y 800 para num_iters pero creo que puede obtener una mayor precisión con más ajustes.
pred3 = predic (Theta1, Theta2, X) print ("Training Set Accuracy:", suma (pred3 [:,np.newaxis] == y) [0] / 5000 * 100, "%")
Con esto, estoy a mitad de la serie. El cuaderno jupyter se cargará en mi GitHub en ( https://github.com/Benlau93/Machine-Learning-by-Andrew-Ng-in-Python ).
Para la implementación de otros python en la serie
Regresión:
- Lineal
- Logística
- Logística regularizada
Gracias por leer.