ニューラルネットワーク
2章のパーセプトロンとすごくにた概念ではある。
用語整理
- 入力層
- 出力層
- 中間層(隠れ層)
入力層から出力層へ向かって、順に第0層、第1層、第2層と呼ぶことにします。
2章の復習からすると以下のような式で表すことが可能
y = h(b+w1x1+w2x2)
h(x) = 0 (x <= 0)
1 (x > 0)
a = b+w1x1+w2x2
h(a) = 0 (x <= 0)
1 (x > 0)
活性化関数
ステップ関数実装
h(x) = 1 (x >= 0)
0 (x < 0)
ステップ関数を式で表そうとすると
step_function(np.array([1.0, 2.0]))のような使い方はできないので、
import numpy as np
x = np.array([-1.0, 1.0, 2.0])
y = x > 0
y
array([False, True, True], dtype=bool)
y = y.astype(np.int)
y
array([0, 1, 1])
上記を関数にまとめると
def step_function(x):
return np.array(x > 0, dtype= np.int)
で表すことができる。
シグモイド関数の実装
h(x) = 1 / 1 + exp(-x)
def sigmoid(x):
return 1/ (1 + np.exp(-x))
x = np.array([-1.0, 1.0, 2.0])
sigmoid(x)
> array([0.2689, 0.73.., 0.88..])
パーセプトロンは0,1の信号しか流れないが、NNは連続の信号が流れてくる。
活性化関数には線形関数を用いてはならない。
線形代数を用いるとニューラルネットワークで層を深くすることの意味がなくなってしまうから。
隠れ層の無いネットワークになるから多層にする利点を活かすことができない
ReLU関数
sigmoidが一般的であるが、ReLU(Rectified Linear Unit)という関数が主に用いられます。
ReLU関数を数式で表すと、次の式のように書くことができます。
h(x) = x (x > 0)
0 (x <= 0)
実装
def relu(x):
return np.maximum(0, x)
多次元配列の計算
行列の内積
A = np.array([[1,2],[3,4]])
B = np.array([[5,6],[7,8]])
np.dot(A, B)
>> array([[19, 22],[43, 50]])
A = np.array([1,2,3],[4,5,6])
B = np.array([[1,2],[3,4],[5,6]])
np.dot(A,B)
>> array([[22,28],[49,64]])
※次元を合わさないとエラーが出るので注意
ニューラルネットワークの内積
X = np.array([1,2])
W = np.array([1,3,5],[2,4,6])
Y = np.dot(X,W)
print(Y)
>>> [5,11,17]
3層ニューラルネットワークの実装
3層ニューラルネットワークを簡単な式に表すと
A = XW = B
W1.shape = (2, 3)
W2.shape = (3, 2)
W3.shape = (2, 2)
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
A3 = np.dot(Z2, W3) + B3
実装まとめ
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 = identity_function(a3)
return y
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y) # [0.3168.., 0.69....]
出力層の設計
恒等関数とソフトマックス関数
恒等関数は、入力をそのまま出力します。
ソフトマックス関数は次の式で表されます
yk = exp(ak) / nΣi=1 exp(ai)
a = np.array([0.3, 2.9, 4.0])
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a = sum_exp_a
ソフトマックス関数の実装の注意
オーバーフローに注意しないといけない。
def softmax(a):
c = np.max(a)
exp_a = np.exp(a-c) # オーバーフロー対策
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
出力層のニューロンの数
10クラス分類問題の場合は出力層を10個に設定する。
手書き数字認識
MNISTデータ・セット
- MNIST
手書き数字の画像セット。最も有名なデータセットのひとつ。0から9までの数字画像から構成される。
訓練画像60000枚、テスト画像10000枚用意されており、それらの画像を使用して、学習と推論を行います。
一般的なMNISTデータセットの使い方では、訓練画像を使って学習を行い、学習したモデルでテスト画像に対してどれだけ正しく分類できるかを計測します。
実装
import sys, os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = \
load_mnist(flatten=True, normalize=False)
print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000,)
print(x_test.shape) # (10000, 784)
print(t_test.shape) # (10000,)
(訓練画像, 訓練ラベル), (テスト画像, テストラベル)という形式で、読み込んだMNISTデータを返します。
import sys, os
sys.path.append(os.pardir)
import numpy as np
from datase.mnist import load_mnist
from PIL import Image
def img_show(img):
pil_img = IMage.fromarray(np.unit8(img))
pil_img.show()
(x_train, t_train), (x_test, t_test) = \
load_mnist(flatten = True, normalize = False)
img = x_train[0]
label = t_train[0]
print(label) # 5
print(img.shape) # (784)
img = img.reshape(28, 28)
print(img.shape) # (28, 28)
img_show(img) # 5の画像が表示
flatten = Trueとして読み込んだ画像はNumpy配列として1次元で格納されている。
表示のときは、28 x 28 のサイズにreshapeする必要がある。
ニューラルネットワークの推論処理
10の数字の分類になるので、出力層は10個になる。
また、隠れ層が2つあり、1つ目の隠れ層が50個、2つ目の層が100個のニューロンを持つものとする。
この50と100という数字は、任意の値に設定可能。
はじめに、3つの関数を定義する。
- get_data()
- init_network()
- predict()
def get_date():
(x_train, t_train), (x_test, t_test) = \
load_mnist(normalize=True, flatten=True, one_hot_label=False)
return x_test, t_test
def init_network():
with open("sample_weight.pkl", 'rb') as f:
network = pickle.load(f)
return network
def predict(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 = softmax(a3)
return y
x, t = get_date()
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
y = predict(network, x[i])
p = np.armax(y) # 最も確立の高い要素のインデックスを取得
if p == t[i]:
accurancy_cnt += 1
print("accurancy:" + str(float(accuracy_cnt) / len(x)))
sample_weight.pklに保存された学習済みの重みパラメーターを読み込みます。このファイルには、重みとバイアスのパラメータが保存されている。pklに関しては次章で説明を行う。