Моделирование наблюдения для вероятностного языка программирования со свободными монадами

Я пытаюсь реализовать DST для вероятностного программирования, вдохновленный сообщением в блоге Джареда Тобина.

Бесплатная монада для DST, включая дистрибутив Бернулли и бета-версию, может выглядеть так:

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). В настоящее время он полностью игнорирует распределение и просто плоско отображает значение наблюдения. Обратите внимание, что это не является предполагаемым поведением при обработке дистрибутива. Он предназначен только для того, чтобы показать, как интерпретатор будет проходить свободную структуру.


person tmpethick    schedule 22.05.2017    source источник
comment
Я не уверен в семантике, которую вы ожидаете от типа ObsModelF. Не могли бы вы написать переводчику для разъяснения? Кроме того, что не так с обертыванием beta 1 1 с помощью PureF или функции, подобной lift?   -  person Li-yao Xia    schedule 22.05.2017
comment
Я хотел бы observe произвольный дистрибутив. Если бы я поднял весь дистрибутив с помощью Pure (как бернулли, так и бета), мне пришлось бы развернуть бернулли, чтобы снова поднять его с помощью ObsF. Кажется, что много операций для простой задачи. Чего я хочу добиться, так это зафиксировать возвращаемое значение ModelF r, пометив его как наблюдаемое. Я могу попытаться уточнить в теле вопроса.   -  person tmpethick    schedule 22.05.2017
comment
Прочитав сообщение в блоге и еще несколько материалов, я получил некоторое представление о том, что значит observe, но я не могу согласовать это с вашим типом в данный момент, поэтому я попросил переводчика, чтобы посмотреть. Кроме того, обертывание/распаковка обычно может быть довольно легко скрыто от пользователя языка, но проще найти подходящий способ очистки API, если сначала есть работающая реализация.   -  person Li-yao Xia    schedule 22.05.2017
comment
@ Li-yaoXia Я обновил ответ третьей попыткой, включающей переводчик. Я был бы очень признателен, если бы вы могли взглянуть и помочь мне понять, что не так.   -  person tmpethick    schedule 26.05.2017
comment
Вы можете использовать ObsF !(ModelF r) r, если хотите убедиться, что он не вложен бесконечно.   -  person PyRulez    schedule 03.12.2017


Ответы (2)


Я знаком с чем-то подобным на других языках/DSL. Я не уверен, что то, что я имею в виду, это именно то, что вам нужно, но я дам грубый набросок идеи. Может быть, вы найдете это полезным.

observe, с которым я знаком, имеет тип, похожий на Distribution a -> a -> Model (), и он интерпретируется как умножение вероятности текущего пути через модель на вероятность (/ плотность) наблюдения при распределении. Это observe принимает Distribution, а не Model, так как нам нужен способ вычислить вероятность (/ плотность) наблюдения.

Если у вас есть тип Distribution, вы также можете заменить операции по распределению одной операцией для введения неопределенности. Это потребует Distribution в качестве аргумента.

У меня есть Haskell DSL, который работает примерно так здесь. Единственное отличие состоит в том, что observe реализован с точки зрения более простой операции под названием weight, который произвольно изменяет логарифмическую вероятность текущего пути.

Кстати, этот подход полностью взят из языка WebPPL.

person Paul Horsfall    schedule 24.05.2017
comment
Выглядит как действительно хороший порт - я обязательно посмотрю поближе. Вам также следует посетить github.com/adscib/monad-bayes, если вы еще этого не сделали. . Кажется, есть несколько сходств. Также похоже, что они начали добавлять MH с одним сайтом, которого не было в исходной статье. Мой интерпретатор в конечном итоге сопоставит его с MH с одним сайтом, поэтому наблюдение должно исправить logPdf и sample для данного дистрибутива. Я изо всех сил пытаюсь понять, как это можно смоделировать как свободную монаду — не могли бы вы сложить свободную монаду? - person tmpethick; 25.05.2017

Вот предложение

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.

person PyRulez    schedule 03.12.2017