ゼロから作る Deep Learning 勉強メモ #5

  • 3
    Like
  • 0
    Comment

はじめに

https://www.oreilly.co.jp/books/9784873117584/
https://www.amazon.co.jp/dp/4873117585
この記事は #4 の続きです。
勉強メモ#1
勉強メモ#2
勉強メモ#3
勉強メモ#4
勉強メモ#6
勉強メモ#7

3層ニューラルネットワークを実装する

行列の内積をつなげて、3層のニューラルネットワークを実装します。

入力層と出力層

入力層は[x1, x2]、出力層は[y1, y2]です。
ソースコードに書き下すと、こんな感じでしょうか。

X = np.array([x1, x2])
Y = np.array([y1, y2])

頭の中を整理するために、一度図を描いてみます。

image.png

0層→1層の信号伝達

0層(入力層)から、1層への信号伝達をソースコードに書き下すと、こんな感じになりました。

W = [[w11, w21, w31], [w12, w22, w32]]
B = [b1, b2, b3]
A = np.dot(X, W) + B

ここでまたこんがらがったので、図を描きました。

image.png

ここで、A を活性化関数に入力します。
活性化関数には、 ReLU 関数を使ってみることにします。
python は関数の引数に関数を指定できます。
活性化関数が交換可能なソースコード(関数の名前をハードコードしない)も、すぐに書けそうです。

Z = ReLU(A)

これで、0層→1層の信号伝達が書けました。
ここまでをまとめて、1つのプログラムにしてみます。

import numpy as np

def ReLU(x):
    return np.maximum(0, x)

if __name__ == '__main__':
    x1, x2 = 0.1, 0.2
    w11, w21, w31, w12, w22, w32 = 0.1, 0.2, 0.3, 0.4, 0.5, 0.6
    b1, b2, b3 = 0.1, 0.2, 0.3

    X = np.array([x1, x2])
    W = np.array([[w11, w21, w31], [w12, w22, w32]])
    B = np.array([b1, b2, b3])

    A = np.dot(X, W) + B
    Z = ReLU(A)
    print(Z)

このプログラムの実行結果は、下記のようになります。

$ python ./network.py
[ 0.19  0.32  0.45]

数値はテキトーに決めたものなので、この結果にも特に意味はないですね...。

[番外編] 関数を交換できる実装にする

上のコードでは ReLU 関数を使いました。
python では関数の引数に関数を指定できるようです。
ReLU 関数以外の関数にも、すぐに差し替えられるようにソースコードをいじってみます。

import numpy as np

def ReLU(x):
    return np.maximum(0, x)

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

def start_network(X, W, B, h):
    A = np.dot(X, W) + B
    Z = h(A)
    return Z

if __name__ == '__main__':
    X = np.array([0.1, 0.2])
    W = np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])
    B = np.array([0.1, 0.2, 0.3])
    Z = start_network(X, W, B, sigmoid)
    print('sigmoid->')
    print(Z)
    Z = start_network(X, W, B, ReLU)
    print('ReLU->')
    print(Z)
$ python ./network.py
sigmoid->
[ 0.54735762  0.57932425  0.61063923]
ReLU->
[ 0.19  0.32  0.45]

活性化関数が変更された場合でも、再利用可能な形にできました。

1層→2層の信号伝達

続きを書いていきます。

import numpy as np

def ReLU(x):
    return np.maximum(0, x)

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

def start_network(X, W, B, h):
    A = np.dot(X, W) + B
    Z = h(A)
    return Z

if __name__ == '__main__':
    X = np.array([0.1, 0.2])
    W = np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])
    B = np.array([0.1, 0.2, 0.3])
    Z = start_network(X, W, B, sigmoid)
    W2 = np.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]])
    B2 = np.array([0.1, 0.2])
    Z2 = start_network(Z, W2, B2, sigmoid)
    print(Z2)

この時点の実行結果は、次のようになります。

$ python ./network.py
[ 0.65336253  0.71250568]

入力層→出力層の信号伝達

ぜんぶ計算してしまいます。
出力層では活性化関数に恒等関数を使うので、それも定義しています。

import numpy as np

def ReLU(x):
    return np.maximum(0, x)

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

def identity(x):
    return x

def start_network(X, W, B, h):
    A = np.dot(X, W) + B
    Z = h(A)
    return Z

if __name__ == '__main__':
    X = np.array([0.1, 0.2])
    W = np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])
    B = np.array([0.1, 0.2, 0.3])
    Z = start_network(X, W, B, sigmoid)
    W2 = np.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]])
    B2 = np.array([0.1, 0.2])
    Z2 = start_network(Z, W2, B2, sigmoid)
    W3 = np.array([[0.1, 0.2], [0.3, 0.4]])
    B3 = np.array([0.1, 0.2])
    Z3 = start_network(Z2, W3, B3, identity)
    print(Z3)

実行結果は、下記のようになります。

[ 0.37908796  0.61567478]

[番外編] 再利用可能な形にリファクタリング

だいぶちらかったプログラムになってしまったので、リファクタリングします。

import numpy as np

# 活性化関数
def ReLU(x):
    return np.maximum(0, x)

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

def identity(x):
    return x

# 信号伝達処理(3層分)
def eval_3_network(X, network, h):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    B1, B2, B3 = network['B1'], network['B2'], network['B3']
    Z1 = eval_single_network(X, W1, B1, h)
    Z2 = eval_single_network(Z1, W2, B2, h)
    Z3 = eval_single_network(Z2, W3, B3, identity)
    return Z3

# 信号伝達処理(1層分)
def eval_single_network(X, W, B, h):
    A = np.dot(X, W) + B
    Z = h(A)
    return Z

# 重みの行列を設定する
def init_network():
    network = {}
    network['W1'] = np.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])
    network['B1'] = np.array([0.1, 0.2, 0.3])
    network['W2'] = np.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]])
    network['B2'] = np.array([0.1, 0.2])
    network['W3'] = np.array([[0.1, 0.2], [0.3, 0.4]])
    network['B3'] = np.array([0.1, 0.2])
    return network

if __name__ == '__main__':
    network = init_network()
    X = np.array([0.1, 0.2])
    Y = eval_3_network(X, network, sigmoid)
    print(Y)

だいぶ整理できました。
あとは重みを外部からもらうようにすれば完璧ですが、番外編なので、これくらいにしておきます。