エルマンネットシミュレータの遊び方
ファイルのコピー
ANNS の一部であるエルマンネットワークのシミュレータの使い方を説明する。
まず自分のホームディレクトリにファイルをコピーする。 端末エミュレータを起動したら以下のようタイプする。
cp -rp ~asakawa/elman2009 .
この操作により,~asakawaにあるelman2009というディレクトリが自分のホームディレクトリにコピーされる。しミューレータを使うためには,まずこのディレクトリに移動しなければならない。ディレクトリを移動するコマンドは cd であったから,
cd elman2009
を実行しなければならない。
ファイルの説明
次のようなファイルがコピーされていることを確認せよ。
- elman: エルマンネットのシミュレータ
- elman.c: エルマンネットのシミュレータのソースコード
- CSM89.plCheermans, Servan-Scriber, McClelland(1989)のデータ生成スクリプト
- Check.plシミュレーション結果の簡単結果を見るためのスクリプト
- ElmanSimulation.sh: Elman(1991) の行った実験の検証をするためのスクリプト
- Elman_corpus.pl: Elman(1991)の用いたデータを生成するためのスクリプト
- Makefile: エルマンネットのシミュレータをコンパイルするために用いられるメイクファイル
- Usage.html: このファイル
- extract_output.sed: シミュレーション結果から出力層の出力だけを抜き出すスクリプト
- convert_data.pl: シミュレータで用いるデータを生成するためのスクリプト
Elman(1991)の追試
Elman corpus
Elman(1991)は次のような文法に従うコーパスを作成した。
# S ---> NP VI . | NP VT NP . | NP VA (NP) . # NP ---> PropN | N | N RC # RC ---> who VI | who VT NP | who VA (NP) # N ---> boy|girl|cat|dog|boys|girls|cats|dogs # PropN ---> Mary|John # VT ---> chase|feed|chases|feeds # VI ---> walk|live|walks|lives # VA ---> see|hear|sees|hears # note(1) N と V の数が一致していなければならない # note(2) 目的語をとる動詞には制限がある。 # chase, feed は直接目的語がかならず必要であり, # see, hear は目的語をとってもとらなくてもよい。 # walk, live では目的語は不要である。
この文法では関係代名詞はさんだ名詞と動詞との数の一致などが必要なため, 単純な文脈自由文法と言うわけではない。おそらく文脈依存文法が必要である。 このコーパスを作るスクリプトが Elman_corpus.plである。
このコマンドには 3 つの引数が必要である。 第一引数で指定された数だけ文を生成する。 たとえば 10 個の文を生成したければ次のようにする。
./Elman_corpus.pl 10 -seed 0
-seed 0 は乱数の種を指定している。seed が 0 だと現在の時刻を 使って乱数系列の種を与えていることになる。seed の値が同じであれば,たえず同じ 結果が得られる。
結果をファイルに保存したければ,リダイレクトすれば良い。100 個の文からなる データtest.dataを作りたければ,次のようにする。
./Elman_corpus.pl 100 -seed 0 > test.data
convert_data
作成されたデータはテキストファイルであるので,テキストエディット,メモ 帳,Emacs などで読むことができる。このデータを elman net simulator で扱うデータ に変換しなければならない。この変換を行うコマンドがconvert_data.pl である。使い方は以下のようにする
./convert_data.pl < test.data
上と同様に,ファイルに書き出したければ,リダイレクトして
./convert_data.pl < test.data > test.input
とすればよい。以上で入力用のデータファイルはできあがったことになる。 参考までに,できあがったデータフィアルの一部を示すと
### . . boy boys cat cats chase chases dog dogs feed feeds girl girls hear hears john live lives mary see sees walk walks who 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 ### john(16) 59 0.058416 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ### feeds(11) 28 0.027723 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ### dog(8) 27 0.026733 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ### .(1) 200 0.198020
などとなっている。データファイルは一行で一単語(ある時点での入力)となっている。 エルマンネットワークシミュレータは ANNS の一部であるので,データフォーマットは パーセプトロンなどで用いられたフォーマットと共通である。ファイル中に # があると その行末まではコメントと見なされるため処理には影響がない。一行目はコメントである。 2行目からがデータであるが,各列それぞれが一単語を表す。たとえば2行目は16列目 だけ1であり,他はすべて0である。16列目は John を表している。2行目の行末にも そのことがコメントで述べられている。同様に 3 行目は 11 列目が 1 であり,他は すべての 0 である。これが feeds という単語を表している。4 行目は 8 列目が 1 であり dogs の意味である。5 行目はピリオドを表しており,1 列目と 2 列目が 1 である。このようにして,すべての単語を 0, 1 で表現した形にするスクリプトが convert_data.plである。
教師信号データの作成
次に教師信号用のデータを作成しなければならない。教師信号は単語予測課題である ので,次の単語にすれば良いだけである。たとえば boys who Mary chases ... という文であれば,boy のときに who, boys who が与えられたら Mary が 教師信号となり,boys who Mary が与えられたら chases を教師信号とすれば良い。 従って,入力データをコピーして,一行だけずらしたファイルを作れば良い。 まず,コピーして
cp test.input test.teach
テキストエディイとなり Emacs なり,自分の使い慣れたテキストエディタで作業する。 まず先頭の2行目を削除し,最後に 1 行加えれば良い。最後の行に何を持ってくるか 意見の分かれるところかも知れないが,ここでは気にしないことにする。つまり学習する 文章が膨大であれば,最後の一行くらいは曖昧であっても体勢に影響はないと考えるのである。
シミュレーションの実行
以上の操作で入力データファイルと教師信号データができあがったことになる。 シミュレータを起動するには
./elman -hidden 10 -input test.input -teacher test.teach
などとする。ここで引数の意味は,hidden が中間層(従って, 文脈層)のユニット数である。多くすれば収束が容易になるが,時間がかかる。 少なくすれば学習が速くなるが,少な過ぎると学習が成立しない場合がある。
学習結果を保存したければ,リダイレクトを使って
./elman -learn 0.01 -hidden 10 -input test.input -teacher test.teach > test.wgt
などとすれば,学習結果の結合係数行列が test.wgt に書き出される。 上の例では,もう一つのオプションであるlearnも使われている。 これは学習係数である。この値が大きいと学習が速くなるが大き過ぎると安定した解に辿り着かない。 learn を 0.1 や 0.0001 などの値にして試してみること。
学習結果の可視化
エルマンネットをうまい具合にビジュアライズするのは難しいが,ここでは1つの やり方のみを示す。ネットワークの出力全体は標準エラー出力に出力される。 まず,これをリダイレクトしてファイルにする。
./elman -hidden 10 -input test.input -teacher test.teach 2>test.err
上のようにすれば,標準エラー出力が test.err というファイルに 書き出される。このファイルには,さまざまな情報が書かれているので,ネットワークの出力 だけを取り出す必要がある。このためにextract_output.sed という sed スクリプトがある。使い方は再びリダイレクトを用いて
sed -f extract_output.sed test.err > test.out
とすれば,test.outというファイルが出来上がる。 このファイルを見ただけでは,どのような学習がなされたのかを知ることが難しい。 そこで,Check.pl というスクリプトを用いると,ごく簡単な 結果が分かる。使い方は取り出してきた出力ファイルを -results 引数で指定し,データ作成で用いたファイルを標準入力から与える。
./Check.pl -results test.out < test.data
この結果の読み方は,例えば出力結果が以下のようになっていたとする。
john chases dogs(mary) . john(cats) who chase(live) chase cat(boy) . john(girls) who chase(live) live(hear) john . john(girls) who(chase) boy . john(girl) who feeds(hears) feeds mary . john(mary) feeds(lives) . john chases dogs(cats) . john chases(hears) dogs(boys) .(who) chases
一番最初の John はネットワークが正しく予測できたことを意味している。2番 目の単語 chases も正しい。その次の単語はネットワークは dogs と予測した が正解は Mary であったことを示している。すなわちこの出力結果は,ネット ワークの予測が正しければ,その単語を出力し,誤っていればカッコないに正 しい単語が示されている。この例では dogs と Mary の混同であるから,品詞 は合っていたことになる。動詞を予測すべき時には動詞を,名詞がくる位置に は名詞を,となっていればネットワークは正しい統語範疇を学習したといえる わけである。
まとめて一括処理
以上でおもなプログラムとスクリプトの実行方法が説明されたことになる。上 記の操作を一括して行わせるために, ElmanSimulation.shというスクリプトが用意されている。 このスクリプトの使い方は,単に
./ElmanSimulation.sh
として実行するだけである。このスクリプトをエディタで編集すれば良い。 このスクリプトの先頭に以下のような記述がある
#!/bin/sh N=10 SENTENCES=10000 TO=5000 HIDDENS=10
これは,シミュレーションを N=10回繰り返すことを指定している。 この値を 100 なり 1000 なりにすれば,その回数だけシミュレーションを自動的に実行してくれる。 SENTENCES=10000は学習すべき文が10000であることを示している。 HIDDENS=10では中間層(従って文脈層)のユニット数を指定して いる。これらのパラメータを書き換えて実行させれば,自動的に望む回数だけ シミュレーションが実行される。結果はresults_で始まるファイル名に 格納される。
その他のサンプルデータ
CSM89
CSM89.plは,Cleeremans, Servan-Schriber, McClelland (1989)の用いた人工文法に従う例文を生成するスクリプトである。

