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

Embedded Probabilistic Programming

Embed Size (px)

Citation preview

Page 1: Embedded Probabilistic Programming

Embedded Probabilistic Programming

~埋め込み DSL による確率プログラミング~

2010/6/30

Page 2: Embedded Probabilistic Programming

濡れた芝生

雨が降る確率: 30%

スプリンクラーが動く確率: 50%

雨が降っても濡れない確率: 10%

スプリンクラーが動いても濡れない確率: 20%

何か別の理由で濡れる確率: 10%

芝生が濡れているときに、雨が降った確率は?Pr (rain | grass_is_wet)

Page 3: Embedded Probabilistic Programming

ふつうにプログラミング

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()

Page 4: Embedded Probabilistic Programming

確率分布 DSL

こんな DSL を用意する。( Haskell )

type Prob = Floatdata PM adist :: [(Prob, a)] -> PM acon :: PM Bool -> PM Bool -> PM Booldis :: PM Bool -> PM Bool -> PM Boolif_ :: PM Bool -> PM a -> PM a -> PM alet_ :: PM a -> (PM a -> PM b) -> PM b

Page 5: Embedded Probabilistic Programming

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 []))))

Page 6: Embedded Probabilistic Programming

確率を木構造で表現

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.20.8

0.10.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.20.8

0.1 0.9

0.20.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

Page 7: Embedded Probabilistic Programming

木探索で DSL を実装(1)

data VC a = V a | C (PV a)type PV a = [(Prob, VC a)]type PM a = PV a

pvUnit :: a -> PV apvUnit x = [(1.0, V x)]

pvBind :: PV a -> (a -> PV b) -> PV bpvBind m f = map g m where g (p, V x) = (p, C (f x)) g (p, C t) = (p, C (pvBind (t f)))

Page 8: Embedded Probabilistic Programming

木探索で 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)

Page 9: Embedded Probabilistic Programming

木の構造から分かること

最低でも 5 段の探索は必要。

10 段以上かかるのは非効率な部分があるから。( pvBind とか)

より効率的な表現ができないか?

Page 10: Embedded Probabilistic Programming

継続渡し形式( Continuation Passing Style / CPS )

継続≒後でやってほしいこと

継続を与えられた関数は、新しい継続を作って下位の関数に渡す。

継続を与えられた値は、その継続を自分自身に適用して返す。

浅井せんせ~!!!

Page 11: Embedded Probabilistic Programming

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)

Page 12: Embedded Probabilistic Programming

限定継続( Delimited Continuation )

shift と reset です

僕が説明するよりは・・・

浅井せんせ~!!!

Page 13: Embedded Probabilistic Programming

限定継続の扱いの違い

OCamllet rec times lst = match lst with [] -> 1 | 0 :: rest -> shift (fun cont -> 0) | first :: rest -> first * times rest

Haskelltimes lst = case lst of [] -> return 1 0 : rest -> shift (\ cont -> 0) first : rest -> (first *) `liftM` times rest

← 戻り値は数値

← 戻り値は継続

Page 14: Embedded Probabilistic Programming

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 ()))

Page 15: Embedded Probabilistic Programming

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)

いろいろ余計なものが付いてる

Page 16: Embedded Probabilistic Programming

Haskell は残念な子なのか

Haskell では

限定継続を使っても

生のデータを扱うことができない

Page 17: Embedded Probabilistic Programming

モナドとは

モナドはクラスの一種

Haskell のクラスは Java や C# のインターフェイス

特定のメソッドを実装すれば、そのクラスのインスタンスになれる

インターフェイスの主な意義は標準化

たいていモナドを使わなくても書ける(ただし IO を除く)

デザパタ厨は、色々なモナドを覚えましょう

モナド専用の記法( do )が用意されている

Page 18: Embedded Probabilistic Programming

モナドの力

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 []

Page 19: Embedded Probabilistic Programming

メモして高速化

コインを n 回投げて、排他的論理和( XOR )をとる。

何も考えないと、高さ n の二分木の探索→ O(2^n)

同じ構造が何度も出てくるので、先に計算して結果を再利用した方が速い。→ O(n)

Page 20: Embedded Probabilistic Programming

メモして高速化

F T F T F T F T FT F T F T F T

T F T FT F T F

T F T F

T F

T F

T

1回目

2回目

3回目

4回目

5回目

N 回までの計算結果を再利用できる

Page 21: Embedded Probabilistic Programming

重み付き選択で高速化

酔っ払いがコインを投げる。( 9 割方は失くす)10 回投げて、すべて表が出る確率は?

正確な値を求めるのが困難なのでサンプリング。→いつまで経っても成功しない。

不都合な値は選ばずに重みを調整する。

Page 22: Embedded Probabilistic Programming

遅延評価で高速化

コインを投げてぜんぶ表が出る確率

事象の発生と観測のタイミングがずれると、非効率になることがある。

ぎりぎりまで観測しなければいい!

Page 23: Embedded Probabilistic Programming

遅延評価で高速化

ぎりぎりまで評価(観測)せず、分からないままにしておく

通常版 遅延評価版

Page 24: Embedded Probabilistic Programming

まとめ

シームレスな DSL を用意するなら OCaml で。副作用万歳!

参照透明な Haskell で限定継続は扱いにくい。でも Haskell には do がある。モナド!

高速化めんどい。

Page 25: Embedded Probabilistic Programming

参考

Embedded Probabilistic Programminghttp://okmij.org/ftp/kakuritu/dsl-paper.pdf

継続を使った Printf の型付けhttp://pllab.is.ocha.ac.jp/~asai/papers/contfest08slide.pdf