Pybrainでsin関数を学習させるサンプル

今までニューラルネットをやるときはC#のライブラリであるNeuronDotNetというのを使っていました。

このライブラリはちゃんとオブジェクト指向的な設計で作られていて、とても使いやすかったのですが、開発があまり活発ではないようなのが残念です。Pybrainも活発ではないものの、定期的にGitHubにコミットされてるのが確認できるので、こっちのほうが将来性はあるかな?と思った次第。

あとは、今回、ちょっと作ってみたいWebアプリがあって、WebならC#よりPythonのほうが楽そうだな。というのもある。

ちなみにライブラリを自分で作るというのは無しの方向で。

インストール

[bash]
$ sudo apt-get install python python-pip python-scipy python-numpy python-numpy
[/bash]

続いてPyBrainをインストール。

[bash]
$ sudo pip install pybrain
[/bash]

ちゃんと入ったかimportして確認。

[bash]
$ python
>>> import pybrain
[/bash]

何か足りなかったらエラーが出る。

sin関数を学習させてみた

コードは以下。

[python]
from pybrain.tools.shortcuts import buildNetwork
from pybrain.datasets import SupervisedDataSet
from pybrain.supervised.trainers import BackpropTrainer
import math
import pylab as plt

if __name__ == '__main__':
# sin curve 0 to 2 PI
samp = [((x,), (math.sin(x),)) for x in map(lambda i: i * 0.01, range(0,628))]

# dimension - input, output
ds = SupervisedDataSet(1, 1)

for x, y in samp :
ds.addSample(x, y)

# input, hidden, output layer
# each 1, 3, 1 neurons
net = buildNetwork(1, 3, 1)
trainer = BackpropTrainer(net, ds)

# training
# if errors < 0.001 then stop
# max training count 20
for e in range(0, 20) :
errors = trainer.train()
if( e % 10 == 0) :
print "epoch%d error:%f" % (e, errors)
if(errors < 0.001) :
break

# plot
plt.plot([net.activate(x)[0] for x, y in ds])
plt.plot([y for x, y in samp])
plt.show()
[/python]

20回学習させた結果が以下。全然フィットしてないですね。

3_20

以下が200回まで学習させた結果。なかなかいい感じになります。

3_200

今度は隠れ層を1層3ニューロンから、20ニューロン、10ニューロンの2層に増やしてみましょう。

[python]
net = buildNetwork(1, 20, 10, 1)
[/python]

めっちゃ直感的で良いですね。今回はリスト内包表記とか、map関数とかも調べて使って見ましたが、分かりやすいし使ってて面白いですね。DropBoxの中の人が、"Python makes me feel good"って言ってたそうですが、まさにそんなかんじです。いいです。Python。

欲を言えば、ラムダ式をもうちょっと簡単に書けるようにして欲しかった。=>とか->とか。

話を戻しまして、2層に増やした結果が以下です。

2010_110

かなり接近しました。しかも、110回で誤差が0.01以下まで収束してしまいました。これは、ニューロンの数が増えて学習能力が向上したという事にほかなりません。ニューラルネットワークはニューロンが増え、中間層が深くなるにつれ学習能力が高くなるということが知られています。が、ただ単に深くすればいいというわけではなく、あまり深いとバックプロパゲーションでうまく学習できないという問題もあります。(教師信号が拡散してしまって情報が失われてしまうのではないかと言われている)

さて、上記のネットワークでもっと学習回数を重ねると最終的に以下のようになりました。

2010_400

ここまで来るとほぼ一致していますね。

では今度は、目標値であるsin波に高周波を加えてみます。

[python]
samp = [((x,), (math.sin(x) + 0.2 * math.cos(x*10),)) for x in map(lambda i: i * 0.01, range(0,628))]
[/python]

これを実行すると、以下のようになります。

filter

どうでしょう?面白いですね。まるでノイズ成分を乗り除くローパスフィルタみたいになってます。人間が直感的に見ても分かりやすいですね。

ニューラルネットはこんなふうに実験的にいろんなことを試して見ることができるのが面白くて気に入っています。