パーセプトロンとNNのコード書いてみた

タイトルの通り。紙の上で行列とかごにょごにょやるのもいいけど、Emacsたんとキャッキャウフフしたくなったでござる。

パーセプトロン

#!/usr/bin/python
# -*- coding: utf-8 -*-

from scipy import *
import matplotlib.pyplot as plt

if __name__ == '__main__':
    # 学習パターンの個数
    N = 30

    # wの真の値
    w0 = array([-0.2, 0.5, -1])
    x1, x2 = -1 + random.rand(N)*2, -1 + random.rand(N)*2
    t = (w0[0]+x1*w0[1]+x2*w0[2] >= 0) * 2 - 1

    # wの初期値。
    w = array([0, 1, 1], dtype=float)

    # N個に対して誤分類が無くなるまでループ
    count = i = 0
    while count < N:
        if dot(w, [1, x1[i], x2[i]])*t[i]>=0:
            count += 1
        else:
            w += array([1, x1[i], x2[i]])*t[i]
            count = 1
        i = (i+1)%N

    x = linspace(-1.0, 1.0, 50)
    y = -w[0]/w[2]-w[1]/w[2]*x
    plt.plot(x, y)

    for i in range(N):
        c = 'r' if t[i] >= 0 else 'b'
        plt.scatter([x1[i]], [x2[i]], color=c)

    plt.xlim(-1.0,1.0)
    plt.ylim(-1.0,1.0)
    plt.show()

実行すると。。。

みたく線形分離できた気分に。*1


5章のNNも。ただし2層限定

!/usr/bin/python
# -*- coding: utf-8 -*-

from scipy import *
import matplotlib.pyplot as plt

class NN(object):
    """
    2層ニューラルネットワーク
    PRML5.3までの知識が元
    """
    def __init__(self,
                 inputs,
                 hidden,
                 outputs,
                 hidden_activation_func=tanh,
                 hidden_activation_func_diff=lambda h:1-h*h,
                 output_activation_func=lambda x:x):

        self._d = inputs
        self._m = hidden
        self._k = outputs
        self._h = hidden_activation_func
        self._h_diff = hidden_activation_func_diff
        self._f = output_activation_func

        self.w_init()

    def w_init(self):
        self._w1 = random.rand(self._m, self._d+1)
        self._w1 = resize(self._w1, (self._m, self._d+1))
        self._w2 = random.rand(self._k, self._m+1)
        self._w2 = resize(self._w2, (self._k, self._m+1))

    def calc(self, x0):
        x = insert(x0, 0, 1.0)
        a = dot(self._w1, x)
        z = insert(self._h(a), 0, 1.0)

        b = dot(self._w2, z)
        return self._f(b)

    def train(self, x0, t, n=0.1):
        x = insert(x0, 0, 1.0)
        a = dot(self._w1, x)
        z = insert(self._h(a), 0, 1.0)

        b = dot(self._w2, z)
        y = self._f(b)

        d2 = y - t
        d1 = dot(self._w2.T, d2)*self._h_diff(z)

        dE2 = dot(array([d2]).T, array([z]))
        dE1 = dot(array([d1[1:]]).T, array([x]))

        self._w1 -= n*dE1
        self._w2 -= n*dE2

ipythonで適当にimportして戯れつつ、
y=x, y=x**2, y=x*(x-0.5)*(x+0.5), y=tanh(x) 等を学習させてみると



みたいな感じに。ほー。
ネットワークの更新は逐次的勾配降下法でやってるんだが、どーも遅いというか何というか。凖ニュートン法とか共役勾配法とか調べて見ようかし。。。

*1:なんて適当な気分だ