Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Embedded Probabilistic Programming

Embedded Probabilistic Programming

B936321bf044487eaf990b605a8826d3?s=128

Ryosuke TAKASHIMA

June 30, 2010
Tweet

Transcript

  1. Embedded Probabilistic Programming ~埋め込みDSLによる確率プログラミング~ 2010/6/30

  2. 濡れた芝生 雨が降る確率:30% スプリンクラーが動く確率:50% 雨が降っても濡れない確率:10% スプリンクラーが動いても濡れない確率:20% 何か別の理由で濡れる確率:10% 芝生が濡れているときに、雨が降った確率は? Pr (rain |

    grass_is_wet)
  3. ふつうにプログラミング OCamlでふつうに書くとこんな感じ。 モンテカルロ法で解くと47%くらい。 let flip p = dist [(p, true);

    (1.-.p, false)] let grassModel = let rain = flip 0.3 and sprinkler = flip 0.5 in let grass_is_wet = flip 0.9 && rain || flip 0.8 && sprinkler || flip 0.1 in if grass_is_wet then rain else fail()
  4. 確率分布DSL こんなDSLを用意する。(Haskell) type Prob = Float data PM a dist

    :: [(Prob, a)] -> PM a con :: PM Bool -> PM Bool -> PM Bool dis :: PM Bool -> PM Bool -> PM Bool if_ :: PM Bool -> PM a -> PM a -> PM a let_ :: PM a -> (PM a -> PM b) -> PM b
  5. DSLで記述した確率モデル 芝生の例はDSLでこんな風に書ける。 grassModel = let_ (flip_ 0.3) (¥ rain ->

    let_ (flip_ 0.5) (¥ sprinkler -> let_ (dis (con (flip_ 0.9) rain) (dis (con (flip_ 0.8) sprinkler) (flip_ 0.1))) (¥ grassIsWet -> if_ grassIsWet rain (dist []))))
  6. 確率を木構造で表現 T T T X T T X T X

    F F X F F X F X F X F X F X 0.1 0.9 0.2 0.8 0.1 0.9 0.1 0.9 0.1 0.9 0.8 0.2 0.9 0.1 0.5 0.5 0.1 0.9 0.2 0.8 0.1 0.9 0.2 0.8 0.9 0.1 0.1 0.9 0.1 0.9 0.8 0.2 0.9 0.1 0.9 0.1 0.9 0.8 0.2 0.1 0.5 0.5 0.3 0.7 1.0
  7. 木探索でDSLを実装(1) data VC a = V a | C (PV

    a) type PV a = [(Prob, VC a)] type PM a = PV a pvUnit :: a -> PV a pvUnit x = [(1.0, V x)] pvBind :: PV a -> (a -> PV b) -> PV b pvBind m f = map g m where g (p, V x) = (p, C (f x)) g (p, C t) = (p, C (pvBind (t f)))
  8. 木探索でDSLを実装(2) dist ch = map (¥(p,v) -> (p, V v))

    ch con e1 e2 = pvBind e1 (¥v1 -> if v1 then e2 else pvUnit False) dis e1 e2 = pvBind e1 (¥v1 -> if v1 then pvUnit True else e2) if_ et e1 e2 = pvBind et (¥t -> if t then e1 else e2)
  9. 木の構造から分かること 最低でも5段の探索は必要。 10段以上かかるのは非効率な部分があるから。 (pvBindとか) より効率的な表現ができないか?

  10. 継続渡し形式 (Continuation Passing Style / CPS) 継続≒後でやってほしいこと 継続を与えられた関数は、 新しい継続を作って下位の関数に渡す。 継続を与えられた値は、

    その継続を自分自身に適用して返す。
  11. CPSでDSLを実装 data VC a = V a | C (PV

    a) type PV a = [(Prob, VC a)] type PM a = (a -> PV Bool) -> PV Bool dist ch k = map (¥(p,v) -> (p, C (k v))) ch con e1 e2 k = e1 (¥v1 -> if v1 then e2 k else k False) dis e1 e2 k = e1 (¥v1 -> if v1 then k True else e2 k) if_ et e1 e2 k = et (¥t -> if t then e1 k else e2 k)
  12. 限定継続(Delimited Continuation) shiftとresetです 僕が説明するよりは・・・

  13. 限定継続の扱いの違い OCaml let rec times lst = match lst with

    [] -> 1 | 0 :: rest -> shift (fun cont -> 0) | first :: rest -> first * times rest Haskell times lst = case lst of [] -> return 1 0 : rest -> shift (¥ cont -> 0) first : rest -> (first *) `liftM` times rest ← 戻り値は数値 ← 戻り値は継続
  14. shift / reset で実装(OCaml) let dist ch = shift (fun

    k -> List.map (fun (p,v) -> (p, C (fun () -> k v))) ch) let neg e = not e let con e1 e2 = e1 && e2 let dis e1 e2 = e1 || e2 let if_ et e1 e2 = if et then e1 () else e2 () let reify0 m = reset (fun () -> pv_unit (m ()))
  15. shift / reset で実装(Haskell) dist ch = shift (¥k ->

    map (¥ (p,v) -> (p, C (k v))) ch) neg = liftM not con = liftM2 (&&) dis = liftM2 (||) if_ et e1 e2 = et >>= (¥t -> if t then e1 else e2) reify0 m = reset (pvUnit `liftM` m) いろいろ余計なものが付いてる
  16. Haskellは残念な子なのか Haskellでは 限定継続を使っても 生のデータを扱うことができない

  17. モナドとは モナドはクラスの一種 HaskellのクラスはJavaやC#のインターフェイス 特定のメソッドを実装すれば、 そのクラスのインスタンスになれる インターフェイスの主な意義は標準化 たいていモナドを使わなくても書ける(ただしIOを除く) デザパタ厨は、色々なモナドを覚えましょう モナド専用の記法(do)が用意されている

  18. モナドの力 do記法を使うと、擬似的に生のデータを扱える grassModel = do rain <- flip_ 0.3 sprinkler

    <- flip_ 0.5 wetByRain <- flip_ 0.9 wetBySprinkler <- flip_ 0.8 wetByOther <- flip_ 0.1 let grassIsWet = wetByRain && rain || wetBySprinkler && sprinkler || wetByOther if grassIsWet then return rain else dist []
  19. メモして高速化 コインをn回投げて、 排他的論理和(XOR)をとる。 何も考えないと、高さnの二分木の探索 → O(2^n) 同じ構造が何度も出てくるので、 先に計算して結果を再利用した方が速い。 → O(n)

  20. メモして高速化 F T F T F T F T F

    T F T F T F T T F T F T F T F T F T F T F T F T 1回目 2回目 3回目 4回目 5回目 N回までの計算結果を再利用できる
  21. 重み付き選択で高速化 酔っ払いがコインを投げる。(9割方は失くす) 10回投げて、すべて表が出る確率は? 正確な値を求めるのが困難なのでサンプリング。 →いつまで経っても成功しない。 不都合な値は選ばずに重みを調整する。

  22. 遅延評価で高速化 コインを投げてぜんぶ表が出る確率 事象の発生と観測のタイミングがずれると、 非効率になることがある。 ぎりぎりまで観測しなければいい!

  23. 遅延評価で高速化 ぎりぎりまで評価(観測)せず、 分からないままにしておく 通常版 遅延評価版

  24. まとめ シームレスなDSLを用意するならOCamlで。 副作用万歳! 参照透明なHaskellで限定継続は扱いにくい。 でもHaskellにはdoがある。モナド! 高速化めんどい。

  25. 参考 Embedded Probabilistic Programming http://okmij.org/ftp/kakuritu/dsl-paper.pdf 継続を使った Printf の型付け http://pllab.is.ocha.ac.jp/~asai/papers/contfest08slide .pdf