コードで確認するニューラルネットワーク・アルゴリズム


はじめに

本記事は、下記コース受講の一環として、(併せて提供される「超AI入門講座」に含まれる動画および資料を)学習した内容について、理解を深めるため、かつ備忘として整理したものです。

コード自体は本記事単体のみにて完結したものとなるよう整理してありますが(ただし入力データは構造のみを記述)、事前の知見のない方へ向けての解説・説明とはなっていません。知識確認の参考としていただくか、必要に応じ上記コース等ほかの情報と併せてご活用ください。

前提

学習するデータとネットワークとの関係

  • 1レイヤー2次元配列(白黒画像)
  • 縦横28(px)のデータを、784個の入力値として扱う
  • 誤差の算出には二乗誤差を用いる
  • 学習係数 $\eta$ は用いない

利用ライブラリ

import numpy as np

ネットワーク定義

シグモイド関数

def sigmoid(a):
    return 1 / (1 + np.exp(-a))

重みWの定義

w1 = np.random.normal(0.0, 1.0, (10, 784))
w2 = np.random.normal(0.0, 1.0, (2, 10))
W = [w1, w2]

バイアスBの定義

b1 = np.ones((10, 1))
b2 = np.ones((2, 1))
B = [b1, b2]

ネットワーク更新のための関数定義

パラメータ更新式公式

wの更新式

w = w - \frac{\partial E}{\partial w} = w - \frac{\partial E}{\partial y} \frac{\partial y}{\partial a} \frac{\partial a}{\partial w}

誤差関数Eのbによる偏微分の公式

\frac{\partial E}{\partial b_j^l} = g_j^l

誤差関数Eのwによる偏微分の公式

\frac{\partial E}{\partial w_{kj}^l} = g_i^{l} x_k^{l}

更新式の公式g

最終レイヤー

g_j^l = (y_j - t_j)f'(a_j^l) 

最終レイヤー以外

g_j^l = \sum_{i=1}^{m} (g_i^{l+1} w_ji^{l+1})f'(a_j^l) 

活性化関数fの微分

def f(a):
    return (1 - sigmoid(a)) * sigmoid(a)

更新式の公式gの実装

def g(l, j):
    if max_layer == l:
        return (y[j] - t[j]) * f(A[l - 1][j])
    else:
        output = 0
        m = A[l].shape[0]
        for i in range(m):
            output += g(l + 1, i) * W[l][i, j] * f(A[l - 1][j])
        return output

誤差関数の微分

パラメーターwによる微分

def diff_w(j, k, l):
    return g(l, j) * X[l - 1][k]

パラメーターbによる微分

def diff_b(j, l):
    return g(l, j)

データ

入力

1レイヤー2次元配列(白黒画像)

inputs.npy`
[
 [
  [0.0 0.0 0.0 ... 0.0 ]
  ...
 ],
 ...
]

教師データ

2クラス分類

true_values.npy`
[
 [
  [0],[1]
 ],
 ...
]
xs = np.load('inputs.npy')[0:101]
ts = np.load('true_values.npy')[0:101]

実行

for x, t in zip(xs, ts):
    # 入力層
    x1 = x.flatten().reshape(784, 1)
    # 中間層入力
    a1 = W[0].dot(x1) + b1

    # 中間層出力
    x2 = sigmoid(a1)
    # 出力層入力
    a2 = W[1].dot(x2) + b2

    # 出力値
    y = sigmoid(a2)
    # 正否(本格的な実装としては結果保存・学習終了判定が行われる部分)
    if np.argmax(y) == np.argmax(t):
        print("正解")
    else:
        print("不正解")

    # パラメーター更新に用いる変数
    X = [x1, x2]
    A = [a1, a2]

    # ネットワークのレイヤーサイズ
    max_layer = len(X)

    # パラメーターw, bの更新
    for l in range(len(X)):
        for j in range(W[l].shape[0]):
            for k in range(W[l].shape[1]):
                W[l][j, k] = W[l][j, k] - diff_w(j, k, l + 1)
            B[l][j] = B[l][j] - diff_b(j, l + 1)

np.save("w1.npy", W[0])
np.save("w2.npy", W[1])
np.save("b1.npy", B[0])
np.save("b2.npy", B[1])