Location : Home > Languages > Perl > Package Title : AI::NNEasy |
![]() |
AI::NNEasy - Perl と XS における手軽なコードを用いて異なるタイプのニューラルネットワークを定義・学習・利用
本モジュールの主目的は Perl で簡単にニューラルネットワークを作成することである。
本モジュールは複数のネットワークタイプ・学習アルゴリズム・活性化関数に拡張できるよう設計されている。
アーキテクチャはモジュール AI::NNFlex に基づいており、バグを取り、コードを最適化し、いくつかの XS 関数を追加し、学習プロセスを高速化した。ニューラルネットワークを生成し使用するためのインタフェースを開発し、結果に勝利したアルゴリズムを追加するようにした。
NN module on Perl 上で稼動する異なるニューラルネットワークモジュールをテストしてこのモジュールを書いたが、Linux と Windows で簡単に動作して、実際の問題を解けるようにもした。
本モジュールを使えば、ユーザがニューラルネットワークのことをあまり詳しく知らなくてもこれを構築することができ、入力を学習させ、使うことができる。
XOR を計算するためにニューラルネットワークを利用した例がある。
use AI::NNEasy ; ## 出力計算用の最大誤差 my $ERR_OK = 0.1 ; ## ニューラルネットワークを生成する。 my $nn = AI::NNEasy->new( 'xor.nne' , ## ニューラルネットワークをファイルに格納 [0,1] , ## ニューラルネットワークの出力型 $ERR_OK , ## 最大誤差 2 , ## 入力数 1 , ## 出力数 [3] , ## 隠れレイヤ(3つのノードをもつ1つの隠れレイヤ) ) ; ## 学習すべき入力と出力の集合 my @set = ( [0,0] => [0], [0,1] => [1], [1,0] => [1], [1,1] => [0], ); ## 集合に対し実際の誤差を計算 my $set_err = $nn->get_set_error(\@set) ; ## 誤差が最大誤差より大きければ学習を終了 if ( $set_err > $ERR_OK ) { $nn->learn_set( \@set ) ; ## ニューラルネットワークを保存 $nn->save ; } ## ニューラルネットワークの利用 my $out = $nn->run_get_winner([0,0]) ; print "0 0 => @$out\n" ; ## 0 0 => 0 my $out = $nn->run_get_winner([0,1]) ; print "0 1 => @$out\n" ; ## 0 1 => 1 my $out = $nn->run_get_winner([1,0]) ; print "1 0 => @$out\n" ; ## 1 0 => 1 my $out = $nn->run_get_winner([1,1]) ; print "1 1 => @$out\n" ; ## 1 1 => 0 ## または @set 全体に対し適用 for (my $i = 0 ; $i < @set ; $i+=2) { my $out = $nn->run_get_winner($set[$i]) ; print "@{$set[$i]}) => @$out\n" ; }
new ( FILE , @OUTPUT_TYPES , ERROR_OK , IN_SIZE , OUT_SIZE , @HIDDEN_LAYERS , %CONF )
FILE
ニューラルネットワークを保存するファイルパス。デフォルトは 'nneasy.nne'。
@OUTPUT_TYPES
ニューラルネットワークの持つ出力の配列。 ニューラルネットワークはこのリストの中における最も近い値を出力として与える。
ERROR_OK
計算された出力の最大誤差。
ERROR_OK が未定義の場合は @OUTPUT_TYPES における最小差を 2 で割った値が設定される。
@OUTPUT_TYPES = [0 , 0.5 , 1] ; ERROR_OK = (1 - 0.5) / 2 = 0.25 ;
IN_SIZE
入力サイズ(入力レイヤにおけるノードの数)。
OUT_SIZE
出力サイズ(出力レイヤにおけるノードの数)。
@HIDDEN_LAYERS
隠れレイヤのサイズのリスト。デフォルトでは1つの隠れレイヤを持ち、サイズは (IN_SIZE + OUT_SIZE) で計算される。そのため2つの入力・1つの出力のニューラルネットワークでは3ノードを持つ。
%CONF
ニューラルネットワークのパラメータを定義する。
デフォルト値
{networktype=>'feedforward' , random_weights=>1 , learning_algorithm=>'backprop' , learning_rate=>0.1 , bias=>1}
オプション
以下に使用例を示す。
my $nn = AI::NNEasy->new( 'xor.nne' , ## ニューラルネットワークをファイルに保存 [0,1] , ## ニューラルネットワークの出力型 0.1 , ## 出力の最大誤差 2 , ## 入力数 1 , ## 出力数 [3] , ## 隠れレイヤ {random_connections=>0 , networktype=>'feedforward' , random_weights=>1 , learning_algorithm=>'backprop' , learning_rate=>0.1 , bias=>1} , ) ;
上記と同等なニューラルネットワークを生成する例を記す。
my $nn = AI::NNEasy->new('xor.nne' , [0,1] , 0.1 , 2 , 1 ) ;
load
過去に保存されたニューラルネットワークをロードする。
save
Storable を用いてニューラルネットワークをファイルに保存する。
learn (@IN , @OUT , N)
入力を学習する。
@IN
1つの入力の値。
@OUT
上記の入力に対する出力。
N
この入力が学習すべき回数。デフォルトは 100。
例えば以下のように使う。
$nn->learn( [0,1] , [1] , 10 ) ;
learn_set (@SET , OK_OUTPUTS , LIMIT , VERBOSE)
出力をある誤差になるまで入力集合を学習する。
@SET
入力と出力のリスト。
OK_OUTPUTS
誤差計算時に OK と見なす出力の最小数。
デフォルトでは OK_OUTPUTS は @SET における異なる入力の数と同数。
LIMIT
学習繰り返し回数の上限。デフォルトは 30000。
VERBOSE
TRUE に指定すると学習時に経過を表示する。
get_set_error (@SET , OK_OUTPUTS)
ニューラルネットワークにおける実際の誤差を取得する。得られた誤差が new() で定義された ERROR_OK よりも大きければ再学習すべきである。
run (@INPUT)
入力を与え、学習済みのニューラルネットワークに基づいて計算された出力を返す。
run_get_winner (@INPUT)
run() と同様であるが、 new() で定義された @OUTPUT_TYPES により最も近い出力を返す。
例えば入力 [0,1] が学習により出力 [1] を返す場合、実際には 1 ではなくて 0.98324 のような値を返す。誤差は完全には 0 には鳴らないからである。しかし run_get_winner() により run() の出力、例えば 0.98324 のような出力を取得するがこの場合は最も近い値 1 に設定する。出力 [0] に対し run() は 0.078964 のような値を返すが、run_get_winner() は 0 を返す。
リリースしたソースの中にはディレクトリ ./samples があって、本モジュールで使用できるサンプルが含まれている。
本モジュールのいくつかの関数は C で書かれた関数を Inline で利用できる。
以下のように使う。
AI::NNEasy::_learn_set_get_output_error AI::NNEasy::NN::tanh AI::NNEasy::NN::feedforward::run AI::NNEasy::NN::backprop::hiddenToOutput AI::NNEasy::NN::backprop::hiddenOrInputToHidden AI::NNEasy::NN::backprop::RMSErr
入力から高速に学習させたいときには有用であり、同時に柔軟なニューラルネットワークを利用できる。
モジュールを高速化するために Class::HPLOO を、特に XS サポートの部分を利用している。
Class::HPLOO により Perl クラス用の構文を可能にする。
class Foo { sub bar($x , $y) { $this->add($x , $y) ; } sub[C] int add( int x , int y ) { int res = x + y ; return res ; } }
これのおかげでモジュールを2日間で作成できた! ;-P
これはニューラルネットワークとは何であり、どのように動作するかを多くの本を読まずに理解してもらうためのものである。
ニューラルネットワークはノード/ニューロンとレイヤ(入力レイヤ・隠れレイヤ・出力レイヤ)から成る。
例えば入力が2つ、隠れレイヤが1つ、出力が2つのニューラルネットワークを考える。
Input Hidden Output input1 ---->n1\ /---->n4---> output1 \ / n3 / \ input2 ---->n2/ \---->n5---> output2
入力 [0,1] があったとしよう。n2 がアクティブであったとすると n3 を活発化させ、さらに n3 は n4 と n5 を活発化させるとする。しかし n3 と n4 との間にはある重み(weight)があり、n3 と n5 の間にも他の重みが与えられているものとする。ノードの間の重みを求めるには実際の出力に近い出力を与える必要がある。[0,1] の出力が [1,1] であれば、ノード output1 と output2 は 1 に近い値、例えば 0.98654 である必要がある。[0,0] の出力が [0,0] であれば output1 と output2 は 0 に近い値、例えば 0.078875 でなけれならない。
ニューラルネットワークにおいて難しい点は、重みを見つけることである。デフォルトでは AI::NNEasy は学習アルゴリズムとして backprop を用いる。ニューラルネットワークを通じて入力をペーストし、重みが正しい出力を与えるまで乱数を使って重みを調整する。
ニューラルネットワークの秘密は隠れレイヤと各レイヤのノード/ニューロンである。
隠れレイヤを定義する最良の方法は (INPUT_NODES + OUTPUT_NODES) の1レイヤである。
2つの入力ノードと1つの出力ノードを持つレイヤは隠れレイヤに3つのノードを持たねばならない。
この定義は入力の数がにゅうりょくの最大変動を定義するからである(ブール代数では N**2)。XOR の例のようにいくつかの論理的な制約により変数が削減されていれば出力は2つの入力・1つの出力に対し隠れレイヤには3ノードでよい。論理的には入力の3つのグループが必要である。
0 0 => 0 # 偽 0 1 => 1 # or 1 0 => 1 # or 1 1 => 1 # 真
実際にはこれは真実の説明ではなく、問題に対し正しい出力を出すための隠れレイヤにおけるノード/ニューロンの数を指定する必要があることを理解してもらうための方法である。
ニューラルネットワークにおけるその他の重要難な点は偽を学習することである。ニューラルネットワークを通じて入力の集合を取得し、それらをペーストして正しい結果が得られるまで計算を行う。このプロセスは実際の出力と十分に近くなるまで行われる。
他の重要な概念としてはニューラルネットワークにおける入力は 0 と 1 の間の値でなければならないことである。そのため次のようになる。
0 0 => 0 0 0.5 => 0.5 0.5 0.5 => 1 1 0.5 => 0 1 1 => 1
しかし本当に推奨されるのは常に 0, 1 のようなブール値を用いることである。これにより学習は高速になり、複雑な問題にも対処できるだろう。
AI::NNFlex, AI::NeuralNet::Simple, Class::HPLOO, Inline
Graciliano M. P. <gmpassos@cpan.org>
どんな形のフィードバック(意見や示唆を含む)も感謝する。 ;-P
AI::NNFlex の著者である Charles Colbourn <charlesc at nnflex.g0n.net> に謝意を表明する。NNFlex が私のニューラルネットワークの出発点であって、その後に AI::NNEasy の開発に着手した。
本プログラムはフリーソフトウェアであり、Perl 本体と同等の条件で修正/再配布してもよい。
![]() |
Updated : 2008/07/17 |