誤差逆伝播法(バックプロパゲーション)
誤差逆伝播法(Backpropagation)
誤差逆伝播法(Backpropagation)は,ニューラルネットワークの学習において,重みとバイアスを更新するための効率的な方法です.このアルゴリズムは,ネットワークの出力と期待される出力との間の「誤差」を計算し,その誤差を逆方向に伝播させて,各層のパラメータの勾配を求めます.
以下では,具体的な数式とPythonコードを用いて,誤差逆伝播法の仕組みを詳しく解説します.
誤差逆伝播法と数式
まずはシンプルな,「入力層」「隠れ層」「出力層」のニューラルネットワークを考えます.
- 入力層:
\mathbf{x} - 隠れ層:
\mathbf{h} = f(\mathbf{W}^{(1)}\mathbf{x} + \mathbf{b}^{(1)}) - 出力層:
\mathbf{\hat{y}} = g(\mathbf{W}^{(2)}\mathbf{h} + \mathbf{b}^{(2)})
ここで,
順伝播(フォーワードパス)
ニューラルネットのシンプルな順伝播を具体的に見ると以下のように分解できます.
- 入力層:
\mathbf{x} - 隠れ層への入力:
\mathbf{z}^{(1)} = \mathbf{W}^{(1)} \mathbf{x} + \mathbf{b}^{(1)} - 隠れ層の出力:
\mathbf{h} = f(\mathbf{z}^{(1)}) - 出力層への入力:
\mathbf{z}^{(2)} = \mathbf{W}^{(2)} \mathbf{h} + \mathbf{b}^{(2)} - 出力層の出力:
\hat{\mathbf{y}} = g(\mathbf{z}^{(2)})
損失関数(Loss Function)
「損失関数」そのものの具体的な説明はこの記事(https://zenn.dev/minoda_kohei/articles/9a92faa9c8955c) に任せて,今回は二乗誤差を採用します.
ここで,
逆伝播(バックワードパス)
損失関数
まず損失関数の各入力
第一層について,微分の連鎖律を用いて少し変形し,損失関数
第二層についても同様に計算します.
以上によって計算された各勾配と学習率
これによって予測値と正解ラベルの「誤差」から各パラメータを更新することができました.
Pythonによる実装
以下に,単純な二層ニューラルネットワークの誤差逆伝播法をPythonで実装した例を示します.
import numpy as np
# 活性化関数とその導関数
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def sigmoid_derivative(z):
return sigmoid(z) * (1 - sigmoid(z))
# パラメータの初期化
np.random.seed(0)
input_size = 2
hidden_size = 2
output_size = 1
W1 = np.random.randn(hidden_size, input_size)
b1 = np.zeros((hidden_size, 1))
W2 = np.random.randn(output_size, hidden_size)
b2 = np.zeros((output_size, 1))
# トレーニングデータ(例)
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]).T # 入力データ
Y = np.array([[0, 1, 1, 0]]) # XOR問題のラベル
# ハイパーパラメータ
learning_rate = 0.1
epochs = 10000
# トレーニングループ
for epoch in range(epochs):
# 順伝播
Z1 = np.dot(W1, X) + b1
A1 = sigmoid(Z1)
Z2 = np.dot(W2, A1) + b2
A2 = sigmoid(Z2)
# 損失の計算
loss = np.mean((A2 - Y) ** 2)
# 逆伝播
dZ2 = (A2 - Y) * sigmoid_derivative(Z2)
dW2 = np.dot(dZ2, A1.T) / X.shape[1]
db2 = np.sum(dZ2, axis=1, keepdims=True) / X.shape[1]
dA1 = np.dot(W2.T, dZ2)
dZ1 = dA1 * sigmoid_derivative(Z1)
dW1 = np.dot(dZ1, X.T) / X.shape[1]
db1 = np.sum(dZ1, axis=1, keepdims=True) / X.shape[1]
# パラメータの更新
W2 -= learning_rate * dW2
b2 -= learning_rate * db2
W1 -= learning_rate * dW1
b1 -= learning_rate * db1
# 進捗の表示
if epoch % 1000 == 0:
print(f"Epoch {epoch}, Loss: {loss}")
# テスト
Z1 = np.dot(W1, X) + b1
A1 = sigmoid(Z1)
Z2 = np.dot(W2, A1) + b2
A2 = sigmoid(Z2)
print("Predictions:")
print(A2)
実行結果
Epoch 0, Loss: 0.2772226743503602
Epoch 1000, Loss: 0.2394590439446847
Epoch 2000, Loss: 0.22243462354320356
Epoch 3000, Loss: 0.2049611513259233
Epoch 4000, Loss: 0.1889131359501558
Epoch 5000, Loss: 0.17595266619727007
Epoch 6000, Loss: 0.16604337947664854
Epoch 7000, Loss: 0.15856117551800525
Epoch 8000, Loss: 0.1528860272321115
Epoch 9000, Loss: 0.14853441549571722
Predictions:
[[0.13084326 0.5080402 0.82550843 0.53945658]]
果たして人間のニューロンはこのようなことをしているのだろうか,,,
LIFe Is LIKe A BOAt
Discussion