ひとことで言うと
誤差逆伝播法(backpropagation)は、損失を各パラメータで微分した値を、出力側から入力側へ連鎖律を適用しながら再利用して求める手続きです。学習アルゴリズムそのものではなく、勾配降下法に食わせる勾配を安く作る計算手順です。
工場のラインで不良品が出たとき、全工程を1つずつ止めて試すのではなく、最終検査の不良の内容から「直前の工程がどれだけ寄与したか」を割り出し、それを上流へ順に伝えていくようなものです。上流の責任配分は、下流ですでに計算した配分を使い回して求まる——この「使い回し」が逆伝播の正体です。
なぜ必要か
勾配さえ求まれば、パラメータ更新は勾配降下法に任せられます。問題は求め方のコストでした。
パラメータ数を 、順伝播1回のコストを とすると:
| 方法 | 勾配1セットのコスト | 精度 |
|---|---|---|
| 数値微分(中心差分) | 約 | 打ち切り誤差と桁落ちが乗る |
| 解析的に手で導出 | ネットワークを変えるたび導出し直し | 正確だが人間が間違える |
| 誤差逆伝播法 | 約 の定数倍 | 丸め誤差のみ |
が数百万を超える現代のネットワークで、 に比例する数値微分は最初から選択肢になりません。逆伝播は** に依存しない**——ここが決定的です。一方で、順伝播で計算した中間出力を保持しておく必要があるため、時間を稼ぐ代わりにメモリを払っています。
仕組み
を損失、第 層の入力を 、重みを 、活性化前の値を 、活性化関数を 、その出力を とします。
中心となるのは、活性化前の値に対する損失の勾配 です。これが層をまたいで次のように伝わります。
は要素ごとの積、 は活性化関数の導関数です。右辺に現れるのは1つ下流の だけで、そこから上流の が作れる。これが「使い回し」の中身です。 さえ手に入れば、欲しかった勾配は掛け算1つで出ます。
| 向き | 何を計算するか | 何を保存するか |
|---|---|---|
| 順伝播 | を出力側へ | 各層の と |
| 逆伝播 | を入力側へ | 勾配 |
が層ごとに掛け合わされる形になっている点に注意してください。この値が恒常的に1より小さければ積は指数的に縮み、大きければ発散します。勾配消失・勾配爆発はこの式から直接読める帰結であって、別の現象ではありません。
試験でどう問われるか
| 問われ方 | 正解に寄る条件 | 引っかけ |
|---|---|---|
| 逆伝播の計算量の説明 | パラメータ数に依存せず、順伝播の定数倍で全勾配が求まる | 「パラメータ数に比例する」は数値微分の性質 |
| の漸化式を選ばせる | 重みの転置を掛け、活性化の導関数と要素積を取る | 転置なし/行列積と要素積の入れ替え |
| 勾配消失の原因 | 層ごとの の積が0に近づく(sigmoid は最大 0.25) | 「学習率が小さすぎるから」は別の話 |
| 逆伝播と勾配降下法の関係 | 逆伝播は勾配を求める手順、更新するのは最適化手法 | 「逆伝播がパラメータを更新する」 |
| 順伝播で何を保持するか | 中間出力を保持するためメモリを消費する | 「メモリを使わないので効率的」 |
実装で確かめる
連鎖律で求めた勾配が、数値微分と一致することを見ます。逆伝播が正しく実装できているかを確かめる定番の手順(勾配確認)でもあります。
import numpy as np
rng = np.random.default_rng(0)
W1, b1 = rng.normal(size=(3, 4)), np.zeros(4)
W2, b2 = rng.normal(size=(4, 1)), np.zeros(1)
x, y = rng.normal(size=(5, 3)), rng.normal(size=(5, 1))
def forward(W2): # W2 以外は固定して損失を返す
h = np.tanh(x @ W1 + b1)
return h, ((h @ W2 + b2 - y) ** 2).mean()
h, loss = forward(W2)
g = 2 * (h @ W2 + b2 - y) / y.size # まず出力に対する勾配を求め
gW2 = h.T @ g # 連鎖律で W2 まで一度に降ろす
num = np.zeros_like(W2) # 同じものを数値微分でも作る
eps = 1e-6
for i in range(W2.size):
Wp, Wm = W2.copy(), W2.copy()
Wp.flat[i] += eps; Wm.flat[i] -= eps
num.flat[i] = (forward(Wp)[1] - forward(Wm)[1]) / (2 * eps)
print("最大誤差:", np.abs(gW2 - num).max())
実行すると 最大誤差: 4.96277990924554e-10 になります。値は一致しますが、数値微分側はパラメータ1つにつき順伝播2回を回しているのに対し、逆伝播側は行列積1つで済んでいます。この差が 倍になって効いてきます。
勾配確認は差分幅 の取り方に敏感です。小さくしすぎると桁落ちで誤差が悪化するため、単調に改善するわけではありません。実装検査以外の目的で数値微分を使うことはまずありません。
取り違えやすいもの
| 用語 | 誤差逆伝播法との関係 |
|---|---|
| 自動微分(リバースモード) | 逆伝播の一般形。逆伝播は「ニューラルネットに適用したリバースモード自動微分」にあたる |
| 数値微分 | 別の勾配計算手段。実装検査に使う。コストがパラメータ数に比例する |
| 勾配降下法・Adam | 勾配を使う側。逆伝播は勾配を作る側で、役割が違う |
| 誤差関数(損失関数) | 逆伝播の出発点となる量。逆伝播そのものではない |
| BPTT | 逆伝播を時間方向に展開したリカレントネット向けの適用形 |
想起チェック
逆伝播で全パラメータの勾配を求めるコストは、パラメータ数に対してどうスケールするか
に依存せず、順伝播1回の定数倍で済みます。中間出力を保持するぶんメモリは増えます。
ある層のデルタを、1つ下流の層のデルタから求める式は
。重みは転置、活性化の導関数とは要素ごとの積です。
勾配消失が起きる理由を、上の式から説明すると
層をさかのぼるたびに が掛け合わされるため、この値が1より小さいと積が指数的に0へ近づきます。sigmoid の導関数は最大でも 0.25 です。
「誤差逆伝播法がパラメータを更新する」は正しいか
正しくありません。逆伝播が作るのは勾配までで、更新するのは勾配降下法や Adam などの最適化手法です。