単純パーセプトロンを書いてみる

今更感漂うネタですが簡単なパーセプトロンのコードを書いてみたいと思います。高卒でもわかる機械学習というシリーズが非常にわかりやすかったのですが、動くコードがなかったのでそこだけ補完してみようという感じです。

今回は単純パーセプトロン(Single Layer Perceptron, SLP)でAND, ORを学習してみます(ついでにXORが学習できないことも確かめます)。内容的には前述の記事の第2回, 第3回に相当します。これらの記事を前提とするので記号などはちゃんとした定義なしにいきなり使ったりします。ご了承ください。

SLPということで層は入力層と出力層の2つだけです。またAND, ORということは2つの論理値から1つの論理値を計算するので、入力層のユニット数は2(バイアス除く), 出力層のユニット数は1となります。

         in  out
          o

{0,1} --> o

{0,1} --> o   o --> {-1,1}

計算の都合上、出力層からの出力は {0,1} ではなく {-1,1} とし、訓練データの形式もこれに合わせます(False が -1 になるだけです)。入力側は普通に {0,1} とします。例えばANDの真理値表は以下のようになります:

a b a AND b
0 0 -1
0 1 -1
1 0 -1
1 1 1

言い換えると、出力層の活性化関数をこのように定義します:

{ \displaystyle
f(u) = \begin{cases}
   1 & (u \ge 0) \\
  -1 & (u < 0)
\end{cases}
}

学習は訓練データ1件ごとのオンライン学習とし、誤差関数を以下のように定義します*1:

{ \displaystyle
E = max(0, -yu)
}

u は出力層のユニット値(w*x), y は教師データです。前述の通り出力側の値は {-1,1} としているので、SLPが正解を出力したら u と y は同符号、不正解なら異符号となります。重み w の更新は不正解時のみ行うので、実質的には

{ \displaystyle
E = -yu
}

と考えて問題ありません。これは明らかに重み w の各要素について微分可能で、例えば

{ \displaystyle
\frac{\partial E}{\partial w_0} = -yx_0
}

となります。これで勾配がわかるので、後は勾配降下法により学習可能です。

ということでPythonコードを書いてみました。AND, OR, XORそれぞれについて教師データを与え、10000セット学習させています*2。一応学習率ηは 0.01 に設定していますが、重みを0初期化しているので学習率は結果に影響しないはずです*3。出力はこんな感じになります:

#------------------------------------------------------------
# [AND]
#------------------------------------------------------------
### Learning ###
w = [-0.03  0.02  0.01]

### Test ###
[1 0 0] -> -1
[1 0 1] -> -1
[1 1 0] -> -1
[1 1 1] -> 1

#------------------------------------------------------------
# [OR]
#------------------------------------------------------------
### Learning ###
w = [-0.01  0.01  0.01]

### Test ###
[1 0 0] -> -1
[1 0 1] -> 1
[1 1 0] -> 1
[1 1 1] -> 1

#------------------------------------------------------------
# [XOR]
#------------------------------------------------------------
### Learning ###
w = [ 0.   -0.01  0.  ]

### Test ###
[1 0 0] -> 1
[1 0 1] -> 1
[1 1 0] -> -1
[1 1 1] -> -1

予想通り、AND, ORは学習できていますがXORは正しく学習できていません(XORは線形分離不能)。次回は隠れ層を増設した多層パーセプトロン(Multi Layer Perceptron, MLP)でXORも含めて学習してみたいと思います。

*1:バッチ学習だとリンク先の式をそのまま使うことはできないと思われるので、リンク先にある通りオンライン学習としました。

*2:実は数回で学習は終わっていますが、収束判定とかはとりあえず考えないことにします。

*3:重み更新式より、初期重みが0なら最終的な重みはηの定数倍となり、η自体は出力結果に影響しない。ちゃんとした本にはこの辺の証明も書いてあるらしいです。