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?
L depende de a, y a
depende de z, y z depende de w, entonces
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:
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₁ (producto externo)
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]
w = w − lr·gradiente para mejorar la red de verdad. Y en la
lección 9 comprobaremos que PyTorch (con autograd) calcula
exactamente estos mismos números — automáticamente.