Haskellで確率モデル

  • こちらにも書いたように、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とを「単項」とする関数表現として構成する