確率的プログラミングをかじるべく、dipplのサイトを見る
個人的にはまだやらんでもいいかなぁ、と思ってた確率的プログラミングだが、マストドンの若者に「なんすか?それ」と聞かれたので、答えられるくらいは知っとこうかなぁ、という気になる。
すると、nsharp_2ch氏に、dipplのサイトが一番入門には良いよ、と教えてもらったので、これを軽く読んでいく。
The WebPPL language
introはまぁいいだろう。で、次の
http://dippl.org/chapters/02-webppl.html
を読む。 分布のprimitiveとしてはsampleとscoreらしい。 なんかこれでambサーチみたいな事やるのかなぁ。
自分は昔PGMのコースでOctave で離散の確率モデルをいろいろするような処理系を書く、という課題を結構真面目に解いたので、やってる事は結構似ている気がする。
とりあえず先に進まないと詳細はわからなかろう、と軽く読み進めると、最後のfactorの所は良く分からない。 factorの中をcと足すんじゃ何が駄目なんだろう?
良く分からないが先に進む。
Exploring a random computation
なぜか次はCPSの話が続くが、いまいちモチベーションが分からないのでなんの話をしているかわからなくなる。
まずsampleの正体が知りたいと思うのだが。
とりあえずcpsBinominalまで読む。 これってcpsSampleを適切に実装してやれば、どんどんサンプリングを出来ますよ、という話かね。
Coroutines
この_sampleは何をしているのか? supportは同時確率の事か?
良く分からないが、とりあえず分布に従った値を返すジェネレータと思っておこう。
unexploredFuturesというのに、サンプルされた値を継続に渡して呼ぶ関数をひたすらプッシュしている訳だよな。
supportがどういう実装なのかがいまいち分からないが、ここからpopしてそれをコールすると一回sample を呼んだ時点のcontinuationで一回サンプリングされた値が呼ばれる訳だな。
このunexploredFuturesはどこで呼ぶのだろうか?
exitで呼んてるな。
supportのところは良く分からないが、読み進めてみよう。
値が実現する確率はlog確率の和を取って最後にexpしている。 これでマージナライズが実装出来るのは以前PGMの授業でOctaveで実装した通りだな。
この辺はa, b, cのテーブル見ながら議論する方が分かりやすいと思うのだけど、それを既に終えているという前提なのかねぇ。こりゃ大変やね。
Continuation-passing transform
ここは何を言いたいのかさっぱり分からんな。 コードをパースしてどうCPSスタイルに直すか、という話をしているのかしら?
なんかリンクがあるのでリンク先読むか。
How to compile with continuations
http://matt.might.net/articles/cps-conversion/
なんかschemeでコンパイラ書く、みたいな話だな。昔こういうの見た事ある気がする。
さて、MとTを見る事から始めよう。 Mはラムダの時にkを足すのとbody をTする、それ以外の場合はそのまま引数を返す。 ラムダ式も形は変われどラムダ式なので、CPSの世界での値としてそのまま値が返るイメージだよな。
Tはトランスフォームだろうが、何をするのか。 再帰的な構造になってて、引数がラムダ式かシンボルならcontにMして渡す。 Mはまぁそのまま返すようなものなので、ようするにcontに引数を渡すだけだ。
で、引数がリストの場合はcarとcdrを別々にTして、contの先で渡ってきたf, eを使ってコールしている。 この辺はモナドレベルが大分上がった今見るとどうという事は無いな。
carがf, cdrがeで、それぞれをTの第一引数にする、というのは、再帰の底では結局Mしてcont呼ぶ訳だよな。 で、このcontは今作ってるラムダで、それは渡されてきた物をコールする訳だ。 つまりMした物をコールする訳だね。
少しややこしいが、ぐっとにらめばぱっと分かるレベル。
そのままhigher-order transformまで一応読んだが、この先は要らなさそうな気がするのでひとまず戻る。
Continuation-passing transformに戻る
さて、戻ってきてみると、CpsTransformはTの事を言っている事が分かる。 なんだ、そうならそうと最初から言ってくれよ。
まぁJSで説明しようという試みなのでこういう所が出てきてしまうのだろうなぁ。
その後の変換が試せる奴で中間変数とか作ったりするとなんかバグってるんだが…まぁ見なかった事にして先に進もう。
Best-first enumeration
ここまでは単にコンパイラの教科書の復習みたいな内容だったが、突然確率的プログラミングっぽくなってきた。
なるほど。
で、サンプルのEumerateメソッドの引数を1, 2, 3と変えてみろ、と書いてあるがEnumerate メソッドなんて無いんだが…あぁ、maxExecutions の事か。
ただこの3つの例の意味は全然分からないな。
しばらく数字を変えてたがさっぱり分からん。ちょっとこの例では分かりようが無い気がするのでとりあえず先に進もう。
Early, incremental evidence
良く分かってないが読み進めてみよう。
見慣れたHMMの話が始まる。 factorはエビデンスに相当する物を入れたりするものか。
昔書いてたPGMのスレを見直す。
Probabilistic Graphical Models(pgm)
factorは向こうの用語では確率変数から実数への射影だなぁ。こっちのは違いそうだが。
とりあえずエビデンスを指定する物と思って先に進もう。
hmmRecur
さて、hmmとは何が違うのだろう? もともと向こうも再帰だった。
まず引数にstateが増えているな。 元のでは何が問題だったかというと、やり直しが多すぎる問題だよな、これ。
観測がfalse, false, falseになってる訳だが、これをhmm(3)でナイーブに得ようとすると、何も考えずに先頭からサンプリングしていって、3つの観測を得る。
これがたまたま3つともfalseになるのを、ひたすら引き直して待つ訳だ。 当然このネットワークらだいたいtrueなHMMなので、全然当たらない。
改善案としては、一つの変数がまずfalseになるまでサンプリング、というように、組み合わせでrejectせずに、一変数ずつrejectしたいよな。
もとのhmmでは前の状態というのは、再帰呼び出しで0まで戻らないと得られない。 このコールグラフにstateの前後関係が暗黙に埋め込まれている。
一方hmmRecurは引数にそれを明示的に渡している。
違いは分かったが、これでは別段途中の段階にevidenceが組み込めていないような?
と読み進めると、それは次にやるのか。
factorを各hmmRecurに埋め込む。 うん、これで順番にrejecj出来そうだね。
CFGの方は面倒なので読まない。
sampleWithFactor
ここを見てて思ったのだが、このfactorというのは、掛けるとjoint probabilityになるような何かって事じゃないかね。 MRFのfactorと同じような。
と思って少しググって以下のページを見つける。
https://probmods.org/chapters/03-conditioning.html
なるほど、引数をexpした物が部分確率になるのか。 だから0だと1で、-infiniteだとゼロなのね。
Particle Filter
ここからは応用の話になるのかな? それならそんなに読まなくても良い気がするが。
ちょっと最初の方を見ると、離散じゃないケースはenumerationじゃ解けない、と言ってる。そうやね。
もうちょっと読んで読むか別の文書行くか考えるか。
Gaussian ramdom walkとか
コードを眺めると、ランダムウォークしてるだけに思える。 冒頭の四角のイメージは関係ないのね。
次のSemi-Markovは、運動量を足して、その先でガウス分布させてる。 ガウス分布が無ければ等速直線運動、、、かな。
Likelihood weighting
何をやってるのか良く分からない… まぁfactorの実装をする為に、スコアを保持する、という事をやっていて、 これを何に使うかは次に説明するのかな?
resamplingで使ってて、これで分布に従ったサンプリングが出来そうなのはなんとなくそんな気もするが、良く分かってない。 なかなか難しくなってきたなぁ。
Particle filtersは全然ついていけず
部分確率の所でfactorに合うようにサンプリングを繰り返す、というアイデアは理解出来るのだがらそのコードを読み解く事が出来ず。
難しいねぇ。
軽く次のMCMC も見てみたが、この先を見てもあまり理解してない度合いは変わら無さそうなので、ここまでで一旦この文書は終わりとする。
一回目読んだ雑感
あとで戻ってくるかもしれないので一回目とつけておく。
まずsampleとかfactorと言った基礎となる関数の説明があまり無く、けれど実装方法を語る、という感じなので、それぞれの定義を実装から逆に推測して読む事になる。
それは無駄に複雑だとは思うが、一方でコードがあるのでもやっとしがちな所を頑張ればちゃんと理解出来る、というのは良い。
自分のここまでの理解だと、分布とはJoint Probability Tableみたいな物で、それを組み合わせて確率分布を構築出来る、というのが確率的プログラミングの基礎なのかなぁ、と思った。
単純な算術演算ならそれで良いのだが、 関数とか入ったりした時の挙動はいまいちちゃんとは理解出来てない。
まずWebPPLの入門ドキュメントを先に読みたいなぁ、と思った。
とりあえず他のドキュメントに行ってみよう。
続き。