桜朔

さくらさく

                                      スポンサーリンク

ChainerでDeep Learning (3) - XOR編

こんにちは,櫻井朔@hajimesakuraiです.

Chanerを使用した機械学習の続きです.

やはり自分で用意したデータで学習をさせなければ意味がないので,練習としてXORを学ばせてみました.

プログラム

中間層をとりあえず1層,またニューロンを2個とした場合,プログラムは以下のようになります(オブジェクト化してなくてすみません).

#!/usr/bin/env python
# coding:utf-8
import numpy as np
import chainer.functions as F
from chainer import Variable, FunctionSet, optimizers

# model definition
h1_num = 2
model = FunctionSet(
    l1 = F.Linear(2, h1_num),
    l2 = F.Linear(h1_num, 1)
)
optimizer = optimizers.SGD()
optimizer.setup(model)

# number of learning
times = 50000

# input and output vector
xor_data = [
    [np.array([0,0]), np.array([0])],
    [np.array([0,1]), np.array([1])],
    [np.array([1,0]), np.array([1])],
    [np.array([1,1]), np.array([0])],
] * times

# main routine
fout = open('output.csv', 'w')
lp = 0
for in_vec, out_vec in xor_data:
    optimizer.zero_grads()

    # extract input and output
    x = Variable(np.array([in_vec]).astype(np.float32))
    t = Variable(np.array([out_vec]).astype(np.float32))
    
    # estimation by model
    h1 = model.l1(x)
    y  = model.l2(F.sigmoid(h1))

    # print
    if lp % 101 == 0:
        print(lp, in_vec, y.data, out_vec) 
        string = "%d, %d, %d, %f, %d\n" % (lp, in_vec[0], in_vec[1], y.data[0], out_vec)
        fout.write(string)

    # error correction
    loss = F.mean_squared_error(y, t)

    # feedback and learning
    loss.backward()
    optimizer.update()
    
    # back to top
    lp += 1

fout.close()

ここでl1およびl2が「入力から中間層」と「中間層から出力」を表す関数になります.XORは2個の入力から1個の出力を得るのでこのようになりますが,中間層のニューロン数は任意です.これらをFunctionSetでまとめてやり,modelという名前にし,chainerのoptimizerに渡してやります.

学習させる値はxor_dataという配列で容易しています.これをchainerに渡すときは型指定をした後にVariable関数を使う必要があります.

実際に学習させたモデルを使って出力を推定するのが

h1 = model.l1(x)
y  = model.l2(F.sigmoid(h1))

の部分です.2進数の問題なのでシグモイド関数でBinarizationする方が良いようです.推定した値と正しい値を比較するためにF.mean_squared_errorなどで誤差を出してやり,この結果をoptimizerに返してやることでモデルを修正するという繰り返し作業になります.

結果(中間層を変化させた場合)

専門家の間では常識のようですが,上記のプログラムではXORの学習は上手く収束しません(ANDやORだと収束する).単純パーセプトロンではXORが実現できないため,例題としてよく使われる理由の1つとなっています.

なので,中間層の数を増やしてみて,学習回数と収束の具合をグラフにしてみました.

f:id:hajimesakurai:20150830192217p:plain

プログラムの出力結果は以下のとおりです. f:id:hajimesakurai:20150830194322p:plain

中間層のニューロンが2個の場合,2層が最も結果が良く,1層および4層以上では収束が得られませんでした.ニューロン数を増やしてみた場合の結果も気になったので10個にしてみたところ,結果は以下となりました.

f:id:hajimesakurai:20150830192601p:plain

4層でも収束するようになり,他の層数でも収束が速くなりました.

ニューロン数を変化させた場合

というわけで,ニューロン数のみを変化させた場合も計算してみたところ,以下となりました.中間層は2層です.

f:id:hajimesakurai:20150830192741p:plain

これは単純に数が多いほうが収束が速いという結果になっています.

後記

以上より,

  • 中間層は多いほうが良いとは限らない
  • ニューロン数は多いほうが望ましいが,計算時間の増大に繋がる

という結果が得られました.

...まあ予想通りですね.

層が多い方が良いわけではないことはニューラルネットが盛んな時期から言われていたことです.そのためにニューラルネット冬の時代が訪れていたわけですが,そこでブレイクスルーを起こしたのがDeep Learningです.このあたりをもう少し詰めていきたいと思います. (お気づきの方もいるかと思いますが,ここまでのところは単なるニューラルネットの話です)

ちなみに,Chainerのプログラムの書き方については下記のページなどを参考にさせていただきました. hi-king.hatenablog.com

それでは.

深層学習 (機械学習プロフェッショナルシリーズ)

深層学習 (機械学習プロフェッショナルシリーズ)

マシンラーニング 第2版 (Rで学ぶデータサイエンス 6)

マシンラーニング 第2版 (Rで学ぶデータサイエンス 6)