順伝搬(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_2\end{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_2\end{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_2\end{pmatrix}
\end{eqnarray}
左層にバイアス\(B=\begin{pmatrix}b_1 \\ b_1 \end{pmatrix}\)があった場合、線形和で表現できる。
行と列がどちらの方向に対応するかに注意。
1つの層に\(N\)個のノードが存在することを\(N\times 1\)の行列で表現する。
\begin{eqnarray}
A &=& XW + B \\
\begin{pmatrix} a_{11} & a_{21} \end{pmatrix} &=& \begin{pmatrix}x_1 x_2\end{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_1\end{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])