[mathjax]
順伝搬(forward)はNumpyの積和計算を使って超絶簡単に記述できる。
行列の積和計算
何はともあれNumpyで行列の積を計算する方法について。
この仕組みがあるから、forなどの制御構造を使わずに行列の積を実現できる。
begin{eqnarray}
A &=& begin{pmatrix}
a_{11} & a_{21} \\
a_{12} & a_{22}
end{pmatrix} \\
B &=& begin{pmatrix}
b_{11} & b_{21} \\
b_{12} & b_{22}
end{pmatrix} \\
A cdot B &=& begin{pmatrix}
a_{11} b_{11} + a_{21} b_{12} && a_{11} b_{21} + a_{21} b_{22} \\
a_{12} b_{11} + a_{22} b_{12} && a_{12} b_{21} + a_{22} b_{22}
end{pmatrix}
end{eqnarray}
積はNumpyのdot関数で一発。
左行列と右行列の\"対応する行\"は一致している必要がある。
つまり(N_1 times M_1 cdot N_2 times M_2)という積計算において(M_1 = N_2)である必要がある。
a11,a21,a12,a22 = (1,2,3,4)
b11,b21,b12,b22 = (1,2,3,4)
a = np.array([[a11,a21],[a12,a22]])
b = np.array([[b11,b21],[b12,b22]])
np.dot(a,b)
# array([[ 7, 10],
# [15, 22]])
和はNumpyの+オペレータで一発。
begin{eqnarray}
A &=& begin{pmatrix}
a_{11} & a_{21} \\
a_{12} & a_{22}
end{pmatrix}
C &=& begin{pmatrix}
c_{1} \\
c_{2}
end{pmatrix}
end{eqnarray}
begin{eqnarray}
A+C &=& begin{pmatrix}
a_{11} + c_1 & a_{21} + c_1 \\
a_{12} + c_1 & a_{22} + c_2
end{pmatrix}
end{eqnarray}
c1, c2 = (5,5)
c = np.array([[c1],[c2]])
a + c
# array([[6, 7],
# [8, 9]])
各層における信号伝達の実装
Numpyで積和が簡単にかける。
すなわち、各層における伝達が積和で表されるとすると、
入力層から各層における信号伝達の実装を簡単にかける。
左側の層が(X=begin{pmatrix}x_1 x_2end{pmatrix})、重みが(W=begin{pmatrix}w_{11} & w_{21} \\ w_{12} & w_{22} end{pmatrix})、
右側の層が(A=begin{pmatrix} a_{11} & a_{21} end{pmatrix})とする。
すると、右層と左層の関係は以下の通り。
begin{eqnarray}
A &=& XW \\
begin{pmatrix} a_{11} & a_{21} end{pmatrix} &=& begin{pmatrix}x_1 x_2end{pmatrix} begin{pmatrix}w_{11} & w_{21} \\ w_{12} & w_{22} end{pmatrix} \\
&=& begin{pmatrix} w_{11}x_1 + w_{21} x_2 \\ w_{12}x_1 + w_{22}x_2end{pmatrix}
end{eqnarray}
左層にバイアス(B=begin{pmatrix}b_1 \\ b_1 end{pmatrix})があった場合、線形和で表現できる。
行と列がどちらの方向に対応するかに注意。
1つの層に(N)個のノードが存在することを(Ntimes 1)の行列で表現する。
begin{eqnarray}
A &=& XW + B \\
begin{pmatrix} a_{11} & a_{21} end{pmatrix} &=& begin{pmatrix}x_1 x_2end{pmatrix} begin{pmatrix}w_{11} & w_{21} \\ w_{12} & w_{22} end{pmatrix} + begin{pmatrix}b_1 \\b_1 end{pmatrix} \\
&=& begin{pmatrix} w_{11}x_1 + w_{21} x_2 + b_1 \\ w_{12}x_1 + w_{22}x_2 + b_1end{pmatrix}
end{eqnarray}
x1,x2 = (0.4,0.9)
w11,w21,w12,w22 = (0.8,0.5,0.1,0.3)
b1 = 0.1
x = np.array([x1,x2])
w = np.array([[w11,w21],[w12,w22]])
b = np.array([b1,b1])
# これで一発
a = np.dot(x,w) + b
# array([0.51, 0.57])
# 活性化関数としてsigmoid関数をかます
z = sigmoid(a)
# array([0.62480647, 0.63876318])
順伝播
入力層から出力層まで上記のような計算を繰り返していく。これを\"順伝播\"とか言う。
順伝\"搬\"ではなく、順伝\"播\"。ネットワークが多層になったとしても全く同様。
以下みたいにNumpyの積和計算を繰り返すだけで出来る。
def init_network():
network = {}
network[\'W1\'] = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
network[\'b1\'] = np.array([0.1,0.2,0.3])
network[\'W2\'] = np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
network[\'b2\'] = np.array([0.1,0.2])
network[\'W3\'] = np.array([[0.1,0.3],[0.2,0.4]])
network[\'b3\'] = np.array([0.1,0.2])
return network
def forward(network, x):
W1, W2, W3 = network[\'W1\'], network[\'W2\'], network[\'W3\']
b1,b2,b3 = network[\'b1\'], network[\'b2\'], network[\'b3\']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = a3
return y
network = init_network()
x = np.array([1.0,0.5])
y = forward(network,x)
# array([0.31682708, 0.69627909])