下記の実装の意味を考えることにしたが、わからなかったのでググってみたが、ネットには自分の必要な情報がなかったので自分で考えてみた。
import numpy as np
from common import functions
import matplotlib.pyplot as plt
def print_vec(text, vec):
print("*** " + text + " ***")
print(vec)
#print("shape: " + str(x.shape))
print("")
# ウェイトとバイアスを設定
# ネートワークを作成
def init_network():
print("##### ネットワークの初期化 #####")
network = {}
network['W1'] = np.array([
[0.1, 0.3, 0.5],
[0.2, 0.4, 0.6]
])
network['W2'] = np.array([
[0.1, 0.4],
[0.2, 0.5],
[0.3, 0.6]
])
network['b1'] = np.array([0.1, 0.2, 0.3])
network['b2'] = np.array([0.1, 0.2])
print_vec("重み1", network['W1'])
print_vec("重み2", network['W2'])
print_vec("バイアス1", network['b1'])
print_vec("バイアス2", network['b2'])
return network
# 順伝播
def forward(network, x):
print("##### 順伝播開始 #####")
W1, W2 = network['W1'], network['W2']
b1, b2 = network['b1'], network['b2']
u1 = np.dot(x, W1) + b1
z1 = functions.relu(u1)
u2 = np.dot(z1, W2) + b2
y = functions.softmax(u2)
print_vec("総入力1", u1)
print_vec("中間層出力1", z1)
print_vec("総入力2", u2)
print_vec("出力1", y)
print("出力合計: " + str(np.sum(y)))
return y, z1
# 誤差逆伝播
def backward(x, d, z1, y):
print("\n##### 誤差逆伝播開始 #####")
grad = {}
W1, W2 = network['W1'], network['W2']
b1, b2 = network['b1'], network['b2']
# 出力層でのデルタ
delta2 = functions.d_sigmoid_with_loss(d, y)
# b2の勾配
grad['b2'] = np.sum(delta2, axis=0)
# W2の勾配
grad['W2'] = np.dot(z1.T, delta2)
# 中間層でのデルタ
delta1 = np.dot(delta2, W2.T) * functions.d_relu(z1)
# b1の勾配
grad['b1'] = np.sum(delta1, axis=0)
# W1の勾配
grad['W1'] = np.dot(x.T, delta1)
print_vec("偏微分_dE/du2", delta2)
print_vec("偏微分_dE/du2", delta1)
print_vec("偏微分_重み1", grad["W1"])
print_vec("偏微分_重み2", grad["W2"])
print_vec("偏微分_バイアス1", grad["b1"])
print_vec("偏微分_バイアス2", grad["b2"])
return grad
# 訓練データ
x = np.array([[1.0, 5.0]])
# 目標出力
d = np.array([[0, 1]])
# 学習率
learning_rate = 0.01
network = init_network()
y, z1 = forward(network, x)
# 誤差
loss = functions.cross_entropy_error(d, y)
grad = backward(x, d, z1, y)
for key in ('W1', 'W2', 'b1', 'b2'):
network[key] -= learning_rate * grad[key]
print("##### 結果表示 #####")
print("##### 更新後パラメータ #####")
print_vec("重み1", network['W1'])
print_vec("重み2", network['W2'])
print_vec("バイアス1", network['b1'])
print_vec("バイアス2", network['b2'])
下記の式のような解釈が必要だが、ポイントは意外と簡単である。下記の式は、z1,u1,u2,yの式から導ける。例えば、z1をb1で微分するときは、合成関数の微分の公式を使う
grad['W1'] = np.dot($x.T$, delta1) = ∂E/∂w1
grad['W2'] = np.dot($z_{1}.T$, delta2) = ∂E/∂w2
delta 1 = $∂E$/$∂{z_{1}}$
delta 2 = $∂E$/$∂y$
grad['b1'] = $∂E$/$∂{b_{1}}$
grad['b2'] = $∂E$/$∂{b_{2}}$
ポイントになる式:
$∂E/∂{Z_{1}}$ = $∂E/∂yw_{2}^{T}$
$∂E/∂w_{2}$ = ${z_{1}}^{T}∂E/∂y$
$∂E/∂w_{1}$ = ${X}^{T}∂E/∂z_{1}$
$∂E/∂{b_{1}}$ = $∂E$/$∂{z_{1}} ∂{z_{1}}/∂{b_{1}}$ = delta 1
$∂E/∂{b_{2}}$ = $∂E$/$∂y ∂y/∂{b_{2}}$ = delta 2
z1 = functions.relu(u1)
u1 = np.dot(x, W1) + b1
u2 = np.dot(z1, W2) + b2
y = u2