Location : Home > Languages > Perl > Package
Title : AI::NNEasy
Toolbox Logo

名称

 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}

オプション

  • networktype:
    The type of the ニューラルネットワークの型。現在は 'feedforward' のみ受け付ける。
  • random_weights:
    初期重みの最大値。
  • learning_algorithm:
    ニューラルネットワークの学習アルゴリズム。'backprop' と 'reinforce' のみを受け付ける。
  • learning_rate:
    learning_algorithm. 出用いる率。
  • bias:
    true に指定すると BIAS ノードを生成する。[0,0] のような NULL 入力を指定するとき有用。

 以下に使用例を示す。

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 があって、本モジュールで使用できるサンプルが含まれている。


INLINE C

 本モジュールのいくつかの関数は 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

 モジュールを高速化するために 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 本体と同等の条件で修正/再配布してもよい。

Toolbox Logo
Updated : 2008/07/17