La joya de la corona. Cómo calcular el gradiente de CADA peso de la red de forma eficiente, usando la regla de la cadena hacia atrás. Lo haremos número por número, sin un solo gap, sobre nuestra red XOR.
En la lección 6 vimos que necesitamos dL/dw para cada peso. Pero la pérdida está
al final de una cadena: el peso afecta a z, que afecta a a, que afecta
a la siguiente capa, que afecta a la pérdida. ¿Cómo calculamos el efecto de un peso enterrado en
la primera capa sobre la pérdida final?
La solución es la regla de la cadena — la misma idea de los engranajes: si A mueve a B, y B mueve a C, el efecto de A sobre C es el producto de los dos efectos. Aquí: el efecto del peso sobre la pérdida = (efecto del peso sobre z) × (efecto de z sobre a) × (efecto de a sobre L).
dL/dw = (dL/da)·(da/dz)·(dz/dw). Backpropagation aplica esta regla hacia atrás, capa por capa, reutilizando lo ya calculado. De ahí el nombre: propagar el error hacia atrás.
Trabajamos con x = (1, 0), target = 1. Recordemos lo que ya calculamos:
a₁ = [0.524979, 0.475021] (salida capa oculta) ŷ = a₂ = 0.543014 (predicción) L = 0.104418 (MSE ½) W₂ = [0.50, -0.40] W₁ = [[0.20, -0.30], [0.40, 0.10]]
Dato útil de la sigmoid: su derivada es σ'(z) = a·(1 − a), donde a = σ(z).
Súper cómoda: solo necesitamos la activación que ya tenemos.
Con MSE L = ½(a₂ − t)², la derivada es directa (por eso el ½):
dL/da₂ = (a₂ - t) = 0.543014 - 1 = -0.456986
Multiplicamos por la derivada de la sigmoid a₂(1−a₂):
dL/dz₂ = dL/da₂ · a₂·(1-a₂)
= -0.456986 · (0.543014 · 0.456986)
= -0.456986 · 0.248151
= -0.113401
Como z₂ = a₁·W₂ + b₂, la derivada respecto a cada peso es la activación que lo multiplicaba. ¿Por qué? Por la misma razón que la derivada de 3x respecto a x es 3: si vendes a unidades a precio p, tu ingreso es a·p — subir p en 1 sube el ingreso en a. Aquí: z₂ = a₁·W₂, así que subir W₂ en 1 sube z₂ en a₁ — eso es la derivada.
dL/dW₂ = a₁ · dL/dz₂ peso de a₁: 0.524979 · -0.113401 = -0.059533 peso de a₂: 0.475021 · -0.113401 = -0.053868 dL/db₂ = dL/dz₂ = -0.113401 (el bias multiplica por 1)
El error de z₂ se reparte hacia atrás según los pesos W₂ que lo trajeron:
dL/da₁ = dL/dz₂ · W₂ hacia a₁: -0.113401 · 0.50 = -0.056701 hacia a₂: -0.113401 · -0.40 = 0.045360
Otra vez por a(1−a), ahora con las activaciones ocultas:
a₁·(1-a₁): [0.524979·0.475021, 0.475021·0.524979] = [0.249377, 0.249377] dL/dz₁ = dL/da₁ · a₁·(1-a₁) z₁ de h₁: -0.056701 · 0.249377 = -0.014140 z₁ de h₂: 0.045360 · 0.249377 = 0.011312
Igual que en el paso 3, pero la entrada ahora es x = (1, 0):
dL/dW₁ = x · dL/dz₁ (cada componente de x multiplica cada componente de dL/dz₁)
h₁ h₂
x₁=1: 1·-0.014140=-0.014140 1·0.011312=0.011312
x₂=0: 0·-0.014140=0 0·0.011312=0
dL/db₁ = dL/dz₁ = [-0.014140, 0.011312]
x₂ son
0, porque x₂ = 0 en este ejemplo. Una entrada apagada no recibe
corrección. Tiene todo el sentido.
Acabamos de calcular, sin saltarnos nada, las derivadas de la pérdida respecto a los 9 valores entrenables:
Capa de salida: dL/dW₂ = [-0.059533, -0.053868] dL/db₂ = -0.113401 Capa oculta: dL/dW₁ = [[-0.014140, 0.011312], [0, 0]] dL/db₁ = [-0.014140, 0.011312]
En la lección 8 usamos estos números en la regla w = w − lr·gradiente para mejorar la red de verdad. Y en la lección 9, PyTorch calculará exactamente estos mismos números automáticamente.