Vicente Rodríguez

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:

Gradient Descent

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)

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.