まずはソースコード
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
# -- Irisデータの読み込み --
iris_data = datasets.load_iris()
input_data = iris_data.data
correct = iris_data.target
n_data = len(correct) # サンプル数
# -- 入力データを標準化する --
ave_input = np.average(input_data, axis=0)
std_input = np.std(input_data, axis=0)
input_data = (input_data - ave_input) / std_input
# -- 正解をone-hot表現にする --
correct_data = np.zeros((n_data, 3))
for i in range(n_data):
correct_data[i, correct[i]] = 1.0
# -- 訓練データとテストデータ --
index = np.arange(n_data)
index_train = index[index%2 == 0]
index_test = index[index%2 != 0]
input_train = input_data[index_train, :] # 訓練 入力
correct_train = correct_data[index_train, :] # 訓練 正解
input_test = input_data[index_test, :] # テスト 入力
correct_test = correct_data[index_test, :] # テスト 正解
n_train = input_train.shape[0] # 訓練データのサンプル数
n_test = input_test.shape[0] # テストデータのサンプル数
# -- 各設定値 --
n_in = 4 # 入力層のニューロン数
n_mid = 25 # 中間層のニューロン数
n_out = 3 # 出力層のニューロン数
wb_width = 0.1 # 重みとバイアスの広がり具合
eta = 0.01 # 学習係数
epoch = 1000
batch_size = 8
interval = 100 # 経過の表示間隔
# -- 各層の継承元 --
class BaseLayer:
def __init__(self, n_upper, n):
self.w = wb_width * np.random.randn(n_upper, n) # 重み(行列)
self.b = wb_width * np.random.randn(n) # バイアス(ベクトル)
self.h_w = np.zeros(( n_upper, n)) + 1e-8
self.h_b = np.zeros(n) + 1e-8
def update(self, eta):
self.h_w += self.grad_w * self.grad_w
self.w -= eta / np.sqrt(self.h_w) * self.grad_w
self.h_b += self.grad_b * self.grad_b
self.b -= eta / np.sqrt(self.h_b) * self.grad_b
# -- 中間層 --
class MiddleLayer(BaseLayer):
def forward(self, x):
self.x = x
self.u = np.dot(x, self.w) + self.b
self.y = np.where(self.u <= 0, 0, self.u) # ReLU
def backward(self, grad_y):
delta = grad_y * np.where(self.u <= 0, 0, 1) # ReLUの微分
self.grad_w = np.dot(self.x.T, delta)
self.grad_b = np.sum(delta, axis=0)
self.grad_x = np.dot(delta, self.w.T)
# -- 出力層 --
class OutputLayer(BaseLayer):
def forward(self, x):
self.x = x
u = np.dot(x, self.w) + self.b
self.y = np.exp(u)/np.sum(np.exp(u), axis=1, keepdims=True) # ソフトマックス関数
def backward(self, t):
delta = self.y - t
self.grad_w = np.dot(self.x.T, delta)
self.grad_b = np.sum(delta, axis=0)
self.grad_x = np.dot(delta, self.w.T)
# -- 各層の初期化 --
middle_layer_1 = MiddleLayer(n_in, n_mid)
middle_layer_2 = MiddleLayer(n_mid, n_mid)
output_layer = OutputLayer(n_mid, n_out)
# -- 順伝播 --
def forward_propagation(x):
middle_layer_1.forward(x)
middle_layer_2.forward(middle_layer_1.y)
output_layer.forward(middle_layer_2.y)
# -- 逆伝播 --
def backpropagation(t):
output_layer.backward(t)
middle_layer_2.backward(output_layer.grad_x)
middle_layer_1.backward(middle_layer_2.grad_x)
# -- 重みとバイアスの更新 --
def uppdate_wb():
middle_layer_1.update(eta)
middle_layer_2.update(eta)
output_layer.update(eta)
# -- 誤差を計算 --
def get_error(t, batch_size):
return -np.sum(t * np.log(output_layer.y+ 1e-7)) / batch_size # 交差エントロピー誤差
# -- 誤差の記録用 --
train_error_x = []
train_error_y = []
test_error_x = []
test_error_y = []
# -- 学習と経過の記録 --
n_batch = n_train // batch_size # 1エポックあたりのバッチ数
for i in range(epoch):
# -- 誤差の計測 --
forward_propagation(input_train)
error_train = get_error(correct_train, n_train)
forward_propagation(input_test)
error_test = get_error(correct_test, n_test)
# -- 誤差の記録 --
test_error_x.append(i)
test_error_y.append(error_test)
train_error_x.append(i)
train_error_y.append(error_train)
# -- 経過の表示 --
if i%interval == 0:
print("Epoch:" + str(i) + "/" + str(epoch),
"Error_train:" + str(error_train),
"Error_test:" + str(error_test))
# -- 学習 --
index_random = np.arange(n_train)
np.random.shuffle(index_random) # インデックスをシャッフルする
for j in range(n_batch):
# ミニバッチを取り出す
mb_index = index_random[j*batch_size : (j+1)*batch_size]
x = input_train[mb_index, :]
t = correct_train[mb_index, :]
# 順伝播と逆伝播
forward_propagation(x)
backpropagation(t)
# 重みとバイアスの更新
uppdate_wb()
# -- 誤差の記録をグラフ表示 --
plt.plot(train_error_x, train_error_y, label="Train")
plt.plot(test_error_x, test_error_y, label="Test")
plt.legend()
plt.xlabel("Epochs")
plt.ylabel("Error")
plt.show()
# -- 正解率の測定 --
forward_propagation(input_train)
count_train = np.sum(np.argmax(output_layer.y, axis=1) == np.argmax(correct_train, axis=1))
forward_propagation(input_test)
count_test = np.sum(np.argmax(output_layer.y, axis=1) == np.argmax(correct_test, axis=1))
print("Accuracy Train:", str(count_train/n_train*100) + "%",
"Accuracy Test:", str(count_test/n_test*100) + "%")
前準備
Irisデータセットはscikit-learnという機械学習用のモジュールを用いて簡単に読み込むことができる。
Anaconda環境であればデフォルトでインストール済み
各層の実装
class BaseLayer:
def __init__(self, n_upper, n):
self.w = wb_width * np.random.randn(n_upper, n) # 重み(行列)
self.b = wb_width * np.random.randn(n) # バイアス(ベクトル)
self.h_w = np.zeros(( n_upper, n)) + 1e-8
self.h_b = np.zeros(n) + 1e-8
def update(self, eta):
self.h_w += self.grad_w * self.grad_w
self.w -= eta / np.sqrt(self.h_w) * self.grad_w
self.h_b += self.grad_b * self.grad_b
self.b -= eta / np.sqrt(self.h_b) * self.grad_b
# -- 中間層 --
class MiddleLayer(BaseLayer):
def forward(self, x):
self.x = x
self.u = np.dot(x, self.w) + self.b
self.y = np.where(self.u <= 0, 0, self.u) # ReLU
def backward(self, grad_y):
delta = grad_y * np.where(self.u <= 0, 0, 1) # ReLUの微分
self.grad_w = np.dot(self.x.T, delta)
self.grad_b = np.sum(delta, axis=0)
self.grad_x = np.dot(delta, self.w.T)
# -- 出力層 --
class OutputLayer(BaseLayer):
def forward(self, x):
self.x = x
u = np.dot(x, self.w) + self.b
self.y = np.exp(u)/np.sum(np.exp(u), axis=1, keepdims=True) # ソフトマックス関数
def backward(self, t):
delta = self.y - t
self.grad_w = np.dot(self.x.T, delta)
self.grad_b = np.sum(delta, axis=0)
self.grad_x = np.dot(delta, self.w.T)
BaseLayerは中間層、出力層クラスの継承元となるクラス。
_init_で初期設定をして、updateで確率的勾配降下法により重みとバイアスを更新している。
MiddleLayerは活性化関数にReLUを使用している
ニュートラルネットワーク
今回のニュートラルネットワークには中間層が2つ、出力層が1つある。各層のインスタンスを生成した上で、順伝播と逆伝播の関数を
実装している。update_wb()は全ての重みとバイアスを更新する関数。
get_errorは誤差を計算する関数で今回は分類問題なので交差エントロピー誤差を計算。ミニバッチ学習のため、
誤差の総和をバッチサイズで除算している。
ミニバッチ
今回はミニバッチ法を用いるので、バッチごとに重みとバイアスを更新する。
訓練データのサンプル数分だけインデックスを作成してindex_randomとし、シャッフルしている。
そして、ループの中でバッチサイズ分だけランダムなインデックスを取り出し、mb_indexとしている。
input_trainとcorrect_trainは行列ですが、これらからindexがmb_indexの列を抜き出してミニバッチとする。
正解率の測定
全訓練データとテストデータに対して順伝播の計算をした後、それぞれ正解数をカウント。
順伝播によって得られた出力と正解の各要素が一致すればTrue、一致しなければFalseとしている。
正解率はこのtrue(1)/false(0)に100を掛けて求めている。