Kedjeregeln i flera variabler
Kunna tillämpa kedjeregeln på sammansatta funktioner av vektorer — det som backpropagation bygger på.
Förkunskaper
Intuition
I en variabel: om y = f(g(x)) så är dy/dx = f′(g(x)) · g′(x). Derivatorna multipliceras längs kedjan.
I flera variabler gäller samma sak, fast med matriser. Ett nät är en kedja:
x → z₁ = W₁x → a₁ = σ(z₁) → z₂ = W₂a₁ → … → L
För att veta hur L ändras när W₁ ändras multipliceras alla mellanledens derivator ihop, bakifrån. Det är backpropagation — inte en separat algoritm, utan kedjeregeln tillämpad effektivt.
Härledning
För är jacobianen en -matris med .
Kedjeregeln: för gäller — matrisprodukt, i den ordningen.
För en skalär förlust arbetar man med gradienter (rader) i stället för fulla jacobianer. Ett lager , :
Varför bakifrån? Med indata och en skalär utdata kostar framåtdifferentiering (en riktning i taget) genomgångar; bakåtdifferentiering ger alla partiella derivator i en genomgång. Med miljontals parametrar är skillnaden avgörande — det är hela skälet till att reverse-mode AD dominerar i deep learning.
Konsekvensen för djupa nät: gradienten till lager 1 är en produkt av många faktorer. Är de systematiskt < 1 försvinner den; är de > 1 exploderar den. Residualkopplingar ger en «genväg» där faktorn är 1, vilket är varför de gör mycket djupa nät tränbara.
Kod
import numpy as np
def framat(x, W1, b1, W2, b2):
z1 = W1 @ x + b1
a1 = np.tanh(z1)
z2 = W2 @ a1 + b2
return z1, a1, z2
def bakat(x, y, W1, b1, W2, b2):
z1, a1, z2 = framat(x, W1, b1, W2, b2)
dL_dz2 = 2 * (z2 - y) # L = ||z2 - y||²
dL_dW2 = np.outer(dL_dz2, a1)
dL_da1 = W2.T @ dL_dz2 # kedjeregeln bakåt
dL_dz1 = dL_da1 * (1 - np.tanh(z1) ** 2) # tanh' = 1 - tanh²
dL_dW1 = np.outer(dL_dz1, x)
return dL_dW1, dL_dz1, dL_dW2, dL_dz2
# Numerisk kontroll — gör alltid detta när du implementerat backprop själv
rng = np.random.default_rng(0)
x, y = rng.normal(size=4), rng.normal(size=2)
W1, b1 = rng.normal(size=(3, 4)) * 0.3, np.zeros(3)
W2, b2 = rng.normal(size=(2, 3)) * 0.3, np.zeros(2)
dW1 = bakat(x, y, W1, b1, W2, b2)[0]
eps = 1e-6; num = np.zeros_like(W1)
for i in range(W1.shape[0]):
for j in range(W1.shape[1]):
Wp = W1.copy(); Wp[i, j] += eps
Wm = W1.copy(); Wm[i, j] -= eps
Lp = ((framat(x, Wp, b1, W2, b2)[2] - y) ** 2).sum()
Lm = ((framat(x, Wm, b1, W2, b2)[2] - y) ** 2).sum()
num[i, j] = (Lp - Lm) / (2 * eps)
print(np.abs(dW1 - num).max() < 1e-6) # True
Behärskning innebär
- Tillämpar kedjeregeln på sammansatta vektorfunktioner
- Skriver gradienten som en produkt av jacobianer
- Kopplar till backpropagation
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- Dive into Deep Learning (CC BY-SA 4.0) — CC BY-SA 4.0
- PyTorch — Autograd mechanics (BSD-3) — BSD-3-Clause