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の初期値を使う