この記事は、書籍『Kaggleではじめる大規模言語モデル入門』の輪読会で学んだテーマを、自分の言葉で整理・深掘りしたものです。
はじめに
深層学習モデルの学習は、損失関数の勾配をパラメータの数だけ求め、その勾配方向にパラメータを更新していく作業に帰着します。
この勾配計算を多層のネットワークに対しても効率よく行うための手法が、誤差逆伝播法です。
以前の記事「MLP(多層パーセプトロン)を線形代数から理解する」では、誤差逆伝播法を「モデルの予測の誤差を出力側から入力側へ逆向きに伝え、各重みをどう直すか計算する手法」として簡潔に紹介しました。
この一文だけでは、次の2点が見えてきません。
- 「逆向きに伝える」とは、数式としてどの計算を指しているのか
- なぜわざわざ逆向きに計算する必要があるのか。順向きに微分を伝えてはいけないのか
本記事では、連鎖律という1つの道具から出発して誤差逆伝播法の計算式を実際に手を動かして導出し、この2つの問いに答えます。
対象読者は、大学レベルの微分・線形代数の素養はあるものの、深層学習の勾配計算を自分の手で追ったことはない方を想定しています。
1. 学習の中心課題は勾配を求めること
ニューラルネットワークの学習は、パラメータ $\theta$(すべての重み $W^{(\ell)}$ とバイアス $b^{(\ell)}$ をまとめたもの)を、損失関数 $\mathcal{L}(\theta)$ が小さくなる方向へ少しずつ動かす作業です。
この更新は勾配降下法によって次のように書けます。
\theta \leftarrow \theta - \eta \nabla \mathcal{L}(\theta)
$\eta$ は学習率、$\nabla \mathcal{L}(\theta)$ は損失をすべてのパラメータで偏微分した勾配ベクトルです。
この式自体は単純ですが、実際にモデルを学習させる作業のほとんどは「$\nabla \mathcal{L}(\theta)$ をどう計算するか」という1点に集約されます。
パラメータ数が数個であれば手計算でも求められますが、現実のモデルは数百万から数十億のパラメータを持ち、しかも各パラメータは何層もの合成関数の内部に埋め込まれています。
この状況で全パラメータの勾配を効率よく求める方法こそが、誤差逆伝播法です。
以降では、記号を前回の記事に合わせ、第 $\ell$ 層の活性化前の値を $z^{(\ell)}$、活性化後の値を $h^{(\ell)}$、重み行列を $W^{(\ell)}$、活性化関数を $\phi$ と書きます。
2. 連鎖律の復習
誤差逆伝播法の土台になっているのは、微分積分で習う連鎖律です。
まず1変数の場合を確認します。
$y = f(u)$、$u = g(x)$ という合成関数があるとき、$y$ を $x$ で微分すると次のようになります。
\frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx}
合成関数の微分は、途中の変数を経由する「伝わりやすさ」の積で求まる、というのが連鎖律の意味です。
この関係は、合成が何段続いても同様に成り立ちます。
$y = f_3(f_2(f_1(x)))$ のような3段の合成であれば、次のように各段の微分をすべて掛け合わせます。
\frac{dy}{dx} = \frac{dy}{du_3} \cdot \frac{du_3}{du_2} \cdot \frac{du_2}{dx}
多変数の場合も考え方は同じです。
入力がベクトル $x \in \mathbb{R}^n$、中間変数がベクトル $u \in \mathbb{R}^m$、出力がスカラー $y$ のとき、$y$ を $x_i$ で偏微分すると、$u$ の各成分を経由する寄与をすべて足し合わせる形になります。
\frac{\partial y}{\partial x_i} = \sum_{k=1}^{m} \frac{\partial y}{\partial u_k} \cdot \frac{\partial u_k}{\partial x_i}
行列とベクトルでまとめて書けば、$\partial y / \partial x = (\partial u / \partial x)^{\top} (\partial y / \partial u)$ という形になります。
$\partial u / \partial x$ はヤコビ行列と呼ばれる、各出力成分を各入力成分で偏微分した値を並べた行列です。
ニューラルネットワークの各層は、この「ベクトルからベクトルへの合成関数」の一段分に相当します。
つまり誤差逆伝播法とは、この多変数版の連鎖律を、層の数だけ機械的に繰り返し適用する手続きにほかなりません。
3. 小さな2層MLPで勾配を手で追う
抽象的な議論だけでは実感が湧きにくいので、具体的な数値を使って最初から最後まで手で計算します。
2次元の入力 $x = (1, 2)$ を受け取り、2ユニットの隠れ層(活性化関数は $\tanh$)を経て、1ユニットの線形な出力層(回帰タスク)へつなぐ、次のようなネットワークを考えます。
h^{(0)} = x, \qquad
z^{(1)} = h^{(0)} W^{(1)} + b^{(1)}, \qquad
h^{(1)} = \tanh\!\left(z^{(1)}\right), \qquad
\hat{y} = h^{(1)} W^{(2)} + b^{(2)}
パラメータの値を次のように定めます。
W^{(1)} = \begin{pmatrix} 0.1 & -0.2 \\ 0.3 & 0.4 \end{pmatrix}, \quad
b^{(1)} = \begin{pmatrix} 0.05 \\ -0.1 \end{pmatrix}, \quad
W^{(2)} = \begin{pmatrix} 0.5 \\ -0.3 \end{pmatrix}, \quad
b^{(2)} = 0.2
正解ラベルを $y = 1.0$ とし、損失は二乗誤差 $\mathcal{L} = \frac{1}{2}(\hat{y} - y)^2$ とします。
まず順伝播で各層の値を計算します。
z^{(1)}_1 = 1 \times 0.1 + 2 \times 0.3 + 0.05 = 0.75, \qquad
z^{(1)}_2 = 1 \times (-0.2) + 2 \times 0.4 - 0.1 = 0.5
h^{(1)}_1 = \tanh(0.75) \approx 0.6351, \qquad
h^{(1)}_2 = \tanh(0.5) \approx 0.4621
\hat{y} = 0.6351 \times 0.5 + 0.4621 \times (-0.3) + 0.2 \approx 0.3789
損失は $\mathcal{L} = \frac{1}{2}(0.3789 - 1)^2 \approx 0.1929$ です。
ここからが逆伝播です。
まず出力層の誤差信号を求めます。
出力層の活性化関数は恒等写像(傾き1)なので、損失を $z^{(2)} = \hat{y}$ で微分した値は次のようにそのまま求まります。
\delta^{(2)} = \frac{\partial \mathcal{L}}{\partial z^{(2)}} = \hat{y} - y \approx -0.6211
$\hat{y} = h^{(1)} W^{(2)} + b^{(2)}$ という式から、出力層の重みの勾配は $\delta^{(2)}$ と隠れ層の出力 $h^{(1)}$ の積で求まります。
\frac{\partial \mathcal{L}}{\partial W^{(2)}_1} = \delta^{(2)} h^{(1)}_1 \approx -0.3945, \qquad
\frac{\partial \mathcal{L}}{\partial W^{(2)}_2} = \delta^{(2)} h^{(1)}_2 \approx -0.2871, \qquad
\frac{\partial \mathcal{L}}{\partial b^{(2)}} = \delta^{(2)} \approx -0.6211
次に隠れ層の勾配です。
ここで初めて活性化関数の導関数が登場します。
$\tanh$ の導関数は $\tanh'(z) = 1 - \tanh^2(z)$ なので、それぞれの値は次のようになります。
\tanh'(z^{(1)}_1) = 1 - 0.6351^2 \approx 0.5967, \qquad
\tanh'(z^{(1)}_2) = 1 - 0.4621^2 \approx 0.7865
隠れ層の誤差信号 $\delta^{(1)}$ は、出力層の $\delta^{(2)}$ を重み $W^{(2)}$ で送り戻し、その場所で活性化関数の導関数を掛けることで得られます。
\delta^{(1)}_1 = \delta^{(2)} W^{(2)}_1 \cdot \tanh'(z^{(1)}_1) \approx -0.1854, \qquad
\delta^{(1)}_2 = \delta^{(2)} W^{(2)}_2 \cdot \tanh'(z^{(1)}_2) \approx 0.1466
隠れ層の重みの勾配は、この $\delta^{(1)}$ と入力 $x$ の積で求まります。
\frac{\partial \mathcal{L}}{\partial W^{(1)}_{1,1}} = \delta^{(1)}_1 x_1 \approx -0.1854, \qquad
\frac{\partial \mathcal{L}}{\partial W^{(1)}_{2,1}} = \delta^{(1)}_1 x_2 \approx -0.3708
\frac{\partial \mathcal{L}}{\partial W^{(1)}_{1,2}} = \delta^{(1)}_2 x_1 \approx 0.1466, \qquad
\frac{\partial \mathcal{L}}{\partial W^{(1)}_{2,2}} = \delta^{(1)}_2 x_2 \approx 0.2931
この計算の順序に注目してください。
出力層で求めた $\delta^{(2)}$ を、隠れ層の勾配を求める際にそのまま使い回しています。
新しく計算し直したのは、$W^{(2)}$ による送り戻しと、活性化関数の導関数を掛ける部分だけです。
これが「誤差を出力側から入力側へ逆向きにたどる」という説明の、数式レベルでの正体です。
4. 一般の層への漸化式
前節の計算を、層数が任意の場合に一般化します。
第 $\ell$ 層の誤差信号を $\delta^{(\ell)} = \partial \mathcal{L} / \partial z^{(\ell)}$ と定義すると、前節の計算はそのまま次の漸化式にまとまります。
\delta^{(\ell)} = \delta^{(\ell+1)} \left(W^{(\ell+1)}\right)^{\top} D_\ell, \qquad
D_\ell = \mathrm{diag}\!\left(\phi'(z^{(\ell)})\right)
$D_\ell$ は、活性化関数の導関数の値を対角成分に並べた対角行列です。
この漸化式は、出力層に近い側から順に $\delta^{(L)}, \delta^{(L-1)}, \dots, \delta^{(1)}$ という順番でしか計算できません。
$\delta^{(\ell)}$ を求めるには、1つ出力側にある $\delta^{(\ell+1)}$ がすでに求まっている必要があるためです。
各層のパラメータに対する勾配は、その層の $\delta^{(\ell)}$ と、1つ入力側にある層の出力 $h^{(\ell-1)}$ から、次の形でまとめて求まります。
\frac{\partial \mathcal{L}}{\partial W^{(\ell)}} = \left(h^{(\ell-1)}\right)^{\top} \delta^{(\ell)}, \qquad
\frac{\partial \mathcal{L}}{\partial b^{(\ell)}} = \delta^{(\ell)}
つまり誤差逆伝播法とは、出力層側から $\delta^{(\ell)}$ を1層ずつ後ろ向きに計算しながら、その途中で各層のパラメータの勾配を副産物として取り出していく手続きです。
5. なぜ逆向きに計算するのか
ここまでで計算のやり方は分かりましたが、そもそもなぜ逆向きに計算する必要があるのでしょうか。
順伝播と同じ向きに、入力側から出力側へ微分を伝えていく方法は考えられないのでしょうか。
実は、そのような方法も存在します。
自動微分の分野では前向き微分と呼ばれ、ある1つのパラメータ $w$ を選び、$w$ をわずかに動かしたときに各層の値がどう変化するかを、順伝播と同じ向きに、入力側から出力側へ追いかけます。
この方法でも最終的な損失に対する勾配は正しく求まりますが、決定的な弱点があります。
前向き微分で計算できるのは、選んだ1つのパラメータ $w$ についての勾配だけだということです。
別のパラメータの勾配が欲しければ、また別の1回分の前向き計算をゼロからやり直す必要があります。
前節の例で数えてみます。
このネットワークにはパラメータが $W^{(1)}$ の4個、$b^{(1)}$ の2個、$W^{(2)}$ の2個、$b^{(2)}$ の1個、合計9個あります。
前向き微分ですべての勾配を求めるなら、この9個のパラメータそれぞれについて、入力側から出力側までの計算を1回ずつ、合計9回行うことになります。
パラメータ数を $P$ とすると、前向き微分の計算量はおおよそ「1回の順伝播のコスト」の $P$ 倍になります。
一方、誤差逆伝播法では、順伝播を1回行った後に、逆伝播を1回行うだけで、全パラメータの勾配が同時に求まります。
第4節の漸化式を見返すと分かるとおり、$\delta^{(\ell)}$ は層に対して1つだけ計算すればよく、その層に含まれる全パラメータの勾配は、その $\delta^{(\ell)}$ を使った1回の掛け算(外積)で一斉に得られます。
出力層に近い側で1度計算した $\delta^{(\ell+1)}$ は、次の層の勾配を求めるときにも、その次の $\delta^{(\ell)}$ を求めるときにも、そのまま使い回されます。
つまり逆伝播では、パラメータ数 $P$ に関わらず、順伝播1回分と逆伝播1回分、合わせておよそ2〜3回分の計算量で全パラメータの勾配が求まります。
モデルのパラメータ数が数百万、数十億という規模になったとき、この「パラメータ数に依存しない」という性質が決定的な差になります。
逆向きに計算するのは慣習ではなく、$\delta^{(\ell)}$ という共通部分を層ごとに1回だけ計算して使い回すための、計算量上の必然だといえます。
6. 勾配消失との接続
第4節の漸化式には、もう1つ重要な意味があります。
\delta^{(\ell)} = \delta^{(\ell+1)} \left(W^{(\ell+1)}\right)^{\top} D_\ell
この式を出力層から入力層まで繰り返し適用すると、入力に近い層の $\delta^{(\ell)}$ は、途中の全層の重み行列と、全層の活性化関数の導関数($D_\ell$)を、掛け算の形で通り抜けてきたものになります。
第3節の数値例でも、隠れ層の $\delta^{(1)}$ を求める際に、出力層の $\delta^{(2)}$ に $\tanh$ の導関数がそのまま掛かっていたことを確認しました。
ここで、活性化関数の導関数の値が1より十分小さい層が何層も続くとどうなるかを考えます。
1未満の値を層の数だけ繰り返し掛け合わせるため、入力側に届く $\delta^{(\ell)}$ は層が深くなるほど指数関数的に0へ近づいていきます。
これが勾配消失と呼ばれる現象です。
逆に、重みや導関数の値が1を超えた状態が続くと、今度は指数関数的に値が発散する勾配爆発が起こります。
この「活性化関数の導関数が層ごとに必ず1回掛かる」という性質は、前回の記事で扱った「隠れ層の活性化関数はなぜすべての層で同じにそろえるのか」という問いにも直接つながっています。
本記事ではその設計判断まで踏み込みませんが、勾配消失・爆発という現象そのものが、第4節の漸化式という1本の式から機械的に導かれる帰結であることは押さえておく価値があります。
まとめ
誤差逆伝播法は、多変数の連鎖律を、出力層から入力層へ向かって層ごとに機械的に適用する手続きです。
第3節で見たとおり、出力層の誤差信号 $\delta^{(2)}$ から始めて、重み行列による送り戻しと活性化関数の導関数の掛け算を繰り返すことで、隠れ層の誤差信号 $\delta^{(1)}$ が得られ、各層のパラメータの勾配はその誤差信号から一斉に取り出せます。
この計算を逆向きに行う理由は、各層の誤差信号という共通部分を1回だけ計算して使い回すことで、パラメータ数に関わらず順伝播と逆伝播のおよそ2回分の計算量で全パラメータの勾配が求まるからです。
そして、この漸化式に活性化関数の導関数と重み行列が毎層掛かるという構造自体が、深いネットワークで勾配消失・爆発が起こる直接の原因になっています。