具有自由Monads的概率编程语言的建模观察



我正在尝试为受Jared Tobin博客文章启发的概率编程实现DST。

包含伯努利和贝塔发行版的DST的免费monad可能如下所示:

data ModelF r =
BernoulliF Double (Bool -> r)
| BetaF Double Double (Double -> r)
deriving (Functor)
type Model = Free ModelF
bernoulli :: Double -> Model Bool
bernoulli p = liftF (BernoulliF p id)
beta :: Double -> Double -> Model Double
beta a b = liftF (BetaF a b id)

我想扩展它以支持对分布的观察。即:强制特定的返回值。

第一次尝试是扩展函子:

data ModelF r =
BernoulliF Double (Bool -> r)
| BetaF Double Double (Double -> r)
| ObsF (ModelF r) r
deriving (Functor)

但是,这是有问题的,因为它允许ObsF的递归嵌套。

一种解决方案是将自由单子提升到另一个代表观测值的自由单子中:

data ObsModelF r =
PureF (Model r)
| ObsF (Model r) r
deriving (Functor)
observe :: Model r -> r -> Free ObsModelF r
observe model o = liftF (ObsF model o)

我所追求的语法是:

let dist = do
p <- beta 1 1
observe (bernoulli p) True

这与ObsModelF不兼容,因为beta 1 1也应该被提升到ObsModelF。这需要两个单独的构造函数:一个用于没有观测值的分布,另一个用于具有观测值的分布。是否有可能避免 DST 中的这种复杂性?


第三次尝试。这次我将分布函子 (ModelF) 包装在观察函子中,然后我提起 (ObsModelF)。 我得到一些不太理解的类型错误(见评论):

data ModelF r =
BernoulliF Double (Bool -> r)
| BetaF Double Double (Double -> r)
deriving (Functor)
data ObsModelF r =
PureF (ModelF r)
| ObsF (ModelF r) r
deriving (Functor)
type ObsModel = Free ObsModelF
bernoulli :: Double -> ObsModel Bool
bernoulli p = liftF . PureF $ BernoulliF p id
beta :: Double -> Double -> ObsModel Double
beta a b = liftF . PureF $ BetaF a b id
observe :: ObsModel r -> r -> ObsModel r
observe f o = liftF $ case f of
(Free f') -> case f' of
(PureF f'') ->  ObsF f'' o   -----| Couldn't match expected type ‘Free ObsModelF r’
(ObsF f'' o') ->  ObsF f'' o    --| with actual type ‘r’
toSampler :: (RandomGen g) => ObsModel r -> State g r
toSampler = iterM $ case
ObsF a o -> case a of 
BernoulliF p f -> return o >>= f  -----| Couldn't match type ‘StateT g Data.Functor.Identity.Identity r’
BetaF a b f    -> return o >>= f     --| with ‘Bool’
PureF a -> case a of 
BernoulliF p f -> MWC.bernoulli p >>= f
BetaF a b f    -> MWC.beta a b >>= f

我包括了一个非常简单的口译员(toSampler)。目前,它完全忽略分布,只是平面映射观测值。请注意,这不是以分配为条件时的预期行为。它只是为了显示解释器将如何遍历自由结构。

我熟悉其他语言/DSL中的类似内容。我不确定我的想法是否正是你所追求的,但我会粗略地勾勒出这个想法。也许你会发现它有帮助。

我熟悉的observe有一个类似Distribution a -> a -> Model ()的类型,它被解释为将当前路径通过模型的概率乘以分布下观测值的概率(/密度)。这个observe需要Distribution,而不是Model,因为我们需要一种方法来计算观测的概率(/密度)。

获得Distribution类型后,还可以将每个分布操作替换为单个操作以引入不确定性。这将以Distribution作为论据。

我有一个Haskell DSL,它在这里几乎以这种方式工作。唯一的区别是observe是用一种称为weight的更简单的操作来实现的,它任意修改当前路径的对数概率。

顺便说一句,这种方法完全从WebPPL语言中提升

这里有一个建议

data ModelF r =
BernoulliF Double (Bool -> r)
| BetaF Double Double (Double -> r)
| ObsFail
deriving (Functor)

然后你可以做

let dist = do
p <- beta 1 1
b <- bernoulli p
if (b==True)
then return ()
else ObsFail

一般来说,我们可以定义

observe dist r = do
r' <- dist
if r == r'
then return ()
else ObsFail

为了采样,我们像往常一样采样,只是每次我们得到ObsFail时都会重新采样。

相关内容

最新更新