pybrainでニューラルネットワーク入門
勉強しつつ書いてみる。微妙な知識で書いてるので、おそらく間違ったことをたくさん書いてる。
まあせめて初学者らしく、初学者に通じるように平易な言葉で!
やりたいこと
関数(モデル)に乱数を与えて生成した訓練データから、元の関数の振る舞いを模倣(近似)できるようにする。
pybrain
Pythonで扱えるニューラルネットワークのライブラリ、だそうで。
ギッハブからインスコ
$ git clone git://github.com/pybrain/pybrain.git $ cd pybrain $(sudo) python setup.py install
参考: 映像奮闘記: PyBrain - a modular Machine Learning Library for Python
概要
バックプロパゲーション(誤差伝搬法)
入力層 - 隠れ層 - 出力層の三層からなり、入力層のノードは隠れ層のすべてのノードへ、隠れ層のノードは出力層へすべてのノードへ辺を持つ有向グラフである。
ニューラルネットワークでは、各ノードから次のノードへ、どれぐらい刺激が伝わるか、という「重み」の係数を持っている。
入力値と、期待される結果をペアを訓練用データとして持っておく。
訓練用の入力値から、期待される出力値と実際の出力値の差(二乗平方和)を算出し、各ノードへの重みを修正する。(誤差伝搬)
超大雑把な使い方
この入力値があったら、こういう結果が欲しい、っていうペアを用意する。
ニューラルネットワークは、入力値が与えられるたびに、その関数の振る舞いに近くなるように変化する。
つまり、よくわかんないモデルを、よくわかんないまま生成できる!(かもしれない)ってこと
今回使う関数
def f(x,y): return sin(x)-cos(y)+random()/10
random()/10のノイズを混ぜてある
今回は入力2、出力1。隠れ層を4とする。(適切な隠れ層の数の設定は、どうすべきなんだろう)
nn.py
#!/usr/bin/env python from pybrain.datasets import SupervisedDataSet from pybrain.tools.shortcuts import buildNetwork from pybrain.supervised import BackpropTrainer from math import sin,cos from random import random def f(x,y): return sin(x)-cos(y)+random()/10 def make_sample(params,f): inc = len(params[0]) data = SupervisedDataSet(inc,1) for p in params: data.addSample( p , f(p[0],p[1]) ) return data hidden_layer_count = 4 sampledata_count = 1000 training_count = 3 checkdata_count = 10 learningrate = 0.01 momentum = 0.99 verbose = True # data trainer_data = make_sample([ [random(),random()] for _ in range(sampledata_count)] , f) checkdata = make_sample([ [random(),random()] for _ in range(checkdata_count)], f) # training network = buildNetwork(trainer_data.indim, hidden_layer_count , trainer_data.outdim,recurrent=True) trainer = BackpropTrainer(network, trainer_data, learningrate = learningrate, momentum = momentum, verbose = verbose) for epoch in range(0,training_count): trainer.train() trainer.testOnData(checkdata, verbose= verbose)
実行結果
Total error: 0.335624543641 Total error: 0.0493996072757 Total error: 0.0110288218894 Testing on data: out: [ -1.108] correct: [ -0.898] error: 0.02208513 out: [ -0.453] correct: [ -0.462] error: 0.00003415 out: [ 0.062] correct: [ 0.186] error: 0.00776188 out: [ -0.979] correct: [ -0.898] error: 0.00321050 out: [ -0.421] correct: [ -0.459] error: 0.00076002 out: [ -0.339] correct: [ -0.314] error: 0.00031656 out: [ -0.606] correct: [ -0.503] error: 0.00526324 out: [ -0.585] correct: [ -0.636] error: 0.00130296 out: [ -0.237] correct: [ -0.168] error: 0.00234576 out: [ -0.143] correct: [ -0.142] error: 0.00000025 All errors: [0.022085132618168034, 3.4146022027572035e-05, 0.007761880400884766, 0.0032104954404822274, 0.00076002279203014802, 0.00031655778021079389, 0.0052632400392830254, 0.0013029642051017734, 0.0023457648422505278, 2.5128484847378107e-07] Average error: 0.00430804554253 ('Max error:', 0.022085132618168034, 'Median error:', 0.0023457648422505278)
これは訓練用データを1000個用意して、3回づつ訓練してみる例。
ご覧のとおり、outとcorrectでだいたい同じような値で対応して、それっぽいモデルが生成されてる。(ちゃんと信頼区間で検定すべきなんだろうけど、面倒くさい)
check_data と trainer_data は同じ関数から生成されたデータではあるけど、trainer は 訓練用データから生成された関数を内部に持ってて、check_data と trainer_dataに直接の関係はない。が、試しに10個のテストデータを突っ込んでみると、誤差が少ないので嬉しいね、ってことになってる。
元のモデルに乱数が存在するので、確実な模倣は不可能にしているのだけど、実際に扱うデータはもっとノイズまみれなのでこんなに上手くいく例は、実際にはほとんどない。
応用
手元の「ゲーム開発者のためのAI入門」によると、アクションゲームにおけるモンスターの接近/逃走(入力層:味方HP/敵HP)、とか、ゲームだと状況が与えられて何らかの判断に用いることが多い、らしい。
今適当に思いついたのだけど、Twitterの誰かの発言を教師データに、単語の出現傾向(どうやってパラメータを抽出する?)からその人の発言かどうか判定する、なんてこともできそう。Twitterのログデータは大量に持ってるので、あとでやってみる。
リカレントネットの学習法と応用: オートマトンの抽出 http://ds9.jaist.ac.jp:8080/ResearchData/sub/99/kobayashi/exsub3.html