¿Cómo funciona el algoritmo Backpropagation en una Red Neuronal?

Enrique Blanco    17 octubre, 2019
Figure. Buscando el valle.

El algoritmo de backpropagation se introdujo originalmente en la década de 1970, pero su importancia no se apreció completamente hasta un famoso artículo de 1986 de David Rumelhart, Geoffrey Hinton y Ronald Williams. Este documento describe varias redes neuronales en las que la retropropagación funciona mucho más rápido que los enfoques de aprendizaje anteriores, lo que hace posible utilizar redes neuronales para resolver problemas que anteriormente habían sido insolubles. Hoy, el algoritmo de backpropagation es el caballo de batalla del aprendizaje en redes neuronales.

En el corazón de backpropagation hay una expresión para la derivada parcial  \frac{ \partial C}{ \partial w } de la función de coste C con respecto a cualquier peso  (o sesgo) en la red. La expresión nos dice qué tan rápido cambia el coste cuando cambiamos los pesos y los sesgos. Y aunque la expresión es algo compleja, también tiene un cierto encanto, ya que cada elemento tiene una interpretación natural e intuitiva. Entonces, backpropagation no es solo un algoritmo rápido para el aprendizaje; en realidad, nos brinda información detallada sobre cómo cambiar los pesos y los sesgos cambia el comportamiento general de la red.

Un enfoque rápido para calcular la salida de una Red Neuronal

Antes de analizar en profundidad backpropagation, hagamos un calentamiento con un algoritmo rápido basado en matriz para calcular la salida de una red neuronal. De hecho, ya vimos brevemente este algoritmo cerca del final del anterior capítulo, pero se describió rápidamente, por lo que vale la pena revisarlo en detalle. En particular, esta es una buena manera de sentirse cómodo con la notación utilizada en backpropagation, en un contexto familiar. Comencemos con una notación que nos permite referirnos a los pesos en la red de una manera no ambigua. Usaremos w^l_{jk} para denotar el peso de la conexión de la neurona k-ésima en la capa a la neurona j en la capa l.

Entonces, por ejemplo, el siguiente diagrama muestra el peso en una conexión desde la cuarta neurona en la segunda capa a la segunda neurona en la tercera capa de una red:

 Figura 1. Introducción a la notación de índices de los pesos de una red neuronal.
Figura 1. Introducción a la notación de índices de los pesos de una red neuronal.

Esta notación es engorrosa al principio, y requiere práctica para familiarizarse con ella. Una peculiaridad de la notación es el ordenamiento de los índices j y k. Puede pensar que tiene más sentido usar j para referirse a la neurona de entrada, y k a la neurona de salida, no viceversa, como se hace realmente. Explicaremos la razón de este “capricho” a continuación. Utilizamos una notación similar para los sesgos y activaciones de la red. Explícitamente, usamos b_j^l para el sesgo de la neurona j-ésima en la capa l-ésima. Y usamos a_j^l para la activación de la neurona j-ésima en la capa l-ésima. El siguiente diagrama muestra ejemplos de estas anotaciones en uso:

Figura 2: Ejemplo de anotación
Figura 2: Ejemplo de anotación

Con estas notaciones, la activación a^l de la neurona j-ésima en la capa l-ésima está relacionada con las activaciones en la capa (l-1)-ésima por la ecuación:

a_j^l=\sigma(\Sigma_k w_{jk}^l a_k^{l-1}+b_j^l )

donde el sumatorio está sobre todas las neuronas k en la capa (l-1). Para reescribir esta expresión en forma de matriz, definimos una matriz de peso w^l para cada capa l. Las entradas de la matriz de peso w^l son sólo los pesos que se conectan a la l-ésima capa de neuronas, es decir, la entrada en la fila j y la columna k es w^l. Del mismo modo, para cada capa l definimos un vector de bias, b^l. Probablemente pueda adivinar cómo funciona esto: los componentes del vector de sesgo son solo los valores b^l, un componente para cada neurona en la capa l-ésima. Y finalmente, definimos un vector de activación a^l cuyos componentes son las activaciones a_j^l. El último ingrediente que necesitamos para reescribir la fórmula anterior en forma de matriz es la idea de vectorizar una función como \sigma. Queremos aplicar una función como \sigma a cada elemento en un vector v . Utilizamos la notación obvia \sigma(v) para denotar este tipo de aplicación de elementos de una función. Es decir, los componentes de \sigma(v) son solo \sigma(v)_j  = \sigma(v_j). Como ejemplo, si tenemos la función f(x) = x^2, entonces la forma vectorizada de f tiene el efecto:

