stMind

You'll never blog alone

Pythonで多層パーセプトロンの実装例

NNの構成

    # initialize
    mlp = MLP(n_input_units=2, n_hidden_units=3, n_output_units=1)

XORを実現する3層のニューラルネットワークを例として実装します。入力層は2、隠れ層は3、出力層は1つのニューロンを持ちます。

class MLP(object):
    """
    3 Layered Perceptron
    """
    def __init__(self, n_input_units, n_hidden_units, n_output_units):
        self.nin = n_input_units
        self.nhid = n_hidden_units
        self.nout = n_output_units

        self.v = np.random.uniform(-1.0, 1.0, (self.nhid, self.nin+1))
        self.w = np.random.uniform(-1.0, 1.0, (self.nout, self.nhid+1))

ただし、入力層と隠れ層の間の重みvと、隠れ層と出力層の間の重みwにはバイアス項を加えるので+1とします。つまり、vは3x3の行列、wは1x4の行列となります。

--- NN configuration ---
Num of input layer units: 2
Num of hidden layer units: 3
Num of output layer units: 1
Shape of first layer weight(v): (3, 3)
Shape of second layer weight(w): (1, 4)

NNの学習

以前のエントリーで書いた誤差逆伝播法と最急降下法を用いた各層の重みの更新式をコードにします。

    def fit(self, inputs, targets, learning_rate=0.2, epochs=10000):
        inputs = self.__add_bias(inputs, axis=1)
        targets = np.array(targets)

        for loop_cnt in xrange(epochs):
            # randomise the order of the inputs
            p = np.random.randint(inputs.shape[0])
            xp = inputs[p]
            bkp = targets[p]

            # forward phase
            gjp = self.__sigmoid(np.dot(self.v, xp))
            gjp = self.__add_bias(gjp)
            gkp = self.__sigmoid(np.dot(self.w, gjp))

            # backward phase(back prop)
            eps2 = self.__sigmoid_deriv(gkp) * (gkp - bkp)
            eps = self.__sigmoid_deriv(gjp) * np.dot(self.w.T, eps2)

            gjp = np.atleast_2d(gjp)
            eps2 = np.atleast_2d(eps2)
            self.w = self.w - learning_rate * np.dot(eps2.T, gjp)

            xp = np.atleast_2d(xp)
            eps = np.atleast_2d(eps)
            self.v = self.v - learning_rate * np.dot(eps.T, xp)[1:, :]
    def __add_bias(self, x, axis=None):
        return np.insert(x, 0, 1, axis=axis)

    def __sigmoid(self, u):
        """
        Sigmoid function(Activation function)
        """
        return (1.0 / (1.0 + np.exp(-u)))

    def __sigmoid_deriv(self, u):
        return (u * (1 - u))

実装するときは、各式で出てくるベクトルや行列の次元を表示して、自分の意図通りになっているかを確認しながら進めていくと良いと思います(下に挙げた参考情報の二つ目を参照)。

XORのテスト

XORが実現できているかを確認します。

    inputs = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
    targets = np.array([0, 1, 1, 0])

    # training
    mlp.fit(inputs, targets)

    # predict
    print '--- predict ---'
    for i in [[0, 0], [0, 1], [1, 0], [1, 1]]:
        print i, mlp.predict(i)
--- predict ---
[0, 0] [ 0.07955348]
[0, 1] [ 0.92560327]
[1, 0] [ 0.93024106]
[1, 1] [ 0.06766488]

確かにXOR学習ができているようです。

参考にした情報