行列の積和と順伝播


順伝搬(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])