tutorial - redes neuronales python pdf
Atascado implementando una red neuronal simple (1)
He estado golpeando mi cabeza contra esta pared de ladrillo por lo que parece una eternidad, y parece que no puedo entenderlo. Estoy tratando de implementar un autoencoder usando solo numpy y multiplicación de matrices. No se permiten trucos theano o keras.
Describiré el problema y todos sus detalles. Al principio es un poco complejo ya que hay muchas variables, pero en realidad es bastante sencillo.
Lo que sabemos
1) X
es una matriz de m
por n
que es nuestra entrada. Las entradas son filas de esta matriz. Cada entrada es un vector de fila n
dimensiones, y tenemos m
de ellas.
2) El número de neuronas en nuestra (única) capa oculta, que es k
.
3) La función de activación de nuestras neuronas (sigmoide, se denotará como g(x)
) y su derivada g''(x)
Lo que no sabemos y queremos encontrar
En general, nuestro objetivo es encontrar 6 matrices: w1
que es n
por k
, b1
que es m
por k
, w2
que es k
por n
, b2 que es m
por n
, w3
que es n
por n
b3
que es m
por n
.
Se inician aleatoriamente y encontramos la mejor solución usando el descenso de gradiente.
El proceso
Todo el proceso se ve algo como esto
Primero calculamos z1 = Xw1+b1
. Es m
por k
y es la entrada a nuestra capa oculta. Luego calculamos h1 = g(z1)
, que simplemente aplica la función sigmoidea a todos los elementos de z1
. naturalmente también es m
por k
y es la salida de nuestra capa oculta.
Luego calculamos z2 = h1w2+b2
que es m
por n
y es la entrada a la capa de salida de nuestra red neuronal. Luego calculamos h2 = g(z2)
que de nuevo también es naturalmente m
by n
y es la salida de nuestra red neuronal.
Finalmente, tomamos esta salida y realizamos un operador lineal sobre ella: Xhat = h2w3+b3
que también es m
by n
y es nuestro resultado final.
Donde estoy atascado
La función de costo que quiero minimizar es el error cuadrático medio. Ya lo implementé en código numpy
def cost(x, xhat):
return (1.0/(2 * m)) * np.trace(np.dot(x-xhat,(x-xhat).T))
El problema es encontrar los derivados del costo con respecto a w1,b1,w2,b2,w3,b3
. Vamos a llamar el costo S
Después de derivarme y controlarme numéricamente , he establecido los siguientes hechos:
1) dSdxhat = (1/m) * np.dot(xhat-x)
2) dSdw3 = np.dot(h2.T,dSdxhat)
3) dSdb3 = dSdxhat
4) dSdh2 = np.dot(dSdxhat, w3.T)
Pero no puedo, por la vida de mí, descubrir dSdz2. Es una pared de ladrillo.
Desde la regla de cadena, debería ser que dSdz2 = dSdh2 * dh2dz2 pero las dimensiones no coinciden.
¿Cuál es la fórmula para calcular la derivada de S con respecto a z2?
Editar : este es mi código para la operación de avance completo del autoencoder.
import numpy as np
def g(x): #sigmoid activation functions
return 1/(1+np.exp(-x)) #same shape as x!
def gGradient(x): #gradient of sigmoid
return g(x)*(1-g(x)) #same shape as x!
def cost(x, xhat): #mean squared error between x the data and xhat the output of the machine
return (1.0/(2 * m)) * np.trace(np.dot(x-xhat,(x-xhat).T))
#Just small random numbers so we can test that it''s working small scale
m = 5 #num of examples
n = 2 #num of features in each example
k = 2 #num of neurons in the hidden layer of the autoencoder
x = np.random.rand(m, n) #the data, shape (m, n)
w1 = np.random.rand(n, k) #weights from input layer to hidden layer, shape (n, k)
b1 = np.random.rand(m, k) #bias term from input layer to hidden layer (m, k)
z1 = np.dot(x,w1)+b1 #output of the input layer, shape (m, k)
h1 = g(z1) #input of hidden layer, shape (m, k)
w2 = np.random.rand(k, n) #weights from hidden layer to output layer of the autoencoder, shape (k, n)
b2 = np.random.rand(m, n) #bias term from hidden layer to output layer of autoencoder, shape (m, n)
z2 = np.dot(h1, w2)+b2 #output of the hidden layer, shape (m, n)
h2 = g(z2) #Output of the entire autoencoder. The output layer of the autoencoder. shape (m, n)
w3 = np.random.rand(n, n) #weights from output layer of autoencoder to entire output of the machine, shape (n, n)
b3 = np.random.rand(m, n) #bias term from output layer of autoencoder to entire output of the machine, shape (m, n)
xhat = np.dot(h2, w3)+b3 #the output of the machine, which hopefully resembles the original data x, shape (m, n)
OK, aquí hay una sugerencia. En el caso del vector, si usted tiene x como un vector de longitud n
, entonces g(x)
también es un vector de longitud n
. Sin embargo, g''(x)
no es un vector, es la matriz jacobiana , y será de tamaño n X n
. De manera similar, en el caso minibatch, donde X es una matriz de tamaño m X n
, g(X)
es m X n
pero g''(X)
es n X n
. Tratar:
def gGradient(x): #gradient of sigmoid
return np.dot(g(x).T, 1 - g(x))
@Paul tiene razón en que los términos de sesgo deberían ser vectores, no matrices. Deberías:
b1 = np.random.rand(k) #bias term from input layer to hidden layer (k,)
b2 = np.random.rand(n) #bias term from hidden layer to output layer of autoencoder, shape (n,)
b3 = np.random.rand(n) #bias term from output layer of autoencoder to entire output of the machine, shape (n,)
La transmisión de Numpy significa que no tiene que cambiar su cálculo de xhat
.
Entonces (¡creo!) Puedes calcular derivados como este:
dSdxhat = (1/float(m)) * (xhat-x)
dSdw3 = np.dot(h2.T,dSdxhat)
dSdb3 = dSdxhat.mean(axis=0)
dSdh2 = np.dot(dSdxhat, w3.T)
dSdz2 = np.dot(dSdh2, gGradient(z2))
dSdb2 = dSdz2.mean(axis=0)
dSdw2 = np.dot(h1.T,dSdz2)
dSdh1 = np.dot(dSdz2, w2.T)
dSdz1 = np.dot(dSdh1, gGradient(z1))
dSdb1 = dSdz1.mean(axis=0)
dSdw1 = np.dot(x.T,dSdz1)
¿Esto funciona para tí?
Editar
He decidido que no estoy seguro de que se gGradient
que gGradient
es una matriz. Qué tal si:
dSdxhat = (xhat-x) / m
dSdw3 = np.dot(h2.T,dSdxhat)
dSdb3 = dSdxhat.sum(axis=0)
dSdh2 = np.dot(dSdxhat, w3.T)
dSdz2 = h2 * (1-h2) * dSdh2
dSdb2 = dSdz2.sum(axis=0)
dSdw2 = np.dot(h1.T,dSdz2)
dSdh1 = np.dot(dSdz2, w2.T)
dSdz1 = h1 * (1-h1) * dSdh1
dSdb1 = dSdz1.sum(axis=0)
dSdw1 = np.dot(x.T,dSdz1)