1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

多層パーセプトロンを1から作ってみた

Last updated at Posted at 2022-12-22

この記事は,Pythonそのものの使い方かDeepLearningの仕組みまで,1から細かく学習した際のメモ書きとスライドのまとめです.

多層パーセプトロン(AND真偽値表の学習)

今回は,多層パーセプトロン(Perceptron)を用いて,AND真偽値表の出力を学習させることを目指す.AND真偽値表とは,入力の2つが共に1である時のみ1を出力するものである.
このAND真偽値表は,前回の1から学ぶDeepLearning (1) で扱ったように,単純パーセプトロンで学習ができたものであった.
今回は,多層パーセプトロンを学習することを第一の目的として,内容の同じ学習を行い,単純パーセプトロンとの違いを見てみることにする.

x1 x2 y
0 0 0
0 1 0
1 0 0
1 1 1

DeepLearning.jpg

import numpy as np

# 活性化関数の定義(Sigmoid関数)
def sigmoid(x):
    return 1/(1+np.exp(-x))
def sigmoid_grad(x):
    return (1.0 - sigmoid(x))*sigmoid(x)

class MLP:
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.1):
        self.w1 = weight_init_std * np.random.randn(input_size, hidden_size)
        self.w2 = weight_init_std * np.random.randn(hidden_size, output_size)
        self.b1 = 0.0
        self.b2 = 0.0
        self.grads = {}

    def forward(self, x): # x : 入力
        self.a1 = np.dot(x, self.w1) + self.b1
        self.z1 = sigmoid(self.a1)
        self.a2 = np.dot(self.z1, self.w2) + self.b2
        self.y = sigmoid(self.a2)
        return self.y

    def backward(self, x, t): # x : 入力,y : 教師
        
        # 誤差の算出
        l = -1 * (t - self.y)
        d_a2 = sigmoid_grad(self.a2) * l
        self.grads['w2'] = np.dot(self.z1.T, d_a2)
        self.grads['b2'] = d_a2

        d_z1 = np.dot(d_a2, self.w2.T)
        d_a1 = d_z1 * sigmoid_grad(self.a1)
        self.grads['w1'] = np.dot(x.T, d_a1)
        self.grads['b1'] = d_a1
    
    def update_parameters(self, lr = 0.1): # lr : 学習率
        
        self.w1 -= lr * self.grads['w1']
        self.w2 -= lr * self.grads['w2']

        self.b1 -= lr * self.grads['b1']
        self.b2 -= lr * self.grads['b2']


def display_model_parameters(model):
    print("w1 : ", model.w1)
    print("w2 : ", model.w2)
    print("b1 : ", model.b1)
    print("b2 : ", model.b2)


if __name__ == '__main__':
    
    # AND回路の学習を行うための入力データと教師ラベルの定義
    input_data = np.array([[0,0],[0,1],[1,0],[1,1]])
    label_data = np.array([0,0,0,1])

    # モデルの作成
    input_size = 2
    hidden_size = 3
    output_size = 1

    model = MLP(input_size, hidden_size, output_size)
    display_model_parameters(model)

    # ハイパーパラメータの指定
    num_train_data = 4
    epoch_num = 5000
    lerning_rate = 0.1

    for i in range(1,epoch_num+1,1):
        sum_loss = 0.0
        for j in range(0,num_train_data,1):
            x = input_data[j:j+1]
            t = label_data[j:j+1]

            y_pred = model.forward(x)
            model.backward(x, t)
            model.update_parameters(lerning_rate)

            sum_loss = np.power(t - y_pred, 2)
        
        print("epoch : {}, loss : {}".format(i, sum_loss/4))
    
    display_model_parameters(model)

    #正解率の算出
    cnt_correct = 0
    cnt_all = 0
    tolerance = 0.05 #許容範囲の設定
    for i in range(0,len(input_data)):
        y = model.forward(input_data[i])
        print("input_data : {}, y : {}".format(input_data[i], y))
        label = label_data[i]
        if  label-tolerance < y and y < label+tolerance:
            cnt_correct += 1
        cnt_all += 1
      
    accuracy = cnt_correct/cnt_all
    print("accuracy : ", accuracy)

実行結果

epoch : 5000, loss : [[0.00178896]]
w1 :  [[-2.6255562  -2.0996942  -1.82297711]
 [-2.6269102  -1.89913472 -2.03126441]]
w2 :  [[-5.08765545]
 [-3.5903548 ]
 [-3.41550001]]
b1 :  [[3.23467422 2.07894497 1.93356411]]
b2 :  [[3.87595026]]
input_data : [0 0], y : [[0.0007504]]
input_data : [0 1], y : [[0.04749434]]
input_data : [1 0], y : [[0.04750903]]
input_data : [1 1], y : [[0.91554948]]
accuracy :  0.75

単純パーセプトロン(前回プログラム)の実行結果

epoch : 5000, loss : 0.0019097360320011203
w :  [5.82859069 5.82738022] b :  -9.028217956770213
input_data : [0 0], y : 0.00011996171284962275
input_data : [0 1], y : 0.03913420962830516
input_data : [1 0], y : 0.039179751963228676
input_data : [1 1], y : 0.9326264954936772
accuracy :  1.0

同じ学習率,エポック数で行った結果だが,学習損失を示したものの最後をみると,中間層を増やした多層パーセプトロンの方が,良くない結果になっていることが確認できる.

実験前までは,単純に層を増やした方が,柔軟に対応できるようになり良い結果が得られると考えていたが,この予想に反する結果になった.
そこで,学習で調整するパラメータが多いのが主な要因であると考えた.エポック数を倍の10000にして再度実行して挙動をみてみることにする.

エポック数を10000に変更して再度実行

epoch : 10000, loss : [[0.00058127]]
w1 :  [[-2.93745684 -1.50381849 -2.61486656]
 [-3.05729588 -1.25551698 -2.56420406]]
w2 :  [[-5.95868659]
 [-1.94927261]
 [-4.95068125]]
b1 :  [[3.85144893 0.67567557 3.15570244]]
b2 :  [[4.40249983]]
input_data : [0 0], y : [[0.00056819]]
input_data : [0 1], y : [[0.02691188]]
input_data : [1 0], y : [[0.02732029]]
input_data : [1 1], y : [[0.95180993]]
accuracy :  1.0

単純パーセプトロン(前回プログラム)も同様にエポック数を10000にて再度実行

epoch : 10000, loss : 0.000508028532782091
w :  [7.20750455 7.20687697] b :  -11.097796727457217
input_data : [0 0], y : 1.5145427708191534e-05
input_data : [0 1], y : 0.020017658200903544
input_data : [1 0], y : 0.020029973106389383
input_data : [1 1], y : 0.9649934048107632
accuracy :  1.0

同じエポック数では,また多層パーセプトロンの学習結果の方が劣った結果になった.
やはり,単純パーセプトロンで解ける問題は,単純パーセプトロンで解いた方が効率も精度も良いということなのか...

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?