June 29, 2018
Backpropagation y Gradient Descent
Gradient Descent
Gradient Descent es el algoritmo de optimización que usamos para minimizar la función de perdida (Lost function, cost function). Para lograr esto el algoritmo cambia los parametros W, b hasta encontrar los valores que minimicen a la función. Este cambio se realiza con el siguiente código:
W = W - learning_rate * dW
Como podemos observar necesitamos la derivada del parametro W que es dW y un hiperparametro llamado learning rate.
En una red neuronal la función de perdida se calcula varias veces, si la función aumento el algoritmo tiene que disminuir el valor de W y si la función disminuyo el algoritmo aumenta el valor de W:
Cuando la funcion de perdida aumenta el valor de la derivada dW es positivo, cuando multipliquemos dW positivo con el learning rate que es negativo obtendremos un resultado negativo que disminuirá el valor de W:
W = W - 0.003 * 0.04
Cuando la funcion de perdida disminuye la derivada dW es negativa y cuando multipliquemos la derivada negativa con el learning rate negativo obtendremos un resultado positivo que aumentará el valor de W:
W = W - 0.003 * -0.06
El learning rate podemos describirlo como la cantidad que W aumentará o disminuirá y la derivada podemos decir que nos da la respuesta de como cambiando el valor de W afecta al resultado de la función de perdida.
Podemos ilustrar el algoritmo de la siguiente manera:
La estrella amarilla indica la posición donde la función de perdida se minimiza y los puntos de colores el camino que W recorre para llegar al valor que minimiza a la función.
Todo este proceso se aplica para los parametros W y b de cada neurona de la red neuronal. En los ejemplos anteriores solo tome a W en cuenta para que la explicación sea sencilla.
Backpropagation
Para obtener las derivadas de W y de b tenemos que realizar una serie de pasos, a esto se le llama backpropagation.
En código usando python el backpropagation para nuestra red neuronal es el siguiente:
dZ2 = A2 - Y
dW2 = (dZ2 * A1)
db2 = dZ2
dZ1 = dW2 * dZ2 * prime_sigmoid(Z1)
dW1 = dZ1 * X
db1 = dZ1
En este código removí las funciones de numpy para que sea más comprensible a la hora de compararlo con las derivadas escritas.
recordemos como es el Forwardpropagation:
Z1 = W1 * X + b1
A1 = sigmoid(Z1)
Z2 = W2 * A1 + b2
A2 = sigmoid(Z2)
y = L(A2, Y)
Derivadas
Para poder realizar el backpropagation necesitamos saber algunos conceptos sobre derivadas, el primer concepto es una derivada parcial. Para derivar una función esta tiene que estar en terminos de alguna variable, generalmente es X, pero si hay dos variables o mas en nuestra función entonces tendremos una derivada parcial, si tenemos X e Y podemos derivar la función con respecto a alguna de ellas si elegimos X la Y actúa como constante y si recordamos las reglas la derivada de una constante es 0, también podemos elegir Y y la X es la que actuará como constante.
El segundo concepto es la regla de la cadena, esta regla se aplica cuando queremos derivar una variable con respecto a una función pero estas dos estan lejos entre si, por ejemplo lo que queremos encontrar es la derivada de W1, W2, b1, b2 con respecto a la función de perdida pero estas variables estan lejos de la función, lo que haremos es derivar las funciones: L, a2, z2, a1, z1 y multiplicarlas entre si, "encadenarlas" hasta llegar a cada una de las derivadas que estamos buscando (W1, W2, b1, b2).
para representar una derivada usaremos la sintaxis dx, esto dxL/dxa se lee: Derivada de L con respecto a a.
La red neuronal
Z1 = W1 * x + b1 -> a1 = sig(Z1) -> Z2 = W2 * a1 + b2 -> a2 = sig(Z2) -> L(a2, Y)
-
Z1 = primer función lineal
-
sig = función sigmoid
-
Z2 = segunda función lineal
-
L = función de perdida
-
Y = etiquetas reales de los datos
Derivada de la función de perdida
El backpropagation como su nombre indica es un algoritmo donde se comienza de atras para adelante es por eso que la primera derivada que calcularemos es la de la función de perdida que luce asi:
L = yIn(a2) + (1 - y)In(1 - a2)
cuando la derivemos nos quedara asi:
dxL/dxa2 = y/a2 - (1 - y)/(1 - a2)
Aquí tenemos una derivada parcial, lo que haremos es derivar la función con respecto a a2
La ecuación tiene dos terminos que tenemos que derivar, el primero es: yIn(a2)
la derivada de In(u) es 1/u
L = 1y/a2
como la y estaba multiplicando al termino In(u) esta se va al numerador junto con el 1 y como el termino 1y es lo mismo que solo poner y podemos simplificar y con esto nos queda
L = y/a2
El segundo termino de la ecuacion es: (1 - y)In(1 - a2)
Ya sabemos cual es la derivada de n(u) solo la aplicamos a este termino quedando:
L = (1 - y)/(1 - a2)
con los dos terminos juntos la derivada es:
dxL/dxa2 = y/a2 + (1 - y)/(1 - a2)
y asi obtenemos dxL/dxa2
Derivada de la segunda función sigmoid
La siguiente derivada que calcularemos es la de la función sigmoid, recordemos la función sigmoid es la siguiente:
1/1 + e^-z
y el resultado de su derivada es el siguiente:
dxa2/dxZ2 = a2(1 - a2)
probablemente esta sea la derivada más dificil de la red neuronal que estamos usando, así que me saltare los pasos pero si quieres indagar en la solución y entiendes ingles te dejo un link donde explican a fondo la solución.
si te preguntas de donde salieron las a2 recordemos que en la red neuronal:
a2 = sig(Z2)
estamos sustituyedo sig(Z2) por a2 para que sea más fácil de leer.
Esta derivada en el código en python la representamos como prime_sigmoid.
Derivada de la segunda función lineal (Z2)
Para obtener esta derivada tenemos que aplicar la regla de la cadena, "encadenamos" las primeras dos derivadas que hemos obtenido
dxL/dxZ2 = dxL/dxa2 * dxa2/dxZ2
dxL/dxZ2 = (y/a2 + (1 - y)/(1 - a2)) * (a2(1 - a2))
el resultado es:
a2 - y
La multiplicación de estos dos terminos es un poco extensa por lo cual no la explicaré pero no es difícil de entender como se llega al resultado
como podemos notar esta es la primera operación que sale en nuestro código en python:
dZ2 = A2 - Y
Se podría decir que en el código "ignoramos" las primeras dos derivadas y ponemos a partir de la tercera.
Derivada de W2
Para nuestra cuarta derivada queremos obtener dxZ2/dxW2, aquí tenemos una derivada parcial y queremos obtener el resultado con respecto a W entonces b se tomará como una constante
tenemos la ecuación:
Z2 = W2 * a1 + b2
la derivada de una constante es 0, b2 = 0
Z2 = W2 * a1 + 0
la derivada de una multiplicación entre constante y variable es solo la constante
dxZ2/dxW2 = a1
a1 la tomamos como constante porque son los datos de aprendizaje que le entregamos a la red neuronal es por eso que no obtenemos derivada de X o a1
Ahora tenemos que encadenar dxZ2/dxW2 con las anteriores para obtener dxL/dxW2, simplemente multiplicamos dxZ2/dxW2 con dxL/dxZ2 ya que en esta ultima derivada tenemos encadenadas las anteriores, el resultado es:
dxL/dxW2 = dxZ2/dxW2 * dxL/dxZ2
dxL/dxW2 = a1 * (a2 - y)
recordemos el código para obtener dW2:
dW2 = dZ2 * A1
dZ2 = A2 - Y
es igual a dxL/dxZ2
Ya tenemos la segunda derivada importante que está en el código.
Derivada de b2
De nuevo tenemos una derivada parcial, esta es parecida a la derivada para obtener dxZ2/dxW2 solo que ahora derivamos con respecto a b2: dxZ2/dxb2
esto quiere decir que ahora tomamos como constante a W2
tenemos la ecuación:
Z2 = W2 * a1 + b2
la derivada de una constante es 0, W2 = 0
Z2 = 0 * a1 + b2
cualquier numero que multiplique a cero es cero
Z2 = 0 + b2
la derivada de una variable es su constante, la constante de b2 es 1 aunque no se escriba en la ecuación b2 esta acompañado por 1
dxZ2/dxb2 = 1
Ahora obtendremos dxL/dxb2. Tenemos que encadenar esta derivada con dxL/dxZ2, como podemos notar no tomamos en cuenta la derivada de dxL/dxW2, esta la usaremos para obtener dxL/dxZ1 mas adelante. Como dxZ2/dxb2 es igual a 1 al multiplicarlo con dxL/dxZ2 simplemente obtendremos dxL/dxZ2
dxL/dxZ2 = (a2 - y)
dxL/dxb2 = dxZ2/dxb2 * dxL/dxZ2
dxL/dxb2 = 1 * (a2 - y)
dZ2 = (a2 - y)
el código en python para obtener db2:
db2 = dZ2
con esto obtendremos la tercera derivada de nuestro código.
Derivada de la primera función sigmoid
Aquí no hay mucho que hacer ya que es la misma derivada que en la primera función solo que en este caso con a1:
dxa1/dxZ1 = a1(1 - a1)
Derivada de la primera función lineal
La primera ecuación lineal es la siguiente:
Z1 = W1 * x + b1
Para obtener su derivada igual usamos la regla de la cadena solo que en este caso tenemos mas terminos por encadenar
dxL/dxZ1 = dxZ2/dxW2 * dxZ2/dxb2 * dxa1/dxZ1
el código en python es:
dZ1 = dW2 * dZ2 * prime_sigmoid(Z1)
En el código usamos dZ2 en lugar de dxZ2/dxb2 ya que estos dos terminos son iguales y sería repetir código
Derivada de W1
Z1 = W1 * x + b1
queremos obtener dxZ1/dxW1 para eso tomaremos b1 como constante
b1 = 0
Z1 = W1 * x + 0
la derivada de una multiplicación entre constante y variable es solo la constante
dxZ1/dxW1 = x
Ahora queremos la derivada con respecto a la función de perdida
dxL/dxW1 para esto encadenamos las dos ecuaciones
dxL/dxW1 = dxL/dxZ1 * dxZ1/dxW1
dxL/dxW1 = dxL/dxZ1 * x
el código en python es:
dW1 = dZ1 * X
Derivada de b1
Esta derivada es igual a la derivada de b2 de la función:
Z1 = W1 * x + b1
el termino W1 * x se elimina y terminamos con b1, la derivada de b1 es 1
dxZ1/dxb1 = 1
aplicamos regla de la cadena con respecto a dxL/dxZ1
dxL/dxb1 = dxZ1/dxb1 * dxL/dxZ1
dxL/dxb1 = 1 * dxL/dxZ1
dxL/dxb1 = dxL/dxZ1
el código en python es el siguiente:
db1 = dZ1
Con estos pasos ya obtuvimos las derivadas necesarias de cada parametro W y b de una red neuronal con dos capas ocultas.