1. 逆伝播(backpropagation)とは
📚 このレッスンの前提(必ず先に確認を): 本レッスンは、勾配・最適化の理論を前提に、それをコードへ落とし込みます。理論があいまいだと式の意味が追いにくいので、 E資格対策「深層モデルのための最適化」 に先に目を通しておくことを強くおすすめします。勾配降下・パラメータ更新の考え方を理解していないと、ここから登場する式(勾配の計算と更新)が腑に落ちにくくなります。
順伝播で出した予測と正解とのズレ(損失)を小さくするには、各パラメータを「どちらにどれだけ動かせば損失が減るか」を知る必要があります。 その情報が勾配(gradient)。逆伝播は、連鎖律(chain rule)を使って出力側から入力側へ勾配を順々に伝える計算です。
学習の1ステップは 順伝播 → 損失 → 逆伝播(勾配計算)→ パラメータ更新。
PyTorch ではこの「逆伝播」を loss.backward() が自動でやってくれますが、
ここで中身を手で書いておくと、autograd(自動微分)の挙動が腑に落ちます。
dL/dW = (dL/dz)·(dz/dW)。損失から各層へ「微分のかけ算」でさかのぼります。
「勾配の形(shape)は、対応するパラメータの形と同じ」——これが実装時の強力なチェックになります。
2. 単層 + MSE の逆伝播
まず一番シンプルに、全結合1層 pred = X·W + b と平均二乗誤差(MSE)で勾配を求めます。
手で導いた式は次の3つだけです(N はサンプル数)。この3式は全結合層の逆伝播の「型」なので、ぜひ覚えてしまいましょう。
dpred = 2·(pred − y) / N… 損失から予測への勾配(形(N, 1))dW = Xᵀ · dpred… 重みの勾配(形はWと同じ(D, 1))db = dpred を行方向に合計… バイアスの勾配(形(1,))
💡 勾配の形=パラメータの形:
dW は必ず W と同じ形 (3, 1) になります。これは更新 W = W − lr·dW が要素ごとに行えるため必須。
勾配の形が合わない=どこかで転置や軸を間違えているサインです。
🔑 この3式は丸暗記でOK(コードの形):
dpred = 2 * (pred - y) / N… 損失 → 予測 の勾配dW = X.T @ dpred… 重みの勾配(入力の転置 × 上流の勾配)db = dpred.sum(axis=0)… バイアスの勾配(行方向=サンプル方向に合計)
全結合層が出てくるたびに、この形が繰り返し登場します。
3. 2層 + ReLU の逆伝播(連鎖律をさかのぼる)
次は中間層を1つ増やします。順伝播は z1 = X·W1+b1 → h = ReLU(z1) → pred = h·W2+b2。
逆伝播は出力側から1ステップずつさかのぼります。ポイントは ReLU の微分:
入力が正だった要素は勾配をそのまま通し、負だった要素は0で遮断します(z1 > 0 のマスク)。
dh = dpred @ W2.T は「重み行列の転置を掛けて勾配を前の層へ戻す」という逆伝播の定石。
dz1 = dh * (z1 > 0) は ReLU の微分(正で1・負で0)を掛けているだけです。
この「転置を掛けて戻す → 活性化の微分を掛ける」の繰り返しが誤差逆伝播の本体です。
🔑 ReLU の逆伝播はこの1行: dz1 = dh * (z1 > 0)
(z1 > 0) は「順伝播のとき正だった所が True(=1)、負だった所が False(=0)」のマスク。
これを上流の勾配 dh に掛けるだけで、正だった所はそのまま通し、負だった所は0で止める——これが ReLU の微分です。
短い式ですが非常に重要なので、形ごと覚えておきましょう。
4. 勾配チェック(数値微分と一致するか)
自分で連鎖律から導いた勾配(=解析的な勾配)が本当に正しいか、別のやり方で「答え合わせ」できます。それが 数値微分です。
そもそも微分とは「入力をほんの少し動かしたら、出力(ここでは損失)がどれだけ変化するか=傾き」のこと。
だから、あるパラメータを +ε と −ε だけ動かして損失を測り、その差を動かした幅で割れば、勾配の近似値が手に入ります((L(+ε) − L(−ε)) / 2ε)。
これは中学・高校で習った「(変化量)÷(動かした量)で傾きを出す」のと同じ発想です。
この数値微分の値と、連鎖律で求めた解析的な勾配がほぼ一致すれば、逆伝播の実装は正しいと確認できます。
💡 ここまでが「自動微分」の正体:
私たちは層ごとに勾配の式を手で書きました。PyTorch の autograd は、この勾配計算を計算グラフから自動で組み立て、
loss.backward() の一行で実行します。次のレッスンで、その PyTorch のやり方を見ます。
5. 練習問題
重みの勾配 dW を求める
単層 pred = X·W + b で、dpred(損失から予測への勾配)が与えられているとき、重みの勾配 dW を計算してください(dW は W と同じ形になるはず)。
ヒントを見る(答え+解説)
import numpy as np
np.random.seed(2)
X = np.random.randn(6, 4) # (N=6, D=4)
W = np.random.randn(4, 1) # (4, 1)
dpred = np.random.randn(6, 1) # 損失から予測への勾配(与えられているとする)
# 重みの勾配 dW を求めてください(形は (4, 1) になるはず)
dW = X.T @ dpred
print("dW.shape:", dW.shape) # (4, 1)
Xᵀ は (4, 6)、dpred は (6, 1) なので (4,6)@(6,1)=(4,1)。
「入力の転置 × 上流の勾配」が全結合層の重み勾配の形です。
ReLU の逆伝播
順伝播で h = ReLU(z1) を計算しました。上流から来た勾配 dh を、ReLU を通して dz1 に変換してください(z1が正だった所だけ通す)。
ヒントを見る(答え+解説)
import numpy as np
z1 = np.array([[ 1.0, -2.0],
[-0.5, 3.0]])
dh = np.array([[ 0.7, 0.4],
[ 0.2, 0.9]])
# z1 が正だった要素だけ dh を通し、負だった要素は0にしてください
dz1 = dh * (z1 > 0)
print("dz1 =\n", dz1)
# 期待: [[0.7 0. ]
# [0. 0.9]]
(z1 > 0) は True/False の配列(1/0として計算される)。これを掛けることで、
正だった要素だけ勾配が通り、負だった要素は遮断されます。これが ReLU の微分です。
6. まとめ
このレッスンのポイント
- 逆伝播は連鎖律で、出力側から入力側へ勾配を伝える計算
- 全結合層の重み勾配は
dW = Xᵀ · (上流の勾配)。形は W と同じ - 勾配を前の層へ戻すときは重みの転置を掛ける(
dh = dpred @ W2.T) - ReLU の微分は
(z > 0)のマスク。正だった所だけ通す - パラメータ更新(SGD)は
W = W − lr · dW。勾配の逆向きに少し動かす - 勾配チェック(数値微分との比較)で実装の正しさを確認できる
- PyTorch の
loss.backward()は、この勾配計算を自動でやってくれる(次のレッスン)
学習ループを回して、損失が下がり続けるのを観察してみましょう。下のコードは「答え(本当の重み)が分かっている練習用データ」を用意し、勾配降下がその答えを当てられるかを試します。
完了するとコース一覧に進捗が記録されます