順伝搬(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\)である必要がある。
1 2 3 4 5 6 7 8 |
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}
1 2 3 4 5 6 |
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}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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の積和計算を繰り返すだけで出来る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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]) |