確率的プログラミングその2、probmods
dipplのサイトを一通り見たが良く分からなかったので、WepPPL自体の入門文書が無いもんかなぁ、と本家を見たら、以下のサイトを紹介してた。
という事で軽く読んでみよう。
Inferが出てきた
Inferは何なのか、というのがちょっと分かってくる例だな。 Inferしないと分布は分からない訳か。なるほど。
sampleとInferの違いを十分正確に分かってる訳では無いが、おいおい分かっていく事を期待して進む。
確率の話がだるい
Product rule とかの話はさすがにかったるい。 確率的プログラミングの入門文書の難しさはここだよな。 読者に想定してよい範囲が全然わからない。でも全部最初から説明しようとすると膨大になってしまって読めたもんじゃない。
ちゃんと主流になれば、プログラマが読むべき定番確率本とかが出来て、それを参照して終わるのだろうけれどねぇ。
Stochastic recursion
再帰を特別にこう書いているのは、やはりまずは限られたprimitiveだけサポートされている、という風に話をする方が良いからだよなぁ。 やっぱりこうやって順番にやっていく方が分かりやすくて良いね。
ただかったるい。 最初にこれをやってると単なる関数呼び出しとかにしか見えないので、かえって分からなくなる、という側面はありそう。
Persistent Randomness
さて、分かりやすい入門の体でここまで来てしまったので、この文書だけでここまで来ると、ここでなんで単にフィールドに保存しとくだけじゃ駄目なの?というのがわからなさそう。
ただ、dipplのサイトを見た後なので、この辺のprimitiveが必要なのは分かる。
このあとのExerciseをちらっと見たが、これはほとんど確率のテストであって、知りたいのはここじゃないんだよなぁ、と思った。 ので、先に進む。
Conditioning
conditionの所まで来る。Conditional Distributionsとかの確率の話はまぁさすがに要らないので読み飛ばす。
このページの7割くらいは確率の話なので要らない。 なかなか手頃な入門は難しいねぇ。
今の所enumerateとrejectionの差が分からない。 enumerateって何するの?
observe
最初の例が何故終わらないのか?少し考える。
それはobsXが0.2になるまでサンプリングを続けるが、これってほとんど当たらないから。連続分布だからね。なるほど。
var Dを置くと何が違う?
そのあとvar Dを置くかそのままcondの中に式書くかは全然違う、と書かれてる。 何が違うか全然分からん…
しばらく考えてみたがやはり分からない。 まったく同じにしか感じられないのだが、何が違うのだろう。
observeとconditionの違いを言っているのかな。 conditionはbooleanな条件を記述している。 一方observeはそういう確率変数が観測された、という事を表している。 事象と確率変数の違いか?
まぁいいや、良く分からんけど先に進もう。
directedとundirected
sampleとobserveだけなgenerative modelをdirected、factorがあるとundirectedと言う、と書いてある。
directedとundirectedというとBNとMRFの事っぽい気もするけれど、どうだろう?ちょっと考えてみよう。
factorはMRFのfactor、つまりpotentialと同じだろうか? 向こうのは、それの積をとるとunnormalizeな同時確率になる、という性質が根本にあった。 このfactorはどうだろう?
引数の実数を受け取り、その時の実行が実現するlog確率に足す訳だ。
その時の実行が実現するlog確率、というのは、MRFの意味でのfactorの引数は何になるのだろうか? それはその実数値を求めるのに使った確率変数になる訳だよな。
お、なるほど。MRFのfactorになってるな。 つまりこのdirectedとundirectedはBNとMRFの事で、conditionとsample だとBN、factorが入るとMRF と考えられるのだな。
ただネットワークは明示的には定義されないので結構微妙だが。
Tug warのExampleが良い
ここまで来ると、どういう感じでプログラムするのかは大分分かってくるな。 実際にどう動くのかの詳細は良く分からないが。
その後の診断のコードもサンプルとして良いね。 ただCognitionには大して関心無いのでその辺の話は無い方が嬉しいが。
Patterns of inferenceの手前まで読んで分からない事
Pattern of interestsから先は通常のベイジアンネットワークの話で確率的プログラミングの話はあまり無さそうなので、ここまででこの文書は読み終わりでいいかなぁ。
大分どういう物か理解は深まったが、分からない事が幾つかある。
- 分布に対して許されている演算は何か?
- sampleとは何か?
まず分布に対して全てが許されているのかが良く分からない。 関数の中で触る事は許されていて、その関数が再帰するのが許されているのも分かるが。
例えばif文は許されているのか?とかが良く分からない。 三項演算子とかは許されてそうだけど。
次に分からないのがsample関数。 ぱっと見は何かしらのdeterministicな値を返しそうなんだけど、あってる?いまいちその辺が良く分からない。
他はだいたいどうやってプログラミングするかは理解出来た気がする。 書いたプログラムがどう動くかはまだ良く分かってないが。
追記 : ddiplを見直すと、if文はあるらしい。 というかここにあるのだけが許されてるのね。
結局dependencyのグラフを辿れる必要があるので、何らかの制限は必要と思うが、そこに再帰を許す所が頑張りなのだろう。
追記2: ググってたら以下を見つけた。 http://agentmodels.org/chapters/2-webppl.html
なるほど、flipがsampleしてるのか分布なのかいまいち分かってないのがsampleの良く分からない感じがしてた原因で、
- flipはsampleしてる
- けれど、そのコードを含むthunkからInferで分布が生成出来る
のか。 お、だいたい全てを理解した。 なるほど、こういうものか。
webpplのここまでの理解をまとめる
なお、この理解はあまり自信が無いので、間違ってる可能性も結構あるので注意。
基本的な考え方
確率的プログラミングの入門がやけに難しいのは、単純な動きは無限回の計算が必要になるので、その現実的な実現方法が初期の所で説明されてしまうからだと思う。
だがそもそも確率的プログラミングとは何か?という事を最初に知る時は、無限回実行すると思っておく方が良い。 実際にどう実現しているかはオプティマイズの一種と考える方が良かろう。
まず、分布というオブジェクトがある。 これは実数を分布に従って取り出せる何か、と思っておく。
この分布に対してsampleを呼び出すと、何かしらdeterministicな実数値が帰ってくる。 これに対してプログラミングをするのが基本的な流れとなる。
これだけだと単なる乱数ライブラリ。
で、分布からsampleしてif文とかで何かして結果を返す関数を書く。
引数無しのこうした関数オブジェクトで実数値を返す物をthunkと呼ぶ。
このthunkは何度も呼ぶと、内部の確率分布に従いその都度ちょっと違った結果の実数値が返ってくる。
このthunkをInferに渡すと、何回も空実行して分布テーブルを作って、新しい分布オブジェクトを作って返す。
さらにthunkの中ではcondition(factor とかもそうだが、ここではconditionを代表とする)という関数呼び出しで、結果をフィルタリング出来る。
Inferはthunkを呼び出す時に、内部のcondition文が失敗したらその結果は捨てて、もう一回thunkを呼び出す。
以上が確率的プログラミングの基本となる。
実現方法の肝
さて、conditionではガウス分布の特定の点、とか指定出来る。これは無限回実行しないと当たらない(しかも無限でも可算回ではたぶん駄目だろう)。
だから無限回実行したのと同じ結果をどうにか出している。 そこで重要になってくるのが、thunkの制約。
thunkはイメージ的にはLINQ式みたいな物で、かなり限定的な算術演算と関数の再帰呼び出しくらいしか出来ない事になっている。 この制約のおかげで、処理系はこの中身を解析して、内部のsample呼び出しの箇所とその引数の分布オブジェクトや、その分布オブジェクトとコードとの関係を理解する事が出来て、その分布オブジェクトの内部情報を使って新しい分布を作る事が出来る。
細かい話をするとコンパイラ用語でのbasic blockにそのままlog確率とかを付与していろいろ判断してそう。
分布オブジェクトには、値の現れるlog確率を表す関数があって、こいつを合成する事で、分布同士の算術演算の結果現れる新しい分布のlog確率を返す関数を作り出す。
こいつはイメージとしては分布はjoint probability tableを持っていて(連続値だが)、それをconditionとかを使って潰したりする事で新しいjoint probability tableを作ってる、という感じ。
こんなイメージでは算術演算は良くても関数の再帰はちゃんとは扱えないが、そこは黒魔術が使われていると思って見なかった事にする。
処理系はthunkのコードを解析して分布同士の演算を調べて、算術演算などの関係から分布テーブルを合成する、conditionなどを解析して足をつぶしたりする、そこにいろいろ黒魔術があって単なる算術合成以上の事が出来る、これが入門時の基本的な考え方でいいんじゃないか。