Location : Home > Languages > Perl > Package
Title : AI::NeuralNet::Simple
Toolbox Logo

名称

 AI::NeuralNet::Simple - ニューラルネット構築用学習モジュール


概要

use AI::NeuralNet::Simple;
my $net = AI::NeuralNet::Simple->new(2,1,2);
# 論理的 'or' を学習させる
for (1 .. 10000) {
     $net->train([1,1],[0,1]);
     $net->train([1,0],[0,1]);
     $net->train([0,1],[0,1]);
     $net->train([0,0],[1,0]);
}

printf "Answer: %d\n",   $net->winner([1,1]);
printf "Answer: %d\n",   $net->winner([1,0]);
printf "Answer: %d\n",   $net->winner([0,1]);
printf "Answer: %d\n\n", $net->winner([0,0]);

要約

 本モジュールは、人工知能に興味を持っているがやさしい導入が必要な人が設計する簡単なニューラルネットワーク学習ツールでである。
 これはCPAN で現在入手可能なニューラルネットワークのモジュールに取って代わろうというものではない。そのかわりドキュメントは初心者にも分かりやすいように設計されている。


説明

免責事項

 以下の情報は極めて不十分であることに留意すること。これは意図的である。ニューラルネットワークに慣れている人は非常に単純化していることを笑うかも知れないが、明敏な読者であれば答えるよりも問いかけていることに気がつくだろう。
 では、なぜ私はこれをするのか? 誰かがニューラルネットワークで何を行っているかを知るのにこのモジュールを触ることで十分理解できるような情報を提供するためである。興味があればさらに協力にすればよい。

生物学

 ニューラルネットワークとは、最も単純化すれば、脳のデザインを性質の模造品を作ろうとするものである。人工知能の分野の様々な挑戦と同様、多くの問題を解くことができる自然のデザインをあからさまに利用している。幸運にも母なる自然は特許を要請したりしない。
 我々の脳は軸索で互いに結ばれたニューロンで構成されている。軸索はシナプスを通じてニューロンと接続している。ニューロンが情報を受け取ればそれを処理し、この情報を処理する他のニューロンに渡し、筋肉の痙攣や感情を示すよう、映画場で隣の席の人のポップコーンに目をやるなど体の他の部分に指令を送る。

ニューロンの例

 さて、ここで読者には生物学のバックグラウンドあるとして、どのようにニューラルネットワークを模擬しているかを示そう。ネットワークの最も単純な部分はニューロン(ノード、またはニューロード)である。ニューロンを以下のように捉えることもあるだろう。(アスキーアートで示す。)

Input neurons   Synapses   Neuron   Output

                            ----
  n1            ---w1----> /    \
  n2            ---w2---->|  n4  |---w4---->
  n3            ---w3----> \    /
                            ----

(上図は本モジュールのCコードには厳密に対応しているわけではないことに注意すること。しかし考え方を理解するには十分である。これはかなり簡略化して書いてある。)
 上の例では3つの入力ニューロン(n1, n2, n3)がある。これらのニューロンはrons feed whatever output they have through the 3つのシナプス(w1, w2, w3)を通じて出力に向かい、問い n4 を検討する。3るのシナプスにはそれぞれ入力に乗じる「重み」が与えてある。
 ニューロン n4 は以下の式に基づき計算される。

output = 0

foreach (input.neuron)
    output += input.neuron.output * input.neuron.synapse.weight

ouput = activation_function(output)

 この「活性化関数(activation function)」は実際の出力を生成するために入力に適用される特別な関数である。活性化関数には線形関数・シグモイド関数・ハイパボリックタンジェントなど様々なバラエティがある。技術的な理由で AI::NeuralNet::Simple では線形の活性化関数を用いていない。
 本モジュールではシグモイド関数を用いている。(これに関する詳しい情報は参考資料を見るか、ググって欲しい。)
 活性化関数が適用されれば出力は次のシナプスに渡され、w4 を乗じてプロセスは続けられる。

AI::NeuralNet::Simple アーキテクチャ

 本モジュールで使用しているアーキテクチャは(現在では)ニューロンの3層固定レイヤ(入力・隠れ・出力)である。実際、3層ネットワークは多くのニューラルネットワークの問題に適用可能であるが、万能と言うわけではない。本モジュールでは単純化のために3層に固定している。
 ここに "logical or" を学習するための3層のネットワークがある。まず入力と出力の数を定義しなければならない。入力は単純でこの考えを説明するのに必要最小限の2つとした。出力については True と False を示す2つのニューロンを想定した。隠れレイヤは1つだけである。これを図にすると以下のようになる。

          Input   Hidden   Output

