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

More than 1 year has passed since last update.


はじめに

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)

だいぶ整理できました。

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