続きです.今回は自らが作成した RNN をテキストファイルに保存したりそこから読み込んで構築したりする実装の紹介です.
まず,RNN の内容を以下のような形で任意のテキストファイルに保存します.
1 #入力層のノード数
2 #中間層のノード数
1 #出力層のノード数
7 #枝の総本数
i 0 c 0 1.0 #以下枝の情報を同じように書き並べる
i 0 c 1 2.0 #入力ノード 0 から中間ノード 0 への重み 1.0 の枝を表す
b c 0 1.0 #バイアスノードから中間ノード 0 への重み 2.0 の枝を表す
b c 1 1.0
c 0 o 0 1.0 #中間ノード 0 から出力ノード 0 への重み 1.0 の枝を表す
c 1 o 0 1.0
b o 0 0.05
(※#で始まるコメントは,実際は書いてはいけない.ここでは説明のために書いてあるだけ)
これで以下の図のような RNN が出来上がります.
そして,恐らく今回一番難解な,そのテキストファイルと RNN 構造の変換の部分の実装を以下に示します.
class Rnn
def initialize(filename)
file = File.open(filename, "r")
@input_nodes = Array.new(file.gets.to_i) { |i|
Node.new("i", i)
}
@central_nodes = Array.new(file.gets.to_i) { |i|
Node.new("c", i)
}
@output_nodes = Array.new(file.gets.to_i) { |i|
Node.new("o", i)
}
file.gets #destroy the information of edge total
@bias = Node.new("b", nil)
@edges = []
file.each { |i|
if i =~ /(([ic])\s+(\d)|b)\s+([co])\s+(\d)\s+(.*)/ then
src_node = nil
if $1 == "b" then
src_node = @bias
else
if $2 == "i" then
src_node = @input_nodes[$3.to_i]
elsif $2 == "c" then
src_node = @central_nodes[$3.to_i]
end
end
dst_node = nil
if $4 == "c" then
dst_node = @central_nodes[$5.to_i]
elsif $4 == "o" then
dst_node = @output_nodes[$5.to_i]
end
edge = Edge.new(src_node, dst_node)
edge.weight = $6.to_f
@edges << edge
end
}
@bias.set_output(-1.0)
end
def save(filename)
file = File.open(filename, "w")
file.puts @input_nodes.length.to_s
file.puts @central_nodes.length.to_s
file.puts @output_nodes.length.to_s
file.puts @edges.length.to_s
@edges.each { |i|
edge = i.src_node.type
if i.src_node.type != "b" then
edge = format("%s %d", edge, i.src_node.id)
end
edge = format(
"%s %s %d %f",
edge, i.dst_node.type, i.dst_node.id, i.weight
)
file.puts edge
}
end
end
@input_nodes, @central_nodes, @output_nodes, @bias, @edges というメンバを持つ Rnn というクラスを作りました.あとは initialize メソッドでテキストファイルの内容を適切に読んでいって,各メンバを適切に初期化するわけです.
(※枝の総本数情報を破棄するのは,不要だからです.ではなぜテキストファイルにそんなものが書いてあるのかというと,そもそもこのテキストファイルの仕様の方が Ruby 版 RNN を作る前の C++ 版で決まっていたからです.そちらでは枝の本数が先駆けて取得出来たほうが楽だったのです.)
save メソッドはその逆で,Rnn クラスの現在の構造を同様の形式でテキストファイルに出力します.
もうこれを詳細に解説するのは不可能に近いかと(^^; 長過ぎて面倒臭過ぎるw
とにかく,これで構造に関する情報のやりとりは整備されました.
あとこれを実際に関数として使うために,中間ノードと出力ノードの reset_calc を全て実行していくメソッドと,入力ノードに値をセットして出力ノードから output を取得するメソッドを実装しなければなりませんが,それはかなり簡単なので次にまわします.
ちなみにテキストファイルの方で構造に関するミス (例 : 出力ノードがない,入力層と出力層が断絶している,存在しないノードに枝が繋がろうとしている,等) があっても,それらの検出はしません.nil 参照が恐らく起こってエラーで止まります.そこのエラー処理まで考えるほど大したものを作っているわけでもないので,省略します (※単に面倒臭いだけというのも大きい).
まぁ仮に作るとしても,テキストファイルのミスを検出した時点で例外を投げるだけで十分だと思いますが (※深く考えずにしている発言なのでそうじゃないかもしれません(^^;).
次回は先ほど言った Rnn クラスを使い物になるようにするためのメソッドの実装と,それのおまけで簡単な入力補助スクリプトの実装を紹介しようと思います.
紀行 (2008-09-17T10:15:24)
か (2008-09-17T12:48:27)
apt は Vine だけってどっかに書いてたのを鵜呑みにしてたっスわ(^^; 情報が古かったのかしら?色んな Linux にあるのね.知らなかったw
か (2008-09-18T08:49:06)
"apt みたいな"
か.やっぱり apt は何にでもあるわけじゃないと.
問題は RubyGems が apt 以外のパッケージ管理ソフトでインストール出来るようになってるかどうかじゃのう (´ω`)
none (2009-12-11T15:33:36)