input1  ----> n1 -+    +----> n4 --->  output1
                   \  /
                    n3
                   /  \
input2  ----> n2 -+    +----> n5 --->  output2

 output 1 は False に、output 2 は True に対応しているとしよう。1 (または True)が与えられるか input 1 及び input 2 が 1 なら output 2 は True で output 1 は False となる。これを表に示すと以下のようになる。

input   output
1   2   1    2
-----   ------
1   1   0    1
1   0   0    1
0   1   0    1
0   0   1    0

 ここで使うネットワークのタイプはフォワードフィード・エラー・プロパゲーション・ネットワーク(forward-feed back error propagation network)、手短に言えばバックプロパゲーションネットワークである。この動作は単純である。入力があれば隠れレイヤに渡され、出力レイヤに移る。これは「フィードフォワード」部分である。出力が期待する答えとなっているか、及びどれくらい離れているかを比較する。これにより重みを調整していく。
 このプロセスを十分に小さくなるまで繰り返す。実際にはネットワークから正確な答えが得られることはほとんど無い。結果を解釈する様々な戦略を用意することが多い。上の例では「勝者総取り」戦略である。最大の値を示したものが「勝者」となり、それが答えとなる。
 例の辞書は "logical_or.pl" にある。

ネットワークの構築

 新しいニューラルネットワークを生成するには以下の基本的な3段階を踏む。

1. 設計

 レイヤの数とレイヤごとのニューロンの数を選択する。AI::NeuralNet::Simple ではレイヤの数は固定である。
 もう少し複雑なニューラルネットのパッケージでは、活性化関数(activation function)を用いるためにニューロンの学習率を設定することもある。

2. 学習

 エラー率が十分低くなるまでニューラルネットワークを学習させる。データセットが大規模になればエラー率が十分小さくなるまでの繰り返しが多くなる。

3. 結果の計測

 ニューラルネットワークでよくある間違いは、学習データとは異なるデータの試験をするときに起こる。本当は最小化されていないのに「極小値」に落ちた解を正しいと判断してしまうことにより起こる。これは間違った結果をもたらす。この状況になっていないことを確認するには学習後、検証用のデータを利用する。もし結果に不満足であればレイヤごとのニューロンの数を変えて学習をしなおすか、学習データを変えることである。


Programming AI::NeuralNet::Simple

new($input, $hidden, $output)

 new() は3つの整数を引数として取る。これらの数は入力レイヤのノード数・隠れレイヤのノード数・出力レイヤのノード数である。"logical or" ネットワークを生成するには以下のように記述する。

my $net = AI::NeuralNet::Simple->new(2,1,2);

 デフォルトではニューロンに対する活性化関数(activation function)は delta = 1 のシグモイド関数である。

S(x) = 1 / (1 + exp(-delta * x))

 この delta は変更することができる。ハイパボリックタンジェントを使って2極活性化関数 T() を定義することもできる。

T(x) = tanh(delta * x)
tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))

  S() が (0, 1) の間の値しか取らないのに対し、T() は (-1, +1) の間の符号付の値をとるので負の重みも指定できる。

delta($delta)

 現在活性化関数で使用している delta の値を取得するか、またはその値を設定する。delta の値が大きくなれば活性化関数は急勾配になる。引数は厳密に正でなければならない。学習中は変更することはできない。

use_bipolar($boolean)

 ネットワークが2極活性化関数を使用しているか否かを返す。引数が指定することで使用するか否かを指定できる。学習中は変更することはできない。

train(\@input, \@output)

 本メソッドは入力データセットと出力データセットを関連つけるためにとネットワークを学習させる。"logical or" を示すと以下のようになる。

$net->train([1,1] => [0,1]);
$net->train([1,0] => [0,1]);
$net->train([0,1] => [0,1]);
$net->train([0,0] => [1,0]);

 データを通じた1つの経路はめったにネットワークを学習させるには十分ではないことに注意すること。
 "logical or" プログラムの例では1万回の学習を行っている。