使い方は Elman_corpus.pl と一緒で,生成すべき文の数と乱数系列の種を与える。
./CSM89.pl 1000 -seed 0
とすれば,1000個の例文を生成してくれる。繰り返しになるが,データとして保存したければ リダイレクションを用いて
./CSM89.pl 1000 -seed 0 > csm89.data
などとすればよい。このできたファイルに対して convert_data.pl すれば入力データが出来上がり,入力データをコピーして一行削ったファイルが教師信号データとなる。それらを用いてシミュレータに実行させればシミュレーションができあがる。 ここまでのサンプルオペレーションを以下に示す。
./convert_data.pl < csm89.data > csm89.input cp csm89.input csm89.teach ----- ここでエディタを用いて csm89.teach を編集 ---- ./elman -to 50000 -hidden 5 -input csm89.input -teacher csm89.teach 2>csm89.err sed -f extract_output.pl csm89.err > csm89.out ./Check.pl -results csm89.out < csm89.data
ここで,シミュレータelmanの引数として-to 50000 として学習回数の上限を設定している。シュミレーションがうまく行かない場合には このパラメータを調整してみると良い。
recursion.state
単純な再帰を試してみるサンプルデータがrecursion.stateである。
1 2 1 . 1 1 2 2 1 1 . 1 1 1 2 2 2 1 1 1 . 1 1 1 1 2 2 2 2 1 1 1 1 . 1 1 1 1 1 2 2 2 2 2 1 1 1 1 1 .
このデータはすなわち,1 が 1 回以上繰り返して,その繰り返しの回数だけ 2 が続き,次に 同じ回数だけ 1 が続くというデータである。
このファイルを,./convert_data.plにかければ, 入力データが出来上がり,それをコピーして編集すれば教師信号データになり, シミュレータに実行させることができる。サンプルオペレーションを以下に示した。
./convert_data.pl < recursion.state > recursion.input cp recursion.input recursion.teach ---エディタで recursion.teach を編集--- ./elman -hidden 10 -input recursion.input -teacher recursion.teach 2>recursion.err sed -f extract_output.sed recursion.err > recursion.out ./Check.pl -results recursion.out < recursion.state
同様にしてrecursion2.stateは1の数と2の数とが同じになるタイプの規則である,
1 2 . 1 1 2 2 . 1 1 1 2 2 2 . 1 1 1 1 2 2 2 2 . 1 1 1 1 1 2 2 2 2 2 . 1 1 1 1 1 1 2 2 2 2 2 2 . 1 1 1 1 1 1 1 2 2 2 2 2 2 2 .
正しく文末のピリオドが予測できるかどうかがシミュレーションのポイントである。
最後に日本語の文を学習させてみよう。sentences.txtが元データである。 これを./convert_data.plにかけてデータファイルを作成し, データファイルをコピーし,編集して教師信号データを作成し, ./elmanにかけてシミュレーションを実行し, その結果を取り出して./Check.plを実行して結果を確認してみよ。
サンプルオペレーションを以下に示す。
./convert_data.pl < sentences.txt > sentences.input cp sentences.input sentences.teach ./elmn -learn 0.01 -hidden 10 -input sentences.input -teacher sentences.teach 2>sentences.err sed -f extract_output.sed sentences.err > sentences.out ./Check.pl -results sentences.out < sentences.txt | nkf --utf8