- こちらにも書いたように、Haskellでは、確率変数のための仕組みRVarがある。
- パラメトリック分布からの乱数発生もRVarを用いていることは、こちらや、こちらでも窺い知れる
- さて。このRVarは確率変数の型なのでこちらにも書いたように、確率モデルをRVar a として作成できる
gaussianMixtureModel :: [Double]
-> [(Double, Double)]
-> RVar Double
- こちらで確率的プログラミングを目的とした、DSL(Domain-specific language)をHaskell上に設計しているが、そこではDistというデータ型を作っており、これもRVarを取り込んで確率分布を「変数〜関数」として実装している。RVarとDistとの2つが確率変数の特徴を持つことになったので、Sampleableというクラスを作って、「ランダムサンプリングすることができる対象」というものを定義している
data Dist a where
Pure :: a -> Dist a
Bind :: Dist b -> (b -> Dist a) -> Dist a
Primitive :: RVar a -> Dist a
Conditional :: (a -> Prob) -> Dist a -> Dist a
- この中で、Primitiveの設定により、RVar a という確率変数を、Dist aに変えている。これによって、確率変数・確率分布をいずれもDist a で扱うように統一される
instance Sampleable Dist IO a where
sampleFrom _ (Pure x) = pure x
sampleFrom s (Bind d f) = do
x <- sampleFrom s d
sampleFrom s (f x)
sampleFrom s (Primitive d) = sampleFrom s d
sampleFrom _ (Conditional _ _) = undefined
class Sampleable d m t where
sampleFrom :: RandomSource m s => s -> d t -> m t
-
- というのがSampleable
- dとmとtとを決めると、どういうSampleableかが決まる
- Sampleable Dist IO aの場合には、dがDist、mがIO、tがa
- SampleFormをすると、最終的にIO a ができる(帰る)
- 引数として取るのは、sとd tだが、
- sはIOに包まれたRandomSource
- d tはtのDist
- 結局、ランダムサンプリングするための乱数生成器でIOの性質を被ったものと、Dist(分布)とから、IOできるtを返す関数
- Pure xがDist tなら、x自体しか選ぶものはないから、それが返る。乱数生成器が何であろうと変わらない
- Primitiveなら、それはRVarそのもの(d)から乱択すればよい
- Bindを使うバージョンは、乱数生成器を使って、dに対応する乱数を取り出したうえで、その乱数をfで変換したもの(サポートの異なる(かもしれない)別の変数サポートでの乱択をする。ここが再帰的になっていてい、最終的には、PureかPrimitiveとしてサンプリングができるところまでたどり着く
- もう少しデータタイプ Dist a を見てみる
data Dist a where
Pure :: a -> Dist a
Bind :: Dist b -> (b -> Dist a) -> Dist a
Primitive :: RVar a -> Dist a
Conditional :: (a -> Prob) -> Dist a -> Dist a
-
- Pure a というのは、ある一つの事象aのみからなる、確率変数。PrimitiveはRVar実装されている、単一の確率変数のサポートからなる確率変数
- それ以外のすべての確率変数は、PureとPrimitiveとにBindとConditionalとを使って定義する
- 異なるサポート(台)に確率の情報を伝える仕組みが、 Bind で、確かに、台がbからaに変化している
- 同じサポート(台)で確率の値を変えるには、Conditionalを使う
- さて。これに確率を付与する
prior :: Dist a -> Dist (a, Prob)
prior (Conditional c d) = do
(x,s) <- prior d
return (x, s * c x)
prior (Bind d f) = do
(x,s) <- prior d
y <- f x
return (y,s)
prior d = do
x <- d
return (x,1)
-
- Pureはその事象が確率1で起きるような確率分布
- PrimitiveもRVarという確率変数が重み1で起きるような確率分布
- Conditionalは、すでに作ってある分布に重みを付与してつくり上げる
- Bindは、確率は変えずに、サポートの値を変える
- これらを使ってすべての確率分布をRVarとPureとを「単項」とする関数表現として構成する