Me imaginé que iba a responder a un post independiente aquí para cualquiera que esté interesado. Esto será utilizando la notación descrita ici .
Introducción
La idea detrás de la retropropagación es tener un conjunto de "ejemplos de entrenamiento" que utilizamos para entrenar nuestra red. Cada uno de ellos tiene una respuesta conocida, por lo que podemos introducirlos en la red neuronal y averiguar en qué medida se equivocó.
Por ejemplo, con el reconocimiento de escritura a mano, tendrías un montón de caracteres manuscritos junto a lo que son en realidad. La red neuronal puede entrenarse mediante retropropagación para "aprender" a reconocer cada símbolo, de modo que cuando se le presente un carácter manuscrito desconocido pueda identificarlo correctamente.
Específicamente, introducimos una muestra de entrenamiento en la red neuronal, vemos lo bien que lo ha hecho y, a continuación, "retrocedemos" para averiguar cuánto podemos cambiar los pesos y el sesgo de cada nodo para obtener un resultado mejor, y luego los ajustamos en consecuencia. De este modo, la red "aprende".
También hay otros pasos que pueden incluirse en el proceso de entrenamiento (por ejemplo, el abandono), pero me centraré sobre todo en la retropropagación, ya que es de lo que trataba esta pregunta.
Derivadas parciales
Una derivada parcial $\frac{\partial f}{\partial x}$ es una derivada de $f$ con respecto a alguna variable $x$ .
Por ejemplo, si $f(x, y)=x^2 + y^2$ , $\frac{\partial f}{\partial x}=2x$ porque $y^2$ es simplemente una constante con respecto a $x$ . Igualmente, $\frac{\partial f}{\partial y}= 2y$ porque $x^2$ es simplemente una constante con respecto a $y$ .
Un gradiente de una función, designado $\nabla f$ es una función que contiene la derivada parcial para cada variable en f. Específicamente:
$$\nabla f(v_1, v_2, ..., v_n) = \frac{\partial f}{\partial v_1 }\mathbf{e}_1 + \cdots + \frac{\partial f}{\partial v_n }\mathbf{e}_n$$ ,
donde $e_i$ es un vector unitario que apunta en la dirección de la variable $v_1$ .
Ahora, una vez que hemos calculado el $\nabla f$ para alguna función $f$ , si estamos en posición $(v_1, v_2, ..., v_n)$ podemos "deslizarnos hacia abajo" $f$ yendo en dirección $-\nabla f(v_1, v_2, ..., v_n)$ .
Con nuestro ejemplo de $f(x, y)=x^2 + y^2$ los vectores unitarios son $e_1=(1, 0)$ y $e_2=(0, 1)$ porque $v_1=x$ y $v_2=y$ y esos vectores apuntan en la dirección del $x$ y $y$ ejes. Así, $\nabla f(x, y) = 2x (1, 0) + 2y(0, 1)$ .
Ahora, para "deslizar hacia abajo" nuestra función $f$ digamos que estamos en un punto $(-2, 4)$ . Entonces tendríamos que movernos en dirección $-\nabla f(-2, -4)= -(2 \cdot -2 \cdot (1, 0) + 2 \cdot 4 \cdot (0, 1)) = -((-4, 0) + (0, 8))=(4, -8)$ .
La magnitud de este vector nos dará la pendiente de la colina (los valores más altos significan que la colina es más empinada). En este caso, tenemos $\sqrt{4^2+(-8)^2}\approx 8.944$ .
Producto Hadamard
Producto Hadamard de dos matrices $A, B \in R^{n\times m}$ es igual que la suma de matrices, excepto que en lugar de sumar las matrices elemento a elemento, las multiplicamos elemento a elemento.
Formalmente, mientras que la suma de matrices es $A + B = C$ donde $C \in R^{n \times m}$ tal que
$$C^i_j = A^i_j + B^i_j$$ ,
El producto Hadamard $A \odot B = C$ donde $C \in R^{n \times m}$ tal que
$$C^i_j = A^i_j \cdot B^i_j$$
Cálculo de los gradientes
(la mayor parte de esta sección procede de El libro de Neilsen ).
Disponemos de un conjunto de muestras de entrenamiento, $(S, E)$ donde $S_r$ es una única muestra de entrenamiento de entrada, y $E_r$ es el valor de salida esperado de esa muestra de entrenamiento. También tenemos nuestra red neuronal, compuesta por pesos $W$ y sesgos $B$ . $r$ se utiliza para evitar la confusión del $i$ , $j$ et $k$ utilizado en la definición de una red feedforward.
A continuación, definimos una función de costes, $C(W, B, S^r, E^r)$ que toma nuestra red neuronal y un único ejemplo de entrenamiento, y nos dice cómo de bien lo ha hecho.
Normalmente lo que se utiliza es el coste cuadrático, que se define por
$$C(W, B, S^r, E^r) = 0.5\sum\limits_j (a^L_j - E^r_j)^2$$
donde $a^L$ es la salida de nuestra red neuronal, dada la muestra de entrada $S^r$
Entonces queremos encontrar $\frac{\partial C}{\partial w^i_j}$ y $\frac{\partial C}{\partial b^i_j}$ para cada nodo de nuestra red neuronal feedforward.
Podemos llamar a esto el gradiente de $C$ en cada neurona porque consideramos $S^r$ y $E^r$ como constantes, ya que no podemos cambiarlas cuando intentamos aprender. Y esto tiene sentido: queremos movernos en una dirección relativa a $W$ y $B$ que minimice el coste, y moviéndose en la dirección negativa del gradiente con respecto a $W$ y $B$ hará esto.
Para ello, definimos $\delta^i_j=\frac{\partial C}{\partial z^i_j}$ como el error de la neurona $j$ en capa $i$ .
Empezamos por calcular $a^L$ enchufando $S^r$ en nuestra red neuronal.
A continuación, calculamos el error de nuestra capa de salida, $\delta^L$ a través de
$$\delta^L_j = \frac{\partial C}{\partial a^L_j} \sigma^{ \prime}(z^L_j)$$ .
Que también puede escribirse como
$$\delta^L = \nabla_a C \odot \sigma^{ \prime}(z^L)$$ .
A continuación, hallamos el error $\delta^i$ en función del error en la capa siguiente $\delta^{i+1}$ a través de
$$\delta^i=((W^{i+1})^T \delta^{i+1}) \odot \sigma^{\prime}(z^i)$$
Ahora que tenemos el error de cada nodo de nuestra red neuronal, calcular el gradiente con respecto a nuestros pesos y sesgos es fácil:
$$\frac{\partial C}{\partial w^i_{jk}}=\delta^i_j a^{i-1}_k=\delta^i(a^{i-1})^T$$
$$\frac{\partial C}{\partial b^i_j} = \delta^i_j$$
Tenga en cuenta que la ecuación para el error de la capa de salida es la única ecuación que depende de la función de coste, por lo que, independientemente de la función de coste, las tres últimas ecuaciones son las mismas.
Como ejemplo, con coste cuadrático, obtenemos
$$\delta ^L = (a^L - E^r) \odot \sigma ^ {\prime}(z^L)$$
para el error de la capa de salida. y luego esta ecuación se puede enchufar en la segunda ecuación para obtener el error de la capa de $L-1^{\text{th}}$ capa:
$$\delta^{L-1}=((W^{L})^T \delta^{L}) \odot \sigma^{\prime}(z^{L-1})$$ $$=((W^{L})^T ((a^L - E^r) \odot \sigma ^ {\prime}(z^L))) \odot \sigma^{\prime}(z^{L-1})$$
que podemos repetir este proceso para encontrar el error de cualquier capa con respecto a $C$ lo que nos permite calcular el gradiente de los pesos y el sesgo de cualquier nodo con respecto a $C$ .
Podría escribir una explicación y prueba de estas ecuaciones si se desea, aunque también se pueden encontrar pruebas de las mismas ici . Sin embargo, animo a todos los que lean esto a que lo comprueben por sí mismos, empezando por la definición $\delta^i_j=\frac{\partial C}{\partial z^i_j}$ y aplicando generosamente la regla de la cadena.
Para algunos ejemplos más, he hecho una lista de algunas funciones de coste junto con sus gradientes ici .
Descenso gradual
Ahora que tenemos estos gradientes, tenemos que usarlos aprender. En la sección anterior, vimos cómo mover para "deslizar hacia abajo" la curva con respecto a algún punto. En este caso, debido a que es un gradiente de algún nodo con respecto a los pesos y un sesgo de ese nodo, nuestra "coordenada" son los pesos actuales y el sesgo de ese nodo. Dado que ya hemos encontrado los gradientes con respecto a esas coordenadas, esos valores ya son cuánto tenemos que cambiar.
No queremos deslizarnos por la pendiente a una velocidad muy rápida, de lo contrario corremos el riesgo de deslizarnos más allá del mínimo. Para evitarlo, queremos cierto "tamaño de paso" $\eta$ .
A continuación, encontrar el cuánto debemos modificar cada peso y sesgo por, porque ya hemos calculado el gradiente con respecto a la corriente que tenemos
$$\Delta w^i_{jk}= -\eta \frac{\partial C}{\partial w^i_{jk}}$$
$$\Delta b^i_j = -\eta \frac{\partial C}{\partial b^i_j}$$
Así, nuestras nuevas ponderaciones y sesgos son
$$w^i_{jk} = w^i_{jk} + \Delta w^i_{jk}$$ $$b^i_j = b^i_j + \Delta b^i_j$$
El uso de este proceso en una red neuronal con sólo una capa de entrada y una capa de salida se denomina Regla Delta .
Descenso Gradiente Estocástico
Ahora que sabemos cómo realizar la retropropagación para una sola muestra, necesitamos alguna forma de utilizar este proceso para "aprender" todo nuestro conjunto de entrenamiento.
Una opción es simplemente realizar la retropropagación para cada muestra de nuestros datos de entrenamiento, de una en una. Sin embargo, esto es bastante ineficiente.
Un enfoque mejor es Descenso Gradiente Estocástico . En lugar de realizar la retropropagación para cada muestra, elegimos una pequeña muestra aleatoria (llamada a lote ) de nuestro conjunto de entrenamiento y, a continuación, realizar la retropropagación para cada muestra de ese lote. La esperanza es que al hacer esto, capturamos la "intención" del conjunto de datos, sin tener que calcular el gradiente de cada muestra.
Por ejemplo, si tuviéramos 1000 muestras, podríamos elegir un lote de tamaño 50 y, a continuación, ejecutar la retropropagación para cada muestra de este lote. La esperanza es que nos dieron un conjunto de entrenamiento lo suficientemente grande que representa la distribución de los datos reales que estamos tratando de aprender lo suficientemente bien como para elegir una pequeña muestra aleatoria es suficiente para capturar esta información.
Sin embargo, hacer retropropagación para cada ejemplo de entrenamiento de nuestro mini lote no es lo ideal, porque podemos acabar "dando bandazos" en los que las muestras de entrenamiento modifican los pesos y los sesgos de tal forma que se anulan entre sí e impiden llegar al mínimo al que estamos intentando llegar.
Para evitarlo, queremos ir al "mínimo medio", porque la esperanza es que, de media, los gradientes de las muestras apunten hacia abajo en la pendiente. Así que, después de elegir nuestro lote al azar, creamos un minilotes que es una pequeña muestra aleatoria de nuestro lote. Entonces, dado un minilote con $n$ muestras de entrenamiento, y sólo actualizar los pesos y sesgos después de promediar los gradientes de cada muestra en el mini lote.
Formalmente, hacemos
$$\Delta w^{i}_{jk} = \frac{1}{n}\sum\limits_r \Delta w^{ri}_{jk}$$
y
$$\Delta b^{i}_{j} = \frac{1}{n}\sum\limits_r \Delta b^{ri}_{j}$$
donde $\Delta w^{ri}_{jk}$ es el cambio de peso calculado para la muestra $r$ et $\Delta b^{ri}_{j}$ es el cambio de sesgo calculado para la muestra $r$ .
Entonces, como antes, podemos actualizar los pesos y los sesgos mediante:
$$w^i_{jk} = w^i_{jk} + \Delta w^{i}_{jk}$$ $$b^i_j = b^i_j + \Delta b^{i}_{j}$$
Esto nos da cierta flexibilidad en cómo queremos realizar el descenso de gradiente. Si tenemos una función que estamos intentando aprender con muchos mínimos locales, este comportamiento de "meneo" es realmente deseable, porque significa que es mucho menos probable que nos quedemos "atascados" en un mínimo local, y más probable que "saltemos" de un mínimo local y, con suerte, caigamos en otro que esté más cerca del mínimo global. Por eso queremos minilotes pequeños.
Por otro lado, si sabemos que hay muy pocos mínimos locales, y en general el descenso por gradiente va hacia los mínimos globales, queremos minilotes más grandes, porque este comportamiento de "meneo" nos impedirá bajar la pendiente tan rápido como nos gustaría. Véase ici .
Una opción es elegir el minilote más grande posible, considerando todo el lote como un único minilote. Esto se denomina Descenso gradual por lotes ya que simplemente estamos promediando los gradientes del lote. Sin embargo, esto casi nunca se utiliza en la práctica, porque es muy ineficiente.