自作ニューラルネット(分類型)を作って、色々やってみる
今度は分類問題に対応した NN 実装を考える。
とはいえ、テンプレは 前回記事 で作ってるので、さっくり定義する
import numpy as np import matplotlib.pyplot as plt %matplotlib inline wb_width = 0.01 # 重みバイアスの広がり方 epoch = 101 # 学習データ数 eta = 0.1 # 学習係数 # 各層のニューロン数 n_in = 2 n_mid = 6 n_out = 2 class Neuron: def __init__(self, n_upper, n, activation_function, differential_function): self.w = wb_width * np.random.randn(n_upper, n) self.b = wb_width * np.random.randn(n) self.grad_w = np.zeros((n_upper, n)) self.grad_b = np.zeros((n)) self.activation_function = activation_function self.differential_function = differential_function def update(self, eta): self.w -= eta * self.grad_w self.b -= eta * self.grad_b def forward(self, x): self.x = x u = x.dot(self.w) + self.b self.y = self.activation_function(u) return self.y def backword(self, t): delta = self.differential_function(self.y, t) self.grad_w = self.x.T.dot(delta) self.grad_b = np.sum(delta, axis=0) self.grad_x = delta.dot(self.w.T) return self.grad_x class Output(Neuron): pass class Middle(Neuron): def backword(self, grad_y): delta = self.differential_function(grad_y, self.y) self.grad_w = self.x.T.dot(delta) self.grad_b = delta.sum(axis = 0) self.grad_x = delta.dot(self.w.T) return self.grad_x
判定の場合、出力層が異なってくる
シグモイド関数はこんな式なので
def sigmoid(u): """シグモイド関数""" return 1 / (1 + np.exp(-u)) def differential_sigmod(grad_y, y): """シグモイド関数の勾配関数""" return grad_y * (1 - y) * y
出力層のソフトマックス関数 + 交差エントロピーは 過去計算 した式を拝借して
def soft_max(u): """ ソフトマックス関数 """ return np.exp(u) / np.sum(np.exp(u), axis = 1, keepdims=True) def differential_softmax(y, t): """ソフトマックス + 交差エントロピー の勾配関数""" return y - t
# 座標系 X = np.arange(-1.0, 1.1, 0.1) Y = np.arange(-1.0, 1.1, 0.1) # 学習データ input_data = [] correct_data = [] for x in X: for y in Y: input_data.append([x, y]) if y < np.sin(np.pi * x): correct_data.append([0, 1]) else: correct_data.append([1, 0]) n_data = len(correct_data) input_data = np.array(input_data) correct_data = np.array(correct_data) # ニューロン生成 middle = Middle(n_in, n_mid, sigmoid, differential_sigmod) output = Output(n_mid, n_out, soft_max, differential_softmax) # 中間出力 interval = 10 # 表示用 sin_data = np.sin(np.pi * X) for i in range(epoch): # ランダムなインデックス値生成 rand_idx = np.arange(n_data) np.random.shuffle(rand_idx) # 中間結果表示用データ total_err = 0 x_1 = [] y_1 = [] x_2 = [] y_2 = [] for idx in rand_idx: # テストデータと、教師データ x = input_data[idx] t = correct_data[idx] # 順伝播 y = output.forward(middle.forward(x.reshape(1, 2))) # 逆伝播 middle.backword(output.backword(t.reshape(1, 2))) # 学習 middle.update(eta) output.update(eta) if i % interval == 0: y = output.y.reshape(-1) total_err += - np.sum(t * np.log(y + 1e-7)) if y[0] > y[1]: x_1.append(x[0]) y_1.append(x[1]) else: x_2.append(x[0]) y_2.append(x[1]) if i % interval == 0: plt.plot(X, sin_data) plt.scatter(x_1, y_1, marker='*') plt.scatter(x_2, y_2, marker='o') plt.show() print(f"Epoch:{i} / Error: {total_err/n_data}")
開始時
30 エポック学習時点
100 エポック学習時点
精度がガンガン上がっていくことが分かる。
Iris データセットを学習させてみる
scikitlearn にアヤメデータセットが存在する。
詳しくはこれ
データセットの 7 割を学習用、3割を検証用に分離する。
from sklearn.datasets import load_iris import pandas as pd from sklearn.model_selection import train_test_split iris = load_iris() X_train, X_test, y_train, y_test = train_test_split(iris['data'], iris['target'], random_state=0)
で、どんなデータかというと
X_train = pd.DataFrame(X_train) y_train = pd.DataFrame(y_train) X_test = pd.DataFrame(X_test) y_test = pd.DataFrame(y_test) X_train.head()
y_train.head()
このままだと形状が合わないので、加工していく。
def conv(x): if x == 0: return np.array([1, 0, 0]) elif x == 1: return np.array([0, 1, 0]) else: return np.array([0, 0, 1]) y_train[0] = y_train[0].apply(conv) y_test[0] = y_test[0].apply(conv) y_train.head()
入力も 0-1 にしてしまおうか
X_train = X_train.applymap(lambda x: x / 10.0) X_test = X_test.applymap(lambda x: x / 10.0) X_train.head()
さぁ、準備は整った、学習を開始しようか
wb_width = 0.01 # 重みバイアスの広がり方 epoch = 101 # 学習データ数 eta = 0.1 # 学習係数 # 各層のニューロン数 n_in = 4 # 1 行 4 データあるよ n_mid = 8 # ニューロン数は適当 n_out = 3 # 結果は 3 通りだよ # 学習データ input_data = np.array(X_train.values) correct_data = np.array(y_train.values) n_data = len(correct_data) middle = Middle(n_in, n_mid, sigmoid, differential_sigmod) output = Output(n_mid, n_out, soft_max, differential_softmax) for i in range(epoch): # ランダムなインデックス値生成 rand_idx = np.arange(n_data) np.random.shuffle(rand_idx) # 中間結果表示用データ total_err = 0 for idx in rand_idx: # テストデータと、教師データ x = input_data[idx] t = correct_data[idx][0] # 順伝播 y = output.forward(middle.forward(x.reshape(1, 4))) # 逆伝播 middle.backword(output.backword(np.array(t))) # 学習 middle.update(eta) output.update(eta) if i % interval == 0: total_err += - np.sum(t * np.log(y + 1e-7)) if i % interval == 0: print(f"Epoch:{i} / Error: {total_err/n_data}")
学習を開始すると、回を重ねる度にエラー率が下がっていくのが分かる。
では、検証用に残していた残りの 30% のデータで検証しよう
input_data = np.array(X_test.values) correct_data = np.array(y_test.values) def is_match(y, t): """[a, b, c] から最も大きい値を 1 として考える""" a, b, c = t if a > b and a > c: return t[0] == 1 elif b > a and b > c: return t[1] == 1 else: return t[2] == 1 correct_count = 0 for idx in range(len(X_test)): x = input_data[idx] t = correct_data[idx][0] # 順伝播してみる y = output.forward(middle.forward(x.reshape(1, 4))) y = y.reshape(-1) # 結果比較 if is_match(y, t): correct_count += 1 print(f'Correct rate: {correct_count} / {len(X_test)}')
うぇーい (゚∀゚)