¡Bienvenido de nuevo!
nuestro viaje para comprender la retropropagación en detalle.
Recordemos brevemente lo que cubrimos hasta ahora en la Parte 1.
Un resumen rápido
Primero comenzamos a intentar comprender las redes neuronales utilizando un conjunto de datos simple.
En ese proceso, primero construimos una pequeña red neuronal y comprendimos cómo hace predicciones a través del proceso de propagación hacia adelante.
Luego observamos que los valores predichos no se acercaban en absoluto a los valores reales, lo que resultó en un gran error.
Ahora queríamos entrenar esta red neuronal para que funcionara mejor.
Al compararlo con la regresión lineal simple, observamos que, en nuestra red neuronal, la pérdida depende de siete parámetros.
[w_1,w_2,w_3,w_4,b_1,b_2,b_3]
Nuestra función de pérdida completa se veía así:
[
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
]
A continuación, tal como hicimos en la regresión lineal simple, queríamos diferenciar la pérdida con respecto a cada parámetro.
Comenzamos con w1w_1 y utilizamos el método de diferenciación clásico. Finalmente llegamos al siguiente resultado:
[
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
]
¿Realmente tenemos que repetir esto?
Ahora es el momento de pensar.
Diferenciamos la pérdida con respecto a w1w_1, lo que significa que estábamos tratando de encontrar cómo cambia la pérdida a medida que cambia w1w_1.
Utilizamos el método de diferenciación clásico aquí.
Ahora tenemos seis parámetros más para diferenciar la pérdida con respecto a.
¿Qué opinas?
¿Necesitamos proceder con el mismo método?
No.
La regla de la cadena al rescate
Si recuerdas, discutimos la regla de la cadena en la Parte 1.
Usamos un ejemplo simple.
consideramos
[
y=x^2
]
y
[
z=y^3
]
Notamos que (z) no depende directamente de (x).
En cambio, la relación parece
[
x rightarrow y rightarrow z.
]
Esto significa que cuando (x) cambia, primero cambia (y), y el cambio en (y) luego afecta a (z).
Dado que el efecto de (x) llega a (z) a través de una variable intermedia, no podemos diferenciar (z) con respecto a (x) directamente. En cambio, utilizamos la regla de la cadena.
[
frac{dz}{dx}
=
frac{dz}{dy}
cdot
frac{dy}{dx}
]
La regla de la cadena nos dice que en lugar de dar un gran paso de (x) a (z), podemos dividir el problema en pasos más pequeños siguiendo la ruta de dependencia.
Siguiendo el camino de la dependencia
Ahora la pregunta es ¿podemos aplicar esta misma idea a nuestra red neuronal?
La respuesta es sí.
Para hacer eso, primero debemos comprender cómo un cambio en w1w_1 viaja a través de la red neuronal antes de que finalmente afecte la pérdida.
Anotemos las ecuaciones de nuestra red neuronal.
[
z_1=w_1x+b_1
]
(z_1) depende del peso (w_1), la entrada (x) y el sesgo (b_1).
A continuación, aplicamos la función de activación ReLU.
[
a_1=mathrm{ReLU}(z_1)
]
Aquí, la activación (a_1) depende completamente de (z_1).
La predicción de nuestra red es
[
hat{y}=w_3a_1+w_4a_2+b_3.
]
Podemos ver que la predicción depende de la activación (a_1).
Finalmente, calculamos la pérdida.
[
L=(y-hat{y})^2.
]
La pérdida depende de la predicción.
Si conectamos todas estas relaciones juntas, obtenemos
[
w_1
flecha derecha
z_1
flecha derecha
a_1
flecha derecha
sombrero{y}
flecha derecha
l.
]
Comparando esto con el ejemplo simple que consideramos.
Ejemplo sencillo:
[
incógnita
flecha derecha
y
flecha derecha
z.
]
Red neuronal:
[
w_1
flecha derecha
z_1
flecha derecha
a_1
flecha derecha
sombrero{y}
flecha derecha
l
]
Como ya dijimos en la parte 1, la única diferencia es que la red neuronal tiene una cadena de dependencia más larga, pero la idea subyacente sigue siendo la misma.
Derivados parciales versus la regla de la cadena
Llegados a este punto, podemos confundirnos entre las derivadas parciales y la regla de la cadena.
Aclaremos ambas ideas antes de seguir adelante.
En el ejemplo sencillo,
[
y=x^2
]
(y) solo depende de la variable de entrada, (x). Entonces, para encontrar el cambio en (y) wrt (x), las derivadas ordinarias son suficientes y escribimos
[
frac{dy}{dx}
]
Pero considere la ecuación
[
z_1=w_1x+b_1
]
Aquí (z_1) depende de tres variables: (w_1), (x) y (b_1).
Ahora queremos saber cómo cambia (z_1) cuando solo cambia (w_1).
Para esto, mantenemos temporalmente fijos (x) y (b_1).
Por lo tanto, escribimos
[
frac{z_1 parcial}{w_1 parcial}
]
El símbolo (partial) nos dice que estamos cambiando solo una variable mientras mantenemos las variables restantes como constantes durante ese cálculo en particular.
Esto es lo que sabemos sobre las derivadas parciales.
Ahora bien, llegando a la regla de la cadena, es un concepto diferente.
Tengamos cuidado de no confundir estos dos conceptos diferentes.
Una derivada parcial responde a la pregunta:
¿Qué variable estamos cambiando?
Por ejemplo,
[
frac{z_1 parcial}{w_1 parcial}
]
lo que significa que solo (w_1) puede cambiar.
La regla de la cadena responde a una pregunta diferente:
¿Cómo viaja el efecto de una variable a través de varios cálculos intermedios?
En nuestra red neuronal,
[
w_1
flecha derecha
z_1
flecha derecha
a_1
flecha derecha
sombrero{y}
flecha derecha
l
]
Como el efecto de cambiar w1w_1 alcanza la pérdida a través de cada parámetro intermedio, multiplicamos las derivadas a lo largo de este camino.
Por lo tanto,
[
frac{parcial L}{parcial w_1}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial a_1}
cdot
frac{parcial a_1}{parcial z_1}
cdot
frac{z_1 parcial}{w_1 parcial}
]
Podemos observar que no ha sucedido nada nuevo.
Seguimos aplicando la misma regla de la cadena que aprendimos en la Parte 1.
La única diferencia es que la cadena ahora es más larga y las funciones involucran múltiples variables, razón por la cual usamos derivadas parciales en lugar de derivadas ordinarias.
Aplicando la regla de la cadena a nuestra red neuronal
Paso 1: ∂L/∂y^partial L/partialhat{y}
ahora sabemos que
[
frac{parcial L}{parcial w_1}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial a_1}
cdot
frac{parcial a_1}{parcial z_1}
cdot
frac{z_1 parcial}{w_1 parcial}
]
Para calcular este gradiente, primero debemos calcular cada derivada parcial una por una.
Para que la regla de la cadena sea fácil de seguir, derivaremos el gradiente para un punto de datos.
En otras palabras, para esta derivación consideramos el caso donde
[
norte=1.
]
Por lo tanto, la pérdida del error cuadrático medio (MSE)
[
L=frac{1}{n}sum_{i=1}^{n}(y_i-hat{y}_i)^2
]
simplifica a
[
L=(y-hat{y})^2.
]
Una vez que tengamos una idea de cómo calcular el gradiente para un único punto de datos, extenderlo a todo el conjunto de datos es sencillo.
Simplemente promediamos los gradientes de todos los puntos de datos, tal como lo hicimos en la Parte 1.
La primera derivada que necesitamos calcular es
[
frac{partial L}{partial hat{y}}.
]
Pero antes de diferenciar, comprendamos primero qué nos dice esta derivada.
El valor real
[
y
]
proviene de nuestro conjunto de datos, por lo que durante la diferenciación se considera una constante.
La única cantidad que puede cambiar aquí es la predicción.
[
sombrero{y}
]
Por lo tanto,
[
frac{partial L}{partial hat{y}}
]
nos dice:
Si la predicción cambia en una pequeña cantidad, ¿cuánto cambia la pérdida?
Ahora que sabemos qué representa esta derivada, calculémosla.
Desde
[
L=(y-sombrero{y})^2
]
Primero aplicamos la regla de la potencia.
Diferenciar el cuadrado exterior.
[
frac{partial L}{partial hat{y}}
=
2(y-hat{y})
cdot
frac{partial (y-hat{y})}{partial hat{y}}
]
Ahora diferencia la expresión del interior.
La derivada de
[
y
]
con respecto a
[
sombrero{y}
]
es
[
0
]
porque el valor real no cambia.
La derivada de
[
-sombrero{y}
]
con respecto a
[
sombrero{y}
]
es
[
-1
]
Por lo tanto,
[
frac{partial (y-hat{y})}{partial hat{y}}
=
-1
]
Sustituyendo esto nuevamente en la ecuación anterior se obtiene
[
frac{partial L}{partial hat{y}}
=
2(y-hat{y})
(-1)
]
Finalmente,
[
frac{partial L}{partial hat{y}}
=
-2(y-hat{y})
]
Esto nos dice cómo cambia la pérdida cada vez que cambia la predicción.
Paso 2: ∂y^/∂a1partialhat{y}/partial a_1
Ahora la siguiente derivada que necesitamos calcular es
[
frac{partial hat{y}}{partial a_1}
]
Veamos primero qué nos dice esta derivada.
Recuerde que la neurona de salida calcula la predicción usando la ecuación
[
hat{y}=w_3a_1+w_4a_2+b_3
]
Como nos estamos diferenciando con respecto a
[
a_1
]
todas las demás cantidades
[
w_3,;w_4,;a_2,;text{y};b_3
]
son tratados como constantes.
Por lo tanto,
[
frac{partial hat{y}}{partial a_1}
]
nos dice
Si la activación [a_1] cambia en una pequeña cantidad, ¿cuánto cambia la predicción [hat{y}]?
Ahora calculemos.
comenzando con
[
hat{y}=w_3a_1+w_4a_2+b_3
]
la derivada de
[
w_3a_1
]
con respecto a
[
a_1
]
es
[
w_3
]
La derivada de
[
w_4a_2
]
es
[
0
]
porque no depende de
[
a_1
]
Similarmente,
[
b_3
]
es una constante, por lo que su derivada es
[
0
]
Por lo tanto,
[
frac{partial hat{y}}{partial a_1}
=
w_3
]
Esto nos dice que por cada unidad de aumento en
[
a_1
]
la predicción cambia por
[
w_3
]
Paso 3: ∂a1/∂z1partial a_1/partial z_1
El siguiente eslabón de nuestra cadena es
[
frac{parcial a_1}{parcial z_1}
]
Anteriormente, calculamos la activación de la primera neurona oculta utilizando la función de activación ReLU.
[
a_1=mathrm{ReLU}(z_1)
]
Por lo tanto,
[
frac{parcial a_1}{parcial z_1}
]
nos dice
Si la entrada a la función ReLU cambia una pequeña cantidad, ¿cuánto cambia su salida?
Desde
[
a_1=mathrm{ReLU}(z_1)
]
su derivada es simplemente la derivada de la función ReLU.
Por lo tanto,
[
frac{parcial a_1}{parcial z_1}
=
mathrm{ReLU}'(z_1)
]
Sabemos que la derivada de la función ReLU depende del valor de su entrada.
[
text{Si } z_1 gt 0,quad mathrm{ReLU}'(z_1)=1.
] [
text{Si } z_1 lt 0,quad mathrm{ReLU}'(z_1)=0.
]
Esto puede parecer confuso al principio, así que vamos a entenderlo con un ejemplo sencillo.
Acabamos de decir que la derivada de la función ReLU depende del valor de su entrada.
Si [z_1 gt 0] entonces [mathrm{ReLU}'(z_1)=1]
Si [z_1 lt 0] entonces [mathrm{ReLU}'(z_1)=0]
¿Pero cómo?
Entendamos esto con un ejemplo simple.
Suponer
[
z_1=3
]
La salida ReLU es
[
mathrm{ReLU}(3)=3
]
Ahora aumente ligeramente la entrada de 3 a 4.
La nueva salida se convierte
[
mathrm{ReLU}(4)=4
]
Observa lo que pasó aquí.
La entrada aumentó en
[
4-3=1
]
y la producción también aumentó en
[
4-3=1
]
Por lo tanto, la derivada es
[
frac{d(mathrm{ReLU})}{dz}
=
frac{1}{1}
=
1
]
Ahora digamos que la entrada es
[
z_1=10
]
Entonces
[
mathrm{ReLU}(10)=10
]
Si aumentamos la entrada a
[
11
]
la salida se convierte
[
mathrm{ReLU}(11)=11
]
Nuevamente, la entrada aumenta en 1 y la salida también aumenta en 1.
Por lo tanto, la derivada es
[
frac{d(mathrm{ReLU})}{dz}
=
frac{1}{1}
=
1
]
Entonces la derivada sigue siendo
[
1
]
Podemos ver que la derivada no es igual al valor de la entrada.
Aunque la entrada cambió de 3 a 10, la pendiente de la función ReLU siguió siendo la misma.
Consideremos ahora una entrada negativa.
Suponer
[
z_1=-3
]
Entonces
[
mathrm{ReLU}(-3)=0
]
Ahora, si aumentamos ligeramente la entrada a
[
-2
]
la salida sigue siendo
[
mathrm{ReLU}(-2)=0
]
La entrada cambió, pero la salida no.
Por lo tanto,
[
frac{d(mathrm{ReLU})}{dz}
=
0
]
Es por eso que la derivada de la función ReLU es
[
mathrm{ReLU}'(z_1)=1
]
para entradas positivas, y
[
mathrm{ReLU}'(z_1)=0
]
para entradas negativas.
En otras palabras, podemos decir que el gradiente pasa sin cambiar siempre que la entrada es positiva y se vuelve cero cuando la entrada es negativa.
Paso 4: ∂z1/∂w1partial z_1/partial w_1
Ahora hemos calculado todas las derivadas excepto una.
La última derivada que debemos calcular es
[
frac{z_1 parcial}{w_1 parcial}
]
recuerda que
[
z_1=w_1x+b_1
]
Dado que [x] y [b_1] son constantes con respecto a [w_1] solo el término [w_1x] cambia al derivar con respecto a [w_1]
Por lo tanto,
[
frac{z_1 parcial}{w_1 parcial}
=
incógnita
]
Poniéndolo todo junto
Ahora hemos calculado todas las derivadas parciales requeridas por la regla de la cadena.
Ellos son:
[
frac{partial L}{partial hat{y}}
=
-2(y-hat{y})
]
[
frac{partial hat{y}}{partial a_1}
=
w_3
] [
frac{parcial a_1}{parcial z_1}
=
mathrm{ReLU}'(z_1)
] [
frac{z_1 parcial}{w_1 parcial}
=
incógnita
]
Ya hemos visto que la regla de la cadena nos dice
[
frac{parcial L}{parcial w_1}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial a_1}
cdot
frac{parcial a_1}{parcial z_1}
cdot
frac{z_1 parcial}{w_1 parcial}
]
Ahora sustituye cada derivada parcial en esta expresión.
obtenemos
[
frac{parcial L}{parcial w_1}
=
-2(y-hat{y})
cdot
w_3
cdot
mathrm{ReLU}'(z_1)
cdot
incógnita
]
Desde
[
z_1=w_1x+b_1
]
también podemos escribirlo como
[
frac{parcial L}{parcial w_1}
=
-2(y-hat{y})
cdot
w_3
cdot
mathrm{ReLU}'(w_1x+b_1)
cdot
incógnita
]
Este es el gradiente de la pérdida con respecto al peso.
[
w_1
]
Nos dice cuánto cambia la pérdida cuando hacemos un cambio muy pequeño en
[
w_1
]
Como sabemos, durante el descenso de gradiente, esto se utiliza para actualizar el peso en la dirección que reduce la pérdida.
Una observación interesante
Note algo interesante.
Llegamos exactamente al mismo gradiente que derivamos en la Parte 1, pero esta vez lo alcanzamos usando la regla de la cadena en lugar de la diferenciación clásica.
Acabamos de descubrir una forma mucho más sistemática de calcular gradientes.
Aunque esta derivación parece más larga, observe lo que sucedió.
Cada paso fue simple. Nunca tuvimos que diferenciar toda la red neuronal a la vez, sino que solo diferenciamos una pequeña parte a la vez.
El verdadero desafío
Ahora ¿qué sigue?
Hemos calculado con éxito el gradiente para un parámetro, pero aún quedan seis parámetros más.
¿Deberíamos repetir el mismo proceso para cada parámetro?
Veamos qué pasa cuando intentamos repetir el proceso.
Primero, echemos un vistazo a las ecuaciones de la regla de la cadena para todos los parámetros.
[
frac{parcial L}{parcial w_1}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial a_1}
cdot
frac{parcial a_1}{parcial z_1}
cdot
frac{z_1 parcial}{w_1 parcial}
]
[
frac{parcial L}{parcial b_1}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial a_1}
cdot
frac{parcial a_1}{parcial z_1}
cdot
frac{parcial z_1}{parcial b_1}
] [
frac{parcial L}{parcial w_2}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial a_2}
cdot
frac{parcial a_2}{parcial z_2}
cdot
frac{z_2 parcial}{w_2 parcial}
] [
frac{parcial L}{parcial b_2}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial a_2}
cdot
frac{parcial a_2}{parcial z_2}
cdot
frac{z_2 parcial}{b_2 parcial}
] [
frac{parcial L}{parcial w_3}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial w_3}
] [
frac{parcial L}{parcial w_4}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial w_4}
] [
frac{parcial L}{parcial b_3}
=
frac{partial L}{partial hat{y}}
cdot
frac{partial hat{y}}{partial b_3}
]
Ahora, al observar las ecuaciones, podemos observar que muchas de las derivadas parciales aparecen repetidamente.
Por ejemplo,
[
frac{partial L}{partial hat{y}}
]
aparece en cada ecuación.
Similarmente,
[
frac{partial hat{y}}{partial a_1}
]
y
[
frac{parcial a_1}{parcial z_1}
]
aparecen en ambos gradientes de [w_1] y [b_1]
Del mismo modo,
[
frac{partial hat{y}}{partial a_2}
]
y
[
frac{parcial a_2}{parcial z_2}
]
aparecen en ambos gradientes de [w_2] y [b_2]
Esto significa que si calculamos el gradiente para cada parámetro por separado, calcularemos repetidamente muchas de las mismas derivadas parciales una y otra vez.
A medida que crece el número de parámetros en una red neuronal, repetir estos cálculos rápidamente se vuelve computacionalmente costoso.
Si bien esto puede no parecer un problema en una red neuronal pequeña, las redes neuronales modernas suelen contener miles o incluso millones de parámetros.
Repetir los mismos cálculos requeriría muchos más recursos computacionales y aumenta el tiempo de entrenamiento.
Tiene que haber una mejor manera
Entonces, ¿existe una mejor manera de calcular todos estos gradientes sin repetir los mismos cálculos?
Sí.
Pero no necesitamos un nuevo concepto matemático. La misma regla de la cadena es suficiente.
Lo que cambia es cómo lo aplicamos.
En lugar de repetir los mismos cálculos para cada parámetro, los organizamos de manera que se reutilicen resultados intermedios.
Esta idea es la base de la retropropagación.
Pero saber que los cálculos repetidos pueden reutilizarse es sólo el comienzo.
La verdadera pregunta es: ¿cómo sabe una red neuronal qué calcular primero, qué calcular a continuación y cómo se obtienen todos los gradientes en un paso hacia atrás eficiente?
Eso es exactamente lo que exploraremos en la Parte 3.
Conclusión
Ahora hemos desarrollado toda la intuición que necesitamos.
Entendemos cómo fluyen los gradientes a través de una red neuronal, por qué la regla de la cadena es esencial y por qué los cálculos repetidos se convierten en un problema a medida que crecen las redes neuronales.
La única pregunta que queda es ¿cómo puede la misma regla de la cadena calcular gradientes para millones de parámetros sin repetir los mismos cálculos?
En la siguiente parte, veremos cómo funciona esto y gradualmente comprenderemos la propagación hacia atrás.
Espero que este blog te haya resultado útil.
Si es nuevo en esta serie y desea leer los artículos anteriores, puede encontrarlos aquí.
Me encantaría escuchar tu opinión. Si tiene alguna pregunta o comentario, no dude en dejar un comentario en LinkedIn.
El aprendizaje nunca agota la mente.
—Leonardo da Vinci
¡Gracias por leer!