次は Back-Propagation です。同じくGPTさんに活躍してもらいました。計算もずいぶん正解するようになりました。
誤差逆伝播法は損失関数 \(E\) の出力から各層のパラメータ(重みおよびバイアス)への偏微分(勾配)を効率よく求めるためのアルゴリズムで、DNNの基礎となっています。順伝播によりネットワークの出力が計算された後、損失 \(E\)に連鎖律(チェーンルール)を適用しながら逆方向に伝播させて各パラメータの更新に必要な勾配を求め、出力誤差を減少させる方向にパラメータを修正しながら学習を進めます。
自動微分はフレームワークに実装されている勾配を求めるためのアルゴリズム。手計算では煩雑になりますが、フレームワークを利用することでシームレスかつ正確に扱えるようになります。
1. ネットワークの構成と順伝播
まず、シンプルな2層(隠れ層と出力層)の全結合ニューラルネットワークを考える。
- 隠れ層: 入力 \( \mathbf{x} \) に対し、 \[ \mathbf{z}^{(1)} = \mathbf{W}^{(1)}\, \mathbf{x} + \mathbf{b}^{(1)}, \quad \mathbf{a}^{(1)} = f\bigl(\mathbf{z}^{(1)}\bigr) \] ここで、\( f \) は隠れ層の活性化関数(例:シグモイドやReLU)。
- 出力層: 隠れ層の出力 \( \mathbf{a}^{(1)} \) を受け、 \[ \mathbf{z}^{(2)} = \mathbf{W}^{(2)}\, \mathbf{a}^{(1)} + \mathbf{b}^{(2)}, \quad \mathbf{a}^{(2)} = g\bigl(\mathbf{z}^{(2)}\bigr) \] ここで、\( g \) は出力層の活性化関数(例:シグモイド)。
損失関数 \( E \)(例:平均二乗誤差やクロスエントロピー誤差)によって、予測値 \( \mathbf{a}^{(2)} \) と正解 \( \mathbf{y} \) との誤差が評価される。
2. 誤差逆伝播法の基本概念
誤差逆伝播法では、連鎖律(チェーンルール)を用いて、損失関数 \( E \) の内部変数に対する偏微分を段階的に求める。
すなわち、任意の中間変数 \( a \) と \( z \) について、以下の通り表せる。
\[
\frac{\partial E}{\partial z} = \frac{\partial E}{\partial a} \cdot \frac{\partial a}{\partial z}
\]
局所誤差を表すデルタ \(\delta = \frac{\partial E}{\partial z}\) は、各層における勾配計算の基礎となる重要な指標である。これは、出力 \(a\) の微小な変化 \(\frac{\partial a}{\partial z}\)が損失 \(E\) にどのような影響を与えるか\(\frac{\partial E}{\partial a}\) を明らかにしており、パラメータの更新方向および大きさを決定するのに欠かせない情報となっている。この考え方により、各層ごとのパラメータ更新に必要な勾配を効率的に計算できる点が、誤差逆伝播法の大きな利点である。
3. 出力層の誤差項δ
出力層では、出力 \( \mathbf{a}^{(2)} = g(\mathbf{z}^{(2)}) \) と正解との差が損失関数 \( E \) で評価される。
たとえば、シグモイド関数の場合は、
\[ g(z)=\frac{1}{1+e^{-z}}, \quad g'(z)=g(z)(1-g(z)) \]
連鎖律を用いると、出力層でのデルタは次のように定義される:
\[ \delta^{(2)} = \frac{\partial E}{\partial \mathbf{z}^{(2)}} = \frac{\partial E}{\partial \mathbf{a}^{(2)}} \odot g'\bigl(\mathbf{z}^{(2)}\bigr) \]
ここで\(\odot\)はアダマール積を意味しする。
この形式は、誤差の各成分が出力 \(a\) に対する感度と、活性化関数 \(g\) の変化率 \(g'(z)\) とで表現されており、局所的な誤差伝播に必要な情報となる。
4. 出力層の重み \(\mathbf{W}^{(2)}\) に関する偏微分
出力層においては、線形結合の定義より \[ \mathbf{z}^{(2)} = \mathbf{W}^{(2)}\, \mathbf{a}^{(1)} + \mathbf{b}^{(2)} \] となる。各出力ユニット \(i\) の入力は、
\[ z^{(2)}_i = \sum_j W^{(2)}_{ij}\, a^{(1)}_j + b^{(2)}_i \]
連鎖律により、各重み \(W^{(2)}_{ij}\) に対する偏微分は、
\[ \frac{\partial E}{\partial W^{(2)}_{ij}} = \frac{\partial E}{\partial z^{(2)}_i} \cdot \frac{\partial z^{(2)}_i}{\partial W^{(2)}_{ij}} = \delta^{(2)}_i \, a^{(1)}_j \]
すなわち、行列表現では
\[ \frac{\partial E}{\partial \mathbf{W}^{(2)}} = \delta^{(2)} \Bigl(\mathbf{a}^{(1)}\Bigr)^\top \]
5. 隠れ層への逆伝播の導出
出力層で計算されたデルタ \(\delta^{(2)}\) は、隠れ層に伝播される。ここでは、隠れ層の各ユニット \(j\) におけるデルタ \(\delta^{(1)}_j\)とする。
まず、出力層の線形結合は次のように定義される: \[ z^{(2)}_i = \sum_j W^{(2)}_{ij}\, a^{(1)}_j + b^{(2)}_i \] そのため、隠れ層ユニット \(j\) の出力 \(a^{(1)}_j\) が出力層の \(z^{(2)}_i\) に与える影響は \[ \frac{\partial z^{(2)}_i}{\partial a^{(1)}_j} = W^{(2)}_{ij}\quad \text{となる。} \]
さらに、隠れ層では \( \mathbf{a}^{(1)} = f(\mathbf{z}^{(1)}) \) としており、活性化関数 \( f \) の微分は \( f'(z^{(1)}_j) \) となる。
連鎖律を適用すると、隠れ層ユニット \(j\) における損失の微分は以下のように表される:
\[ \frac{\partial E}{\partial z^{(1)}_j} = \sum_i \frac{\partial E}{\partial z^{(2)}_i}\,\frac{\partial z^{(2)}_i}{\partial a^{(1)}_j}\,\frac{\partial a^{(1)}_j}{\partial z^{(1)}_j} = \Bigl(\sum_i W^{(2)}_{ij}\,\delta^{(2)}_i\Bigr) \cdot f'\bigl(z^{(1)}_j\bigr) \]
ここで、隠れ層のデルタ \( \delta^{(1)}_j \) を定義すると、
\[ \delta^{(1)}_j = \frac{\partial E}{\partial z^{(1)}_j} = \Biggl(\sum_i W^{(2)}_{ij}\,\delta^{(2)}_i\Biggr) \cdot f'\bigl(z^{(1)}_j\bigr) \]
行列表現では、全隠れ層のデルタは次のようになる:
\[ \delta^{(1)} = \Bigl(\mathbf{W}^{(2)}\Bigr)^\top\,\delta^{(2)} \odot f'\bigl(\mathbf{z}^{(1)}\bigr) \]
この導出から、デルタは「損失 \(E\) の出力 \(a\) に対する偏微分」と「活性化関数の微分 \( \frac{\partial a}{\partial z} \)」の積として定義できるため、各層の誤差伝播に必須の情報となる。
さらに、このデルタ情報を利用して、各パラメータの更新(モデルの改善)が効率的に行われる。
6. 計算例とモデル改善の流れ
以下に、1入力・1隠れ・1出力の場合の計算例を示す。ここではシグモイド関数を活性化関数とし、損失関数を平均二乗誤差 \[ E=\frac{1}{2}\left(a^{(2)}-y\right)^2 \] とする。
[ステップ1] 順伝播
入力: \(x=1\)
重み・バイアス:
入力~隠れ層: $$ W^{(1)}=0.5,\; b^{(1)}=0 $$
隠れ層~出力層: $$ W^{(2)}=0.8,\; b^{(2)}=0 $$
隠れ層:
線形結合:
$$ z^{(1)} = 0.5 \times 1 + 0 = 0.5 $$
シグモイド関数:
$$ a^{(1)} = \sigma(0.5) \approx 0.6225 $$
出力層:
線形結合:
$$ z^{(2)} = 0.8 \times 0.6225 + 0 \approx 0.498 $$
出力(シグモイド関数):
$$ a^{(2)} = \sigma(0.498) \approx 0.622 $$
真の正解 \(y=1\) として、損失は
$$ E = 0.5 × (0.622 – 1)^2 ≈ 0.0714 $$
[ステップ2] 出力層での逆伝播
損失の出力 \(a^{(2)}\) に対する偏微分:
$$ \frac{\partial E}{\partial a^{(2)}} = a^{(2)} - y \approx 0.622 - 1 = -0.378 $$
シグモイドの微分:
$$ g'(z^{(2)}) = a^{(2)}(1-a^{(2)}) \approx 0.622 \times 0.378 \approx 0.235 $$
よって、出力層のデルタ:
$$ \delta^{(2)} = (-0.378) \times 0.235 \approx -0.0888 $$
これを用いて、出力層の重み勾配は:
$$ \frac{\partial E}{\partial W^{(2)}} = \delta^{(2)} \times a^{(1)} \approx -0.0888 \times 0.6225 \approx -0.0553 $$
[ステップ3] 隠れ層での逆伝播とモデル改善
次に、出力層から隠れ層へデルタを伝播させる。
$$ \delta^{(1)} = (W^{(2)})^T \times \delta^{(2)} \odot f'(z^{(1)}) $$
数値例では、\(W^{(2)}=0.8\) のため、
$$ (W^{(2)})^T \times \delta^{(2)} = 0.8 \times (-0.0888) \approx -0.07104 $$
隠れ層のシグモイド微分:
$$ f'(z^{(1)}) = a^{(1)}(1-a^{(1)}) \approx 0.6225 \times 0.3775 \approx 0.235 $$
よって、隠れ層のデルタ:
$$ \delta^{(1)} \approx -0.07104 \times 0.235 \approx -0.01669 $$
隠れ層の重み勾配(入力 \(x=1\) を用いる):
$$ \frac{\partial E}{\partial W^{(1)}} = \delta^{(1)} \times x \approx -0.01669 \times 1 = -0.01669 $$
ここで得られた各層の勾配(出力層: -0.0553、隠れ層: -0.01669)から、パラメータ更新(例えば、確率的勾配降下法)を行う。
例えば学習率 \( \eta \) を 0.1 とすると、重みの更新は以下のようになる:
$$ W^{(2)}_{\text{new}} = W^{(2)} - \eta \times \frac{\partial E}{\partial W^{(2)}}, \quad W^{(1)}_{\text{new}} = W^{(1)} - \eta \times \frac{\partial E}{\partial W^{(1)}} $$
$$ \begin{aligned} W^{(2)}_{\text{new}} &= 0.8 - 0.1 \times (-0.0553) \approx 0.80553,\\[8pt] W^{(1)}_{\text{new}} &= 0.5 - 0.1 \times (-0.01669) \approx 0.50167 \end{aligned} $$
この重みWの更新により、モデルは出力誤差を減少させる方向へ学習が進む。
すなわち、デルタが求めた各層での「局所的な誤差指標」を基にして、ネットワーク全体の性能が改善されることになる。
7. 自動微分
自動微分(Automatic Differentiation, AD)は、ニューラルネットワークやその他の複雑な計算グラフにおいて、各中間変数の勾配(微分値)を自動的に計算する技術である。これにより、手動での連鎖律(チェーンルール)の適用から解放され、正確かつ効率的な勾配計算が実現される。
自動微分の利点
- 正確性: 数値微分の近似誤差や記号微分の複雑な手作業を避け、連鎖律を忠実に適用して正確な勾配計算を行う。
- 効率性: 膨大なパラメータや複雑な計算グラフを持つモデルでも高速に勾配を計算できるため、大規模なニューラルネットワークの学習に最適。
自動微分の実装例:PyTorch
現代のディープラーニングライブラリ(例:PyTorch、TensorFlowなど)は自動微分機能を内蔵している。以下に、PyTorchを使った簡単な全結合ネットワークの例を示す。
import torch # 計算グラフの構築 W1 = torch.tensor([[0.5]], requires_grad=True) b1 = torch.tensor([0.0], requires_grad=True) W2 = torch.tensor([[0.8]], requires_grad=True) b2 = torch.tensor([0.0], requires_grad=True) # 順伝播 x = torch.tensor([1.0]) z1 = W1 @ x + b1 # 隠れ層の線形結合 a1 = torch.sigmoid(z1) # 活性化関数(シグモイド) z2 = W2 @ a1 + b2 # 出力層の線形結合 a2 = torch.sigmoid(z2) # 最終出力 # 逆伝播 y = torch.tensor([1.0]) loss = 0.5 * (a2 - y) ** 2 loss.backward() # 逆伝播により、自動的に勾配計算が実行される
参考文献
- Rumelhart, Hinton, and Williams, "Learning representations by back-propagating errors" (1986)
- Goodfellow, Bengio, and Courville, "Deep Learning" (MIT Press, 2016)
0 件のコメント:
コメントを投稿