entender la retropropagación?
Si está tratando de comprender cómo se entrenan los sistemas de inteligencia artificial modernos, como los modelos de lenguaje grande (LLM), la retropropagación es uno de los conceptos más importantes que debe comprender.
Pero si me preguntas cómo me sentí cuando lo encontré, estaba completamente perdido al mirar las ecuaciones matemáticas. Para mí fue como un bloqueo mental.
Me di cuenta y quise empezar desde cero y desarrollar mi comprensión paso a paso.
Ese viaje comenzó con mi artículo anterior, donde construimos una red neuronal desde cero utilizando un conjunto de datos simple y comprendimos cómo hace predicciones.
El blog recibió una gran respuesta. ¡Gracias por eso!
Ahora, sigamos con el mismo enfoque. Analizaremos la retropropagación paso a paso, manteniéndola tan simple e intuitiva como antes.
Antes de comenzar, sólo quiero decir una cosa. Daremos esto paso a paso.
Temas como la retropropagación pueden resultar abrumadores al principio, pero una vez que construimos una base sólida, todo lo demás se vuelve mucho más fácil de entender.
Entonces, comencemos.
¡Bienvenido de nuevo!
Continuemos nuestro viaje de aprendizaje a través del aprendizaje profundo.
Ya tenemos un conocimiento básico de las redes neuronales, que exploramos utilizando un conjunto de datos simple en el blog anterior.
Ahora, primero recordemos lo que aprendimos en el blog anterior sobre redes neuronales.
Resumen rápido
Consideramos este conjunto de datos simple.
Después de trazar los datos, se veía así:
Observamos que una sola línea no era suficiente para encajarlo. Entonces, decidimos resolverlo usando redes neuronales.
A continuación, conocimos la ecuación de una sola neurona y, después, aprendimos sobre las diferentes capas de una red neuronal.
Para simplificar, consideramos una capa oculta con dos neuronas ocultas.
A continuación, observamos cómo las dos neuronas ocultas producían dos transformaciones lineales diferentes y luego quisimos combinarlas en la capa de salida.
Sin embargo, descubrimos que la combinación de dos líneas produjo otra línea, no la curva que podría ajustarse a los datos.
Aquí es donde nos dimos cuenta de la importancia de las funciones de activación, ya que introducen no linealidad en el modelo.
Entonces, pasamos las salidas de las neuronas ocultas a través de la función de activación (ReLU) y luego las combinamos en la capa de salida.
En otras palabras, tomamos la combinación lineal de las salidas de la función de activación en la capa de salida y, finalmente, obtuvimos la curva.
En el blog anterior construimos la arquitectura de la red neuronal y vimos cómo realiza predicciones mediante propagación directa.
Antes de aprender cómo funciona la propagación hacia atrás, veamos primero los valores producidos en cada capa durante el paso hacia adelante que analizamos en un blog anterior.
Usaremos estos valores a lo largo del blog para comprender cómo aprende la red al actualizar sus parámetros.
¿Por qué la red necesita aprender?
Cuando observamos la curva final producida por nuestra red neuronal, podemos ver que no encaja bien.
Por ejemplo, cuando las horas estudiadas (x) son 1, la puntuación real del examen es 55, pero nuestra red neuronal predice que será 28, lo cual es una gran diferencia.
Ahora, necesitamos hacer que nuestra red neuronal funcione mejor, lo que significa que debería predecir valores mucho más cercanos a los puntajes reales de los exámenes.
Para ello, la red neuronal necesita aprender. Por aprendizaje nos referimos a determinar qué parámetros se deben aumentar y cuáles se deben disminuir para reducir la pérdida.
Aprendiendo de un ejemplo familiar
Ahora bien, ¿cómo podemos hacer esto?
En este punto, no sabemos cómo hacerlo.
Hagamos una cosa. Procedamos con lo que ya sabemos.
¿Pero qué sabemos ya?
Ya tenemos una idea sobre la regresión lineal simple, cómo se calcula la pérdida y cómo se ve la curva del cuenco.
Quizás podamos aprender algo de ello.
En regresión lineal simple, necesitamos encontrar los valores óptimos para β0 (intersección) y β1 (pendiente).
Por supuesto, ya tenemos fórmulas, pero también las hemos obtenido nosotros mismos.
Lo que hicimos fue trazar una gráfica con tres ejes. Un eje representó (β0), el segundo representó (β1) y el tercero representó la pérdida.
Trazamos los valores de pérdida para diferentes valores (β0) y (β1) y observamos una curva en forma de cuenco.
Entonces entendimos que la pérdida mínima ocurre en la parte inferior de la curva, donde la pendiente de la superficie de pérdida se vuelve cero.
Para encontrar ese punto, usamos derivación parcial y finalmente resolvimos las ecuaciones resultantes para obtener las fórmulas.
En la regresión lineal simple, podemos utilizar diferentes funciones de pérdida, como la suma de errores cuadráticos (SSE), el error cuadrático medio (MSE) u otras funciones de pérdida adecuadas según el problema.
Aquí, consideraremos el error cuadrático medio (MSE) como nuestra función de pérdida.
Para regresión lineal simple, la función de pérdida es
[
L(beta_0,beta_1)=frac{1}{n}sum_{i=1}^{n}left(y_i-hat{y}_iright)^2
]
dónde
[
hat{y}_i=beta_0+beta_1x_i.
]
Observe que la pérdida depende sólo de dos parámetros, [beta_0] y [beta_1]
Tenemos que encontrar los valores de [beta_0] y [beta_1] que minimicen esta pérdida.
Ahora, veamos nuestra red neuronal.
Dado que nuestro problema actual es un problema de regresión no lineal, podemos continuar usando el mismo error cuadrático medio (MSE).
La función de pérdida ahora se puede escribir como
[
L(w_1,w_2,w_3,w_4,b_1,b_2,b_3)
=
frac{1}{n}
sum_{i=1}^{n}
left(y_i-hat{y}_iright)^2.
]
Sin embargo, a diferencia de la regresión lineal simple, nuestra predicción ya no está dada por
[
hat{y}=beta_0+beta_1x.
]
En cambio, es producido por toda la red neuronal.
Para nuestra red neuronal,
[
hat{y}_i
=
w_3,mathrm{ReLU}(w_1x_i+b_1)
+
w_4,mathrm{ReLU}(w_2x_i+b_2)
+
b_3.
]
Como resultado, la pérdida ya no depende sólo de dos parámetros. Ahora depende de los siete parámetros de la red neuronal, que son [w_1,w_2,w_3,w_4,b_1,b_2,b_3]
Al igual que en la regresión lineal simple, nuestro objetivo sigue siendo el mismo: encontrar los valores de estos parámetros que minimicen la pérdida.
Para lograrlo, debemos comprender cómo cambia la pérdida cuando cambiamos cada parámetro individualmente mientras mantenemos fijos los parámetros restantes.
En otras palabras, necesitamos calcular derivadas parciales como
[
frac{parcial L}{parcial w_1},
patio
frac{parcial L}{parcial w_2},
patio
frac{parcial L}{parcial w_3},
patio
ldots,
patio
frac{partial L}{partial b_3}.
]
Estas derivadas parciales nos dicen qué tan sensible es la pérdida a cada parámetro y nos ayudan a determinar si ese parámetro debe aumentarse o disminuirse para reducir la pérdida.
Estableciendo la meta
En una regresión lineal simple, cuando trazamos los valores de pérdida para diferentes combinaciones de pendiente e intersección, obtenemos una curva en forma de cuenco en un espacio tridimensional.
Para nuestra red neuronal, sin embargo, no podemos visualizar la superficie de pérdida de la misma manera porque ahora existe en un espacio de ocho dimensiones.
Aunque no podamos visualizarlo, nuestro objetivo sigue siendo el mismo: encontrar los valores de los parámetros que minimicen la pérdida.
Es hora de entender la regla de la cadena
Ahora, basándonos en lo que ya sabemos de la regresión lineal simple, encontramos una manera de seguir adelante, que consiste en calcular las derivadas parciales de la pérdida con respecto a cada parámetro.
Los parámetros son
[w_1,w_2,w_3,w_4,b_1,b_2,b_3]
Pero antes de continuar, hay un concepto importante que debemos comprender: la regla de la cadena porque es la base de todo lo que haremos a continuación.
La regla de la cadena se utiliza siempre que una cantidad depende de otra cantidad, que a su vez depende de otra cantidad.
Entendamos esto con un ejemplo simple.
Suponer
[
y=x^2
]
y
[
z=y^3
]
Ahora queremos encontrar
[
frac{dz}{dx}
]
Primero, encontremos esta derivada usando diferenciación clásica.
Observe que [z] está escrito en términos de [y], no de [x]. Como queremos la derivada con respecto a [x], primero podemos eliminar la variable intermedia sustituyendo [y=x^2] en la ecuación por [z]
Sustituyendo,
[
z=(x^2)^3=x^6
]
Ahora la expresión depende sólo de [x] por lo que podemos diferenciarla directamente.
Usando la regla de la potencia,
[
frac{dz}{dx}
=
frac{d}{dx}(x^6)
=
6x^5
]
Este método puede resultar sencillo para problemas sencillos como este porque podemos sustituir fácilmente una expresión por otra.
Sin embargo, imagine una expresión mucho más grande con varias variables intermedias.
Reescribir toda la ecuación antes de diferenciar sería difícil y existe una mayor probabilidad de cometer errores.
En lugar de combinar todo primero en una sola expresión, tenemos un enfoque mucho más sistemático llamado regla de la cadena.
En lugar de eliminar las variables intermedias, con la regla de la cadena podemos trabajar con ellas paso a paso.
Veamos cómo podemos implementar la regla de la cadena.
Ya sabemos que [frac{dz}{dx}] nos dice cuánto cambia [z] cuando hacemos un cambio muy pequeño en [x]
Aquí [z] no depende directamente de [x]
En cambio, la relación se ve así:
[
x rightarrow y rightarrow z.
]
Esto significa que siempre que [x] cambia, primero cambia [y] y ese cambio en [y] luego cambia [z]
Ahora, en lugar de intentar diferenciar todo a la vez, la regla de la cadena nos dice que rompamos el problema en partes más pequeñas.
[
frac{dz}{dx}=frac{dz}{dy}timesfrac{dy}{dx}
]
Ahora calculemos cada parte por separado.
Desde
[
z=y^3,
]
obtenemos
[
frac{dz}{dy}=3y^2.
]
Del mismo modo, desde
[
y=x^2,
]
obtenemos
[
frac{dy}{dx}=2x.
]
Multiplicando estos juntos,
[
frac{dz}{dx}=3y^2times2x.
]
Finalmente, sabemos que
[
y=x^2,
]
entonces lo sustituimos nuevamente en la ecuación.
[
frac{dz}{dx}=3(x^2)^2times2x=6x^5.
]
Lo importante que podemos observar aquí es que nunca diferenciamos la expresión completa a la vez.
En cambio, lo dividimos en derivadas más pequeñas, las resolvimos una por una y luego las multiplicamos.
Usaremos exactamente la misma idea en nuestra red neuronal.
La única diferencia es que la cadena ahora es un poco más larga.
Resolviendo paso a paso usando el método de diferenciación clásico
Ahora que entendemos la regla de la cadena, procedamos a calcular las derivadas parciales con respecto a cada parámetro.
Hasta ahora, utilizamos valores específicos para los pesos y sesgos para comprender cómo funciona el pase hacia adelante. Sin embargo, nuestro objetivo es aprender estos valores a partir de los datos.
Entonces, en lugar de usar valores fijos, representémoslos primero usando parámetros.
La salida de nuestra red neuronal está dada por
[
hat{y}=w_3a_1+w_4a_2+b_3
]
dónde
[
a_1=mathrm{ReLU}(z_1)
] [
z_1=w_1x+b_1
]
y
[
a_2=mathrm{ReLU}(z_2)
] [
z_2=w_2x+b_2
]
Usando este resultado, podemos calcular el error cuadrático medio (MSE), que es la función de pérdida de nuestra red neuronal.
La ecuación general de MSE es
[
L(w_1,w_2,w_3,w_4,b_1,b_2,b_3)=frac{1}{n}sum_{i=1}^{n}(y_i-hat{y}_i)^2
]
Ahora, sustituyamos la ecuación de predicción en la función de pérdida.
[
L(w_1,w_2,w_3,w_4,b_1,b_2,b_3)=frac{1}{n}sum_{i=1}^{n}left(y_i-left(w_3a_{1i}+w_4a_{2i}+b_3right)right)^2
]
Desde
[
a_{1i}=mathrm{ReLU}(w_1x_i+b_1)
]
y
[
a_{2i}=mathrm{ReLU}(w_2x_i+b_2)
]
la función de pérdida completa se convierte en
[
L(w_1,w_2,w_3,w_4,b_1,b_2,b_3)=frac{1}{n}sum_{i=1}^{n}left(y_i-left(w_3mathrm{ReLU}(w_1x_i+b_1)+w_4mathrm{ReLU}(w_2x_i+b_2)+b_3right)right)^2
]
Ahora, comencemos a encontrar la derivada parcial con respecto a cualquiera de los parámetros, comencemos con
[w_1]
Entonces, tenemos que calcular
[
frac{partial}{partial w_1}left[frac{1}{n}sum_{i=1}^{n}left(y_i-left(w_3,mathrm{ReLU}(w_1x_i+b_1)+w_4,mathrm{ReLU}(w_2x_i+b_2)+b_3right)right)^2right]
]
Esta ecuación parece difícil de resolver. ¿Cómo podemos encontrar la derivada parcial con respecto a
[w_1]
de una ecuación tan grande?
Procedamos usando las mismas ideas de diferenciación que ya conocemos.
En lugar de diferenciar todo a la vez, simplificaremos el problema paso a paso.
queremos calcular
[
frac{parcial L}{parcial w_1}
]
Sustituye la función de pérdida en la derivada.
[
frac{parcial L}{parcial w_1}
=
frac{partial}{parcial w_1}
izquierda(
frac{1}{n}
sum_{i=1}^{n}
(y_i-hat{y}_i)^2
bien)
]
Observe que no sustituimos la expresión completa por
[hat{y}_i]
todavía. Lo haremos sólo cuando sea necesario.
Como [frac{1}{n}] es una constante, sabemos que se puede mover fuera de la derivada.
[
frac{parcial L}{parcial w_1}
=
frac{1}{n}
frac{partial}{parcial w_1}
izquierda(
sum_{i=1}^{n}
(y_i-hat{y}_i)^2
bien)
]
La sumatoria también es lineal, por lo que la derivada puede pasar por ella.
¿Qué queremos decir con eso?
Significa que en suma sumamos muchos términos y podemos diferenciar cada término por separado y luego sumar las derivadas.
[
frac{parcial L}{parcial w_1}
=
frac{1}{n}
sum_{i=1}^{n}
frac{partial}{parcial w_1}
izquierda(
(y_i-hat{y}_i)^2
bien)
]
Ahora diferencia el cuadrado
Dejar
[
A=y_i-hat{y}_i
]
Entonces
[
frac{parcial L}{parcial w_1}
=
frac{1}{n}
sum_{i=1}^{n}
frac{partial}{parcial w_1}(A^2)
]
Usando la regla de la potencia,
[
frac{partial}{parcial w_1}(A^2)
=
2A
frac{parcial A}{parcial w_1}
]
Sustituye esto en la ecuación anterior.
[
frac{parcial L}{parcial w_1}
=
frac{2}{n}
sum_{i=1}^{n}
A
frac{parcial A}{parcial w_1}
]
Reemplace [A] con [y_i-hat{y}_i].
[
frac{parcial L}{parcial w_1}
=
frac{2}{n}
sum_{i=1}^{n}
(y_i-hat{y}_i)
frac{partial}{parcial w_1}
(y_i-hat{y}_i)
]
Diferenciar la expresión interior
Sabemos que el verdadero objetivo (observación real) [y_i] es una constante,
[
frac{y_i parcial}{w_1 parcial}=0
]
Por lo tanto,
[
frac{partial}{parcial w_1}
(y_i-hat{y}_i)
=
-frac{partialhat{y}_i}{partial w_1}
]
Sustituya esto de nuevo.
[
frac{parcial L}{parcial w_1}
=
-frac{2}{n}
sum_{i=1}^{n}
(y_i-hat{y}_i)
frac{partialhat{y}_i}{partial w_1}
]
Ahora diferenciando la predicción [frac{partialhat{y}_i}{partial w_1}
]
Lo sabemos desde la capa de salida de nuestra red neuronal.
[
hat{y}_i
=
w_3a_ {1i}
+
w_4a_ {2i}
+
b_3
]
Sustituya las ecuaciones de funciones de activación de neuronas ocultas.
[
hat{y}_i
=
w_3mathrm{ReLU}(w_1x_i+b_1)
+
w_4mathrm{ReLU}(w_2x_i+b_2)
+
b_3
]
Diferenciar con respecto a [w_1]
[
frac{partialhat{y}_i}{partial w_1}
=
frac{partial}{parcial w_1}
izquierda(
w_3mathrm{ReLU}(w_1x_i+b_1)
+
w_4mathrm{ReLU}(w_2x_i+b_2)
+
b_3
bien)
]
Diferenciando cada término por separado.
Como [w_3] es constante,
[
frac{partial}{parcial w_1}
izquierda(
w_3mathrm{ReLU}(w_1x_i+b_1)
bien)
=
w_3
frac{partial}{parcial w_1}
mathrm{ReLU}(w_1x_i+b_1)
]
El segundo término contiene sólo [w_2] por lo que
[
frac{partial}{parcial w_1}
izquierda(
w_4mathrm{ReLU}(w_2x_i+b_2)
bien)
=0
]
También,
[
frac{parcial b_3}{parcial w_1}=0
]
Por eso,
[
frac{partialhat{y}_i}{partial w_1}
=
w_3
frac{partial}{parcial w_1}
mathrm{ReLU}(w_1x_i+b_1)
]
Diferenciar la expresión ReLU
Dejar
[
u=w_1x_i+b_1
]
Entonces
[
mathrm{ReLU}(w_1x_i+b_1)
=
mathrm{ReLU}(u)
]
Ahora diferenciando
[
u=w_1x_i+b_1
]
con respecto a [w_1]
[
frac{du}{dw_1}=x_i
]
Ahora diferencia la activación.
[
frac{d,mathrm{ReLU}(u)}{du}
=
mathrm{ReLU}'(u)
]
Aquí usamos la regla de la cadena,
[
frac{partial}{parcial w_1}
mathrm{ReLU}(w_1x_i+b_1)
=
mathrm{ReLU}'(u)
frac{du}{dw_1}
]
Sustituyendo [frac{du}{dw_1}=x_i]
[
frac{partial}{parcial w_1}
mathrm{ReLU}(w_1x_i+b_1)
=
mathrm{ReLU}'(u)x_i
]
Reemplazo de [u]
[
frac{partial}{parcial w_1}
mathrm{ReLU}(w_1x_i+b_1)
=
mathrm{ReLU}'(w_1x_i+b_1)x_i
]
Esta derivación de ReLU puede resultar confusa, vayamos más despacio y veamos qué hicimos realmente aquí.
Sabemos que la activación de ReLU no depende directamente de w1w_1.
Depende del valor de w1xi+b1w_1x_i+b_1.
Al mismo tiempo, la expresión w1xi+b1w_1x_i+b_1 depende de w1w_1.
Entonces, cuando w1w_1 cambia, primero cambia w1xi+b1w_1x_i+b_1, lo que a su vez cambia la salida de ReLU.
Este es exactamente el tipo de situación en la que utilizamos la regla de la cadena.
Entonces, para encontrar cómo cambia ReLU con respecto a w1w_1, primero encontramos cómo cambia w1xi+b1w_1x_i+b_1 con respecto a w1w_1, y luego cómo cambia ReLU con respecto a w1xi+b1w_1x_i+b_1.
Finalmente, combinamos estos dos resultados usando la regla de la cadena.
Ahora sustituir atrás
Anteriormente, encontramos
[
frac{parcial L}{parcial w_1}
=
-frac{2}{n}
sum_{i=1}^{n}
(y_i-hat{y}_i)
frac{partialhat{y}_i}{partial w_1}
]
También calculamos
[
frac{partialhat{y}_i}{partial w_1}
=
w_3
mathrm{ReLU}'(w_1x_i+b_1)
x_i
]
Sustituye esto en la ecuación anterior.
[
frac{parcial L}{parcial w_1}
=
-frac{2}{n}
sum_{i=1}^{n}
(y_i-hat{y}_i)
w_3
mathrm{ReLU}'(w_1x_i+b_1)
x_i
]
resultado final
hemos derivado
[
frac{parcial L}{parcial w_1}
=
-frac{2}{n}
sum_{i=1}^{n}
(y_i-hat{y}_i)
w_3
mathrm{ReLU}'(w_1x_i+b_1)
x_i
]
Esto nos dice exactamente cómo cambia la pérdida cuando el peso
[w_1]
cambios.
Al principio, esta ecuación puede parecer difícil de entender, pero en realidad es bastante simple.
Como nos dice cómo cambia la pérdida total cuando hacemos un cambio muy pequeño en el peso w1, este valor se llama gradiente y es exactamente lo que utiliza el descenso de gradiente para actualizar el peso.
Para calcular este gradiente, la ecuación considera todos los ejemplos de entrenamiento del conjunto de datos.
Para cada ejemplo de entrenamiento:
[(y_i-hat{y}_i)] nos dice qué tan lejos está la predicción del valor real.
[w_3] nos dice cuánto contribuye la primera neurona oculta a la predicción final.
[mathrm{ReLU}'(w_1x_i+b_1)] nos dice si el cambio en [w_1] puede pasar a través de la activación de ReLU.
[x_i] nos dice cuánto afecta un pequeño cambio en [w_1] a la entrada de la neurona.
Cada ejemplo de entrenamiento aporta su propio gradiente basado en estas cantidades.
Sumamos todas estas contribuciones individuales y, dado que utilizamos la función de pérdida del error cuadrático medio (MSE), al dividir por nn se obtiene el gradiente promedio en todo el conjunto de datos.
Este gradiente promedio nos dice cómo se debe ajustar w1w_1 para reducir la pérdida general, en lugar de solo el error para un único ejemplo de entrenamiento.
Conclusión
Si recuerda nuestra discusión sobre la regresión lineal simple, calculamos las derivadas parciales con respecto a solo dos parámetros.
En este blog, hemos derivado con éxito
[frac{partial L}{partial w_1}]
Aunque la derivación fue larga, utilizamos las mismas ideas de cálculo que ya conocíamos en cada paso.
Simplemente aplicamos la diferenciación paso a paso y utilizamos la regla de la cadena donde fuera necesario.
Ahora, nuestra red neuronal todavía tiene seis parámetros más, y cada uno de ellos tiene su propia derivada parcial.
Entonces, ¿qué opinas?
¿Necesitamos repetir todo este proceso para cada peso y sesgo?
Afortunadamente, no.
A medida que las redes neuronales se hacen más grandes, derivar manualmente cada gradiente rápidamente se volvería difícil e ineficiente.
Tiene que haber una mejor manera.
La buena noticia es que no necesitamos nuevas matemáticas.
Simplemente necesitamos una mejor manera de organizar estos cálculos.
Todo sigue basándose en la misma regla de la cadena que hemos estado usando a lo largo de este artículo.
En la siguiente parte, veremos cómo la regla de la cadena se puede aplicar de manera eficiente en toda la red neuronal, lo que nos llevará a uno de los algoritmos más importantes del aprendizaje profundo: la retropropagación.
Espero que hayas aprendido algo de este artículo. Si todavía estás confundido acerca de las redes neuronales o te gustaría revisar los conceptos básicos, siempre puedes leer mi artículo anterior aquí.
Si esto le resultó útil, no dude en compartirlo con personas que puedan necesitarlo.
En caso de que tengas alguna duda o idea, puedes comentar en LinkedIn.
“No importa lo lento que vayas, siempre y cuando no te detengas”.
— Confucio
¡Gracias por leer y nos vemos en la Parte 2!