NumPyだけでXOR回路
XOR回路
XOR回路は入力x1,x2に対して、出力yを行います。 | x1| x2| y | |:-:|:-:|:-:| | 0 | 0 | 0 | | 0 | 1 | 1 | | 1 | 0 | 1 | | 1 | 1 | 0 |
多層パーセプトロン
隠れ層をもつニューラルネットワークのことです。 単純パーセプトロンを何層にも重ねたので、多層パーセプトロンといいます。 XOR回路は線形分離可能ではないため、多層パーセプトロンで解きます。
ニューラルネットワークを作成する
基本AND回路と同じで、 1.パラメータの宣言 2.学習 2.1.訓練データの推論 2.2.損失の計算 2.3.勾配の計算 2.4.パラメータの更新 3.テストデータの推論 です。
パラメータの宣言
2層で行います。
def __init__(self, input_size, hidden_size, output_size): self.params = {} self.params['W1'] = 1.5**np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = 1.5*np.random.randn(hidden_size, output_size) self.params['b2'] = np.zeros(output_size)
学習
def training(self, x, t): loss_W = lambda W: self.loss(x, t) grads = {} grads['W1'] = numerical_gradient(loss_W, self.params['W1']) grads['b1'] = numerical_gradient(loss_W, self.params['b1']) grads['W2'] = numerical_gradient(loss_W, self.params['W2']) grads['b2'] = numerical_gradient(loss_W, self.params['b2']) for key in ('W1', 'b1', 'W2', 'b2'): self.params[key] -= 0.5*grads[key] #return grads
推論
def predict(self, x): W1, W2 = self.params['W1'], self.params['W2'] b1, b2 = self.params['b1'], self.params['b2'] h1 = np.dot(x, W1) + b1 z1 = relu(h1) h2 = np.dot(z1, W2) + b2 y = softmax(h2) return y
損失の計算
def loss(self, x, t): y = self.predict(x) loss = mean_squared_error(y, t) return loss
勾配の計算
def numerical_gradient(f, x): h = 1e-4 # 0.0001 grad = np.zeros_like(x) it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) while not it.finished: #全ての組み合わせを計算 idx = it.multi_index tmp_val = x[idx] x[idx] = float(tmp_val) + h fxh1 = f(x) # f(x+h) x[idx] = float(tmp_val) - h fxh2 = f(x) # f(x-h) grad[idx] = (fxh1 - fxh2) / (2*h) x[idx] = tmp_val # 値を元に戻す it.iternext() return grad
パラメータの更新
for key in ('W1', 'b1', 'W2', 'b2'): self.params[key] -= 0.5*grads[key]
推論
for i in range(x_train.shape[0]): print(x_train[i], "answer", network.predict(x_train[i]))
ソースコード
#!/usr/bin/env python # -*- coding: utf-8 -*- #たまにうまくいく import numpy as np class LayerNet: def __init__(self, input_size, hidden_size, output_size): self.params = {} self.params['W1'] = 1.5**np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = 1.5*np.random.randn(hidden_size, output_size) self.params['b2'] = np.zeros(output_size) def predict(self, x): W1, W2 = self.params['W1'], self.params['W2'] b1, b2 = self.params['b1'], self.params['b2'] h1 = np.dot(x, W1) + b1 z1 = relu(h1) h2 = np.dot(z1, W2) + b2 y = softmax(h2) return y def loss(self, x, t): y = self.predict(x) loss = mean_squared_error(y, t) return loss def training(self, x, t): loss_W = lambda W: self.loss(x, t) grads = {} grads['W1'] = numerical_gradient(loss_W, self.params['W1']) grads['b1'] = numerical_gradient(loss_W, self.params['b1']) grads['W2'] = numerical_gradient(loss_W, self.params['W2']) grads['b2'] = numerical_gradient(loss_W, self.params['b2']) for key in ('W1', 'b1', 'W2', 'b2'): self.params[key] -= 0.5*grads[key] #return grads def relu(x): return np.maximum(x, 0) 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 def mean_squared_error(y, t): loss = np.sum(0.5*(y-t)**2) return loss def numerical_gradient(f, x): h = 1e-4 # 0.0001 grad = np.zeros_like(x) it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) while not it.finished: #全ての組み合わせを計算 idx = it.multi_index tmp_val = x[idx] x[idx] = float(tmp_val) + h fxh1 = f(x) # f(x+h) x[idx] = float(tmp_val) - h fxh2 = f(x) # f(x-h) grad[idx] = (fxh1 - fxh2) / (2*h) x[idx] = tmp_val # 値を元に戻す it.iternext() return grad x_train = np.array([[0,0], [0,1], [1,0], [1,1]]) t_train = np.array([[0,1],[1,0],[1,0],[0,1]]) #[1,1]のとき[0,0]と間違っていた network = LayerNet(2, 2, 2) for step in range(10001): batch_mask = np.random.choice([0,1,2,3], 1) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] network.training(x_batch, t_batch) #for key in ('W1', 'b1', 'W2', 'b2'): # network.params[key] -= 0.5*grad[key] if step % 1000 == 0: print("step", step) for i in range(x_train.shape[0]): print(x_train[i], "answer", network.predict(x_train[i]))
出力
たまに正しく出力されます。
('step', 10000) (array([0, 0]), 'answer', array([ 0.01287935, 0.98712065])) (array([0, 1]), 'answer', array([ 0.99375572, 0.00624428])) (array([1, 0]), 'answer', array([ 0.99372009, 0.00627991])) (array([1, 1]), 'answer', array([ 0.00540053, 0.99459947]))
100回中何回成功するかを計測するプログラム
#network = LayerNet(2, 2, 2) ans_label = np.argmax(t_train, axis=1) ans = 0 for i in range(100): network = LayerNet(2, 2, 2) for step in range(1001): batch_mask = np.random.choice([0,1,2,3], 1) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] network.training(x_batch, t_batch) if step == 1000: y = np.argmax(network.predict(x_train), axis=1) print("y:", y) a = (y == ans_label) if a.all(): ans += 1 print("ans:", ans, "/ 100")
学習率0.5のプログラムを3回実行した結果。
('ans:', 26, '/ 100') ('ans:', 30, '/ 100') ('ans:', 27, '/ 100')
およそ30/100でしか正解しない。
学習率0.01
('ans:', 15, '/ 100') ('ans:', 29, '/ 100')
改良点
パラメータを最適化していく。
1.確率的勾配降下法ではなく、Momentum、AdaGradを使ってみる。
2.重みの初期値を変える。Xavierの初期値か、ReLUをつかっているためHeの初期値を使う