for (1 .. 10000) {
   $net->train([1,1] => [0,1]);
   $net->train([1,0] => [0,1]);
   $net->train([0,1] => [0,1]);
   $net->train([0,0] => [1,0]);
}

 ルーチンはネットワークの答えがどれくらい遠いかを示す平均2乗誤差を返す。
 学習セット全体に対する平均2乗誤差を制御するには train_set() を使うほうがよい。そのほうがより少ないメモリで実行できる。

train_set(\@dataset, [$iterations, $mse])

 train と似ていて、本メソッドはデータセット全体を学習させる。
 個々の train メソッドを呼び出すよりは高速である。1つめの引数は入力と出力配列参照の対の配列参照であることが想定されている。
 2つめの引数は集合を学習させる繰り返し回数である。この日キスが指定されなくても iterations() メソッドで(もちろん train_set() を呼び出す前に)指定することができる。デフォルトは 10,000 である。
 3つめの引数は目標とする平均2乗誤差である。これが指定されれば学習は平均2乗誤差がこの値を目指して計算し、この値を下回ると終了する。平均2乗誤差を繰り返しごとに計算することはやや手間がかかるので時間がかかる。

$net->train_set([
   [1,1] => [0,1],
   [1,0] => [0,1],
   [0,1] => [0,1],
   [0,0] => [1,0],
], 10000, 0.01);

 ルーチンは、最後の繰り返しにおける、学習全体にわたる最高の平均2乗誤差(平均2乗誤差ではない)平均2乗誤差を返す。

iterations([$integer])

 正整数を引数として渡せば、本メソッドは train_set が利用する繰り返し数を設定し、ネットワークオブジェクトを返す。引数を指定しなければ設定されている繰り返し数を返す。

$net->iterations;         # 100000 を返す
my @training_data = ( 
   [1,1] => [0,1],
   [1,0] => [0,1],
   [0,1] => [0,1],
   [0,0] => [1,0],
);

$net->iterations(100000) # もっと多くの繰り返し
    ->train_set(\@training_data);

learn_rate($rate)

 本メソッドは、引数なしで呼び出されれば現在の学習率を返す。デフォルト値は .20 である。
 引数は0より大きく1より小さくなければならない。この場合は学習率を設定してオブジェクトを返す。

$net->learn_rate; # 学習率を返す
$net->learn_rate(.1)
    ->iterations(100000)
    ->train_set(\@training_data);

 低い学習率を指定するとネットワークの学習は遅くなるが正確さは向上する。学習率が高ければ学習の正確性は劣り、回答がオーバーシュートする傾向がある。

infer(\@input)

 本メソッドは、入力として配列参照を渡せば、対応する推測結果を配列参照で返す。これらの値は近似値ではあるが、正確な値ではない。たとえば "logical or" プログラムでは以下のような結果を返す。

use Data::Dumper;
print Dumper $net->infer([1,1]);

$VAR1 = [
        '0.00993729281477686',
        '0.990100297418451'
        ];

 2つめの出力は 1 に近く、勝者総取り戦略(winner take all strategy)を採用していればヘルパメソッドは...

winner(\@input)

 本メソッドは推測された結果から最高値を示すインデクスを返す。

print $net->winner([1,1]); # "1" を出力する

 もう少し複雑な例を見たい場合には "examples/game_ai.pl" プログラムを見よ。


エクスポート

 デフォルトではなし。


注意

 これはアルファコードである。非常にアルファである。永遠に私のハードドライブに眠らせているよりもいいだろうと思ってCPANに乗せた。誰かがこれを使って私にパッチを送ってくれることを望む。


To Do


バグ

 おそらくある。


参考資料


著者

 Curtis "Ovid" Poe, ovid [at] cpan [dot] org

 複数ネットワークサポート・継続性・平均平方誤差(MSE; mean squared error)のエクスポート, training until 所与の閾値以下でのMSEまでの学習とアクチベーション関数のカスタマイズをの追加は Raphael Manfredi, Raphael_Manfredi@pobox.com による。


著作権とライセンス

 Copyright (c) 2003-2005 by Curtis "Ovid" Poe

 Copyright (c) 2006 by Raphael Manfredi

 本ライブラリはフリーソフトウェアであり、Perl 本体と同等の条件で修正/再配布してもよい。

Toolbox Logo
Updated : 2008/06/17