f\left( \begin{bmatrix} 2 \\ 3 \end{bmatrix}\right) =   \begin{bmatrix} f(2) \\ f(3) \end{bmatrix} =   \begin{bmatrix} 4 \\ 9 \end{bmatrix}

Podemos, con esta notación en mente, escribir la ecuación que nos interesa como:

a^l=\sigma(w^l a^{l-1}+b^l )

Esta expresión nos da una forma mucho más global de pensar acerca de cómo las activaciones en una capa se relacionan con las activaciones en la capa anterior: simplemente aplicamos la matriz de peso a las activaciones, luego agregamos el vector de polarización y finalmente aplicamos la función \sigma. Esa visión global a menudo es más fácil y más sucinta (¡e implica menos índices!) que la visión de neurona por neurona que hemos visto ahora. Piense en ello como una forma de escapar del infierno del índice, sin dejar de ser preciso sobre lo que está sucediendo.

Las dos suposiciones que tenemos que realizar sobre la función de coste

El objetivo de backpropagation es calcular las derivadas parciales \frac{ \partial C}{ \partial w } y \frac{ \partial C}{ \partial b } de la función de coste C con respecto a cualquier peso w o sesgo b en la red.
Para que backpropagation funcione, debemos hacer dos suposiciones principales sobre la forma de la función de coste. Sin embargo, antes de exponer esas suposiciones, es útil tener en mente una función de coste de ejemplo. Utilizaremos la función de coste cuadrática:

C(w, b) = \frac{1}{2n}\Sigma_x  \parallel y(x) -a^L(x) \parallel ^2

w denota la colección de todos los pesos en la red, b todos los sesgos, n es el número total de entradas de entrenamiento, a^L es el vector de salidas de la red cuando se ingresa x, y la suma es sobre todas las entradas de entrenamiento, x. L es el número de capas que tiene nuestra red neuronal.
Bien, entonces, ¿qué suposiciones debemos hacer sobre nuestra función de coste, C para poder aplicar backpropagation? La primera suposición que necesitamos es que la función de coste puede escribirse como un promedio C=\frac{1}{n} \Sigma_x C_x sobre las funciones de coste C_x para un ejemplo de entrenamiento individual. Este es el caso de la función de coste cuadrático, donde el coste de un solo ejemplo de entrenamiento es C_x=1/2   \parallel y-a^L  \parallel  ^2. Este supuesto también será válido para todas las demás funciones de coste que encontraremos.
La razón por la que necesitamos esta suposición es porque lo que backpropagation realmente nos permite hacer es calcular las derivadas parciales \frac{ \partial C_x}{ \partial w } y \frac{ \partial C_x}{ \partial b } para un solo ejemplo de entrenamiento. Luego recuperamos \frac{ \partial C}{ \partial w } y \frac{ \partial C}{ \partial b } promediando los ejemplos de entrenamiento. De hecho, con esta suposición en mente, supondremos que el ejemplo de entrenamiento x ha sido arreglado, y eliminaremos el subíndice x, escribiendo el coste C_x como C. Eventualmente volveremos a colocar la x, pero por ahora es una notación molestia que es mejor dejar implícita.

La segunda suposición que hacemos sobre el coste es que se puede escribir en función de los resultados de la red neuronal:

Figura 3. Construcción de la función de coste como función del output de una red neuronal.
Figura 3. Construcción de la función de coste como función del output de una red neuronal.

Por ejemplo, la función de costo cuadrático cumple este requisito, ya que el coste cuadrático para un solo ejemplo de entrenamiento  x puede escribirse como:

C_x = \frac{1}{2} \parallel y -a^L(x) \parallel ^2 =  \frac{1}{2} \Sigma_j ( y_j -a_j^L)

y por lo tanto es una función de las activaciones de salida. Por supuesto, esta función de costo también depende de la producción deseada y, y puede que se pregunte por qué no consideramos el coste también en función de y. Sin embargo, recuerde que el ejemplo de entrenamiento de entrada x es fijo, por lo que la salida y también es un parámetro fijo. En particular, no es algo que podamos modificar cambiando los pesos y los sesgos de ninguna manera, es decir, no es algo que la red neuronal aprenda. Por lo tanto, tiene sentido considerar C como una función de las activaciones de salida a^L sólo, con y simplemente un parámetro que ayuda a definir esa función.

En el siguiente post de esta serie proseguiremos con una breve explicación de las cuatro ecuaciones fundamentales detrás de backpropagation que nos permitirán una total comprensión del algoritmo.

Para mantenerte al día con LUCA, visita nuestra página web,  suscríbete a LUCA Data Speaks o síguenos en TwitterLinkedIn YouTube.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *