0. はじめに
今まで何度か勉強してきたディープラーニングの仕組み。ただ、細かい数式理解に関してはおろそかにしていて、数学的に理解仕切れていない状態でした。
自分と同じような境遇の方向けに、基礎にして最大の肝である逆誤差伝播法についてまとめました。わかりやすいシンプルな例を用いるとともに、当たり前として省かれてしまいそうな簡単なところまで丁寧に説明しています。お役に立てれば幸いです。
1. 逆誤差伝播法とは何か、なぜ学ぶのか?
ディープラーニングの根幹を支える「逆誤差伝播法(バックプロパゲーション)」は、ニューラルネットワークの学習を可能にしている最も重要なアルゴリズムのひとつです。この技術がなければ、画像認識、音声翻訳、ChatGPTのような大規模言語モデルも実現することはできません。
「逆誤差伝播法」とは何を行なっているのでしょうか?
一言でいえば、「ネットワークが出した誤差を各層へと逆向きに伝え、重みを効率よく調整するための手法」です。これにより、モデルは「どの重みを、どの程度修正すればよいか」を学習を通じて知ることができます。数学的には、連鎖律(Chain Rule)を用いて、出力層から入力層へと微分値(勾配)を計算し伝播させることで、全体の損失関数を各重みに対して効率的に最小化する仕組みになっています。
では、なぜこのアルゴリズムを数学的に理解することが重要なのでしょうか?
それは、以下のような力が身につくからです:
- ニューラルネットワークの挙動や収束の仕組みを深く理解できる
- 自作のモデルや新しい構造を提案する際、適切な学習アルゴリズムを設計できる
本記事では、逆誤差伝播法の基本的な仕組みを丁寧に解説しつつ、その数学的な構造を追っていきます。数式が苦手な方も、できるだけ丁寧にお伝えしていきますので、ぜひ最後までお付き合いください。
2. 超シンプルな1層ニューラルネットワークで逆誤差伝播法を体感する
逆誤差伝播法というと、「微分」「連鎖律」「多層ネットワーク」など、いかにも複雑そうなイメージを持ちがちです。しかし本質は、「誤差を元にして重みをどう変えたらいいかを計算する」ただそれだけの話です。
ここでは、できるだけシンプルな例を使って、その“心臓部”に迫ってみます。
モデル構造
考えるのは以下のような超シンプルなネットワークです。
- 入力: $x=2$
- 重み: $w$(学習すべきパラメータ)
- 出力: $y=wx$
- 正解ラベル: $t=8$
- 損失関数: 平均二乗誤差 (MSE): $L = \frac{1}{2}(y - t)^2$
順伝播(フォワード)
まずは、現時点の重み $w=1$として、順伝播を行ってみましょう。
$$
y = w \cdot x = 1 \cdot 2 = 2
$$
$$
L = \frac{1}{2}(y - t)^2 = \frac{1}{2}(2 - 8)^2 = \frac{1}{2} \cdot 36 = 18
$$
逆伝播(バックプロパゲーション)
次に、「この誤差を小さくするには、wをどう変えればいいか?」を考えます。
それには、損失関数 Lを重み wで微分すればOKです。
連鎖律を使って計算します:
$$
\frac{dL}{dw} = \frac{dL}{dy} \cdot \frac{dy}{dw}
$$
各項を計算:
$$
\frac{dL}{dy} = \frac{d}{dy} \left( \frac{1}{2}(y - t)^2 \right) = (y - t) = (2 - 8) = -6
$$
$$
\frac{dy}{dw} = \frac{d}{dw}(wx) = x = 2
$$
よって、
$$
\frac{dL}{dw} = -6 \cdot 2 = -12
$$
これにより、ωをどう変えればLが小さくなるのかがわかりました
なぜ連鎖律は成り立つのか?
連鎖律の成り立ちは、微分の定義と合成関数の性質に基づいています。関数 $y = f(g(x))$の微分を考えると、微小な変化 dx に対する y の変化 dy は、まず x の変化が $u = g(x)$ に影響を与え、その結果として $y=f(u)$ に影響を及ぼすという二段階の変化として捉えられます。
このように、x の変化が uを通じて yに伝わるため、全体の変化率(微分)は各段階の変化率の積となり、連鎖律が成り立つのです。
数式的にも理解してみます。
関数 y=f(g(x))y = f(g(x))y=f(g(x)) の導関数を、微分の定義に基づいて導出します。
$\frac{dy}{dx} = \lim_{h \to 0} \frac{f(g(x + h)) - f(g(x))}{h}$
ここで、分子を $g(x+h)−g(x)$ で割り、同じもので掛けることで、次のように変形できます:
$= \lim_{h \to 0} \left[ \frac{f(g(x + h)) - f(g(x))}{g(x + h) - g(x)} \cdot \frac{g(x + h) - g(x)}{h} \right]$
この式の各部分は、それぞれ $f$の $g(x)$ における導関数と、$g$ の $x$における導関数に対応します。したがって、連鎖律が成り立つことが示されます。
パラメータの更新
学習率ωはパラメータをLが小さくなる方向にどれだけ移動させるかを調整します
学習率 $\eta = 0.1$ とすると、重みの更新式は:
$$
w_{\text{new}} = w - \eta \cdot \frac{dL}{dw} = 1 - 0.1 \cdot (-12) = 1 + 1.2 = 2.2
$$
重みを 2.2 に更新することで、次の順伝播ではより正解に近づくことになります。
3. 2層ニューラルネットワークで理解を深める
前章では、1つの重みを持つ単純なネットワークで逆誤差伝播法の概念を確認しました。ここでは、より現実に使われているモデルに近い、2層のニューラルネットワークを用いて、逆誤差伝播法を適用する流れを見ていきましょう。
ディープラーニングにおいては、層の数は「学習するパラメータ(重み・バイアス)を持つ層」のことを指します。つまり入力層は 数えずに、隠れ層と出力層を 数えます。
モデル構造
以下のような構造を持ったモデルを考えます。
-
入力: $x \in \mathbb{R}^2$
- 入力: $x = [1.0, 0.5]^T$
- 初期パラメータ:
- $W^{(1)} = [0.1, 0.2], b^{(1)} = 0.1$
- $W^{(2)} = [0.3], b^{(2)} = 0.1$
- 隠れ層(1ユニット): 活性化関数 = シグモイド
- 出力層(1ユニット): 活性化関数 = 恒等関数(線形)
-
損失関数: 二乗誤差 $L = \frac{1}{2}(y - t)^2$
- 学習率: $\eta = 0.1$
- 正解ラベル: $t=0.8$
順伝播(フォワード)
隠れ層の計算:
$$
z^{(1)} = W^{(1)} \cdot x + b^{(1)} = 0.1 \cdot 1.0 + 0.2 \cdot 0.5 + 0.1 = 0.1 + 0.1 + 0.1 = 0.3
$$
$$
h = \sigma(z^{(1)}) = \frac{1}{1 + e^{-0.3}} \approx 0.5744
$$
出力層の計算:
$$
z^{(2)} = W^{(2)} \cdot h + b^{(2)} = 0.3 \cdot 0.5744 + 0.1 \approx 0.2723
$$
$$
y = z^{(2)} \quad
$$
損失の計算:
$$
L = \frac{1}{2}(y - t)^2 = \frac{1}{2}(0.2723 - 0.8)^2 \approx 0.1386
$$
逆伝播(バックプロパゲーション)
パラメータを更新する前に、下準備としてまず各層における誤差を求めます。
出力層の誤差
隠れ層の誤差 $\delta^{(2)}$ は以下のように定義されます:
微分$\frac{dL}{dy}$「ネットワークが出力y をほんのわずか動かしたとき、損失L がどれだけ変化するか」を数値化した勾配(感度)です。言い換えると、モデルが学習で「どの方向に、どれくらい強く」予測を補正すべきかを示す誤差の初動です。
$$
\delta^{(2)} = \frac{dL}{dy} = y - t = 0.2723 - 0.8 = -0.5277
$$
隠れ層の誤差
隠れ層の誤差 $\delta^{(1)}$ は以下のように定義されます:
微分$\frac{dL}{dz^{(1)}}$「ネットワークが$z^{(1)}$をほんのわずか動かしたとき、損失L がどれだけ変化するか」を数値化した勾配(感度)を示します。
$$
\delta^{(1)} = \frac{\partial L}{\partial z^{(1)}}
$$
式を展開していくと以下のように表すことができます。
$$
\delta^{(1)} = \delta^{(2)} \cdot W^{(2)} \cdot \sigma'(z^{(1)})
$$
- $\delta^{(1)}$:隠れ層の誤差(= 損失関数の出力に対する影響をこの層の出力がどれだけ持つか)
- $\delta^{(2)}$:出力層の誤差(損失関数の勾配)
- $W^{(2)}$:隠れ層 → 出力層の重み
- $\sigma'(z^{(1)})$:隠れ層の活性化関数の導関数(非線形変換の勾配)
式の展開について詳しくみてみます。
出力層の損失 L は、隠れ層の出力 $h$を通じて関係しています。
$$
L = L(y) = L(W^{(2)} \cdot h + b^{(2)})
$$
ここで、連鎖律を使って展開します
$$
\frac{\partial L}{\partial z^{(1)}} = \frac{\partial L}{\partial h} \cdot \frac{\partial h}{\partial z^{(1)}}
$$
- $\frac{\partial L}{\partial h} = (W^{(2)})^T \cdot \delta^{(2)}$:出力層から見た隠れ層出力の影響
- $\frac{\partial h}{\partial z^{(1)}} = \sigma'(z^{(1)})$:活性化関数の微分
よって
$$
\delta^{(1)} = (W^{(2)})^T \cdot \delta^{(2)} \odot \sigma'(z^{(1)})
$$
※スカラーの場合、転置は不要なので簡略化して
$$
\delta^{(1)} = \delta^{(2)} \cdot W^{(2)} \cdot \sigma'(z^{(1)})
$$
具体的に数字を当てはめ計算すると
$$
\sigma'(z^{(1)}) = h(1 - h) = 0.5744 \cdot (1 - 0.5744) \approx 0.2445
$$
$$
\delta^{(1)} = -0.5277 \cdot 0.3 \cdot 0.2445 \approx -0.0387
$$
シグモイド関数の微分以下のように表されます
$$
\frac{d}{dx} \sigma(x) = \sigma(x) \cdot (1 - \sigma(x))
$$
パラメータの更新
出力層の重み
勾配降下法に基づき、Lが小さくなるようなパラメータ更新は以下のように表すことができます
$$
W^{(2)} \leftarrow W^{(2)} - \eta \cdot \frac{\partial L}{\partial W^{(2)}}
$$
$$
\frac{\partial L}{\partial W^{(2)}} = \frac{\partial L}{\partial y}\cdot \frac{\partial y}{\partial W^{(2)} }= \delta^{(2)} \cdot h
$$
上式を代入することで
$$
W^{(2)} \leftarrow W^{(2)} - \eta \cdot \delta^{(2)} \cdot h
$$
それぞれ値を代入することで更新された重みの値を求めることができます
$$
W^{(2)} - \eta \cdot \delta^{(2)} \cdot h = 0.3 - 0.1 \cdot (-0.5277) \cdot 0.5744 \approx 0.3303
$$
出力層のバイアス
同様にして、Lが小さくなるようなパラメータ更新は以下のように表すことができます
$$
b^{(2)} \leftarrow b^{(2)} - \eta \cdot \frac{\partial L}{\partial b^{(2)}}
$$
$\frac{\partial L}{\partial b^{(2)}}$を計算すると以下のようになります
$$
\frac{\partial L}{\partial b^{(2)}}= \frac{\partial L}{\partial y}\cdot \frac{\partial y}{\partial b^{(2)} }= \delta^{(2)} \cdot 1
$$
値を代入することで更新されたバイアスの値を求めることができます
$$
b^{(2)} - \eta \cdot \delta^{(2)} = 0.1 + 0.0528 = 0.1528
$$
隠れ層の重み
Lが小さくなるようなパラメータ更新は以下のように表すことができます
$$
W^{(1)} \leftarrow W^{(1)} - \eta \cdot \frac{\partial L}{\partial W^{(1)}}
$$
$\frac{\partial L}{\partial W^{(2)}}$を計算すると以下のようになります
$$
\frac{\partial L}{\partial W^{(1)}} = \frac{\partial L}{\partial z^{(1)}}\cdot \frac{\partial z^{(1)}}{\partial W^{(1)} }= \delta^{(1)} \cdot x
$$
それぞれ値を代入することで更新された重みの値を求めることができます
$$
W^{(1)}_1 := 0.1 - 0.1 \cdot (-0.0387) \cdot 1.0 \approx 0.1039
$$
$$
W^{(1)}_2 := 0.2 - 0.1 \cdot (-0.0387) \cdot 0.5 \approx 0.2019
$$
隠れ層のバイアス
Lが小さくなるようなパラメータ更新は以下のように表すことができます
$$
b^{(1)} \leftarrow b^{(1)} - \eta \cdot \frac{\partial L}{\partial b^{(1)}}
$$
$\frac{\partial L}{\partial b^{(1)}}$を計算すると以下のようになります
$$
\frac{\partial L}{\partial b^{(1)}}= \frac{\partial L}{\partial z^{(1)}}\cdot \frac{\partial z^{(1)}}{\partial b^{(1)} }= \delta^{(1)} \cdot 1
$$
それぞれ値を代入することで更新された重みの値を求めることができます
$$
b^{(1)} := b^{(1)} - 0.1 \cdot (-0.0387) \approx 0.1039
$$
まとめ
本記事では、シンプルな1層のニューラルネットワークの例から始め、2層に拡張したケースまでを通じて、誤差(損失関数の値)に基づいたパラメータ更新の流れを確認しました。
出力に近い層から順に、誤差を逆伝播させながら各層の重みやバイアスをどのように更新すればよいか、その仕組みを具体的に理解することができました。
今後はこの理解をさらに発展させ、より一般化された多層ネットワークへの応用についても取り上げていく予定です。