使用Control.Monad.MonadRandom对正态分布进行采样

时间:2012-12-18 15:44:41

标签: haskell distribution

我想采用给定均值和标准差的正态分布进行采样。我知道如何在各种上下文中执行此操作,如Data.Random.Rvar或Data.Random.MonadRandom。 但是,我的函数的上下文是Control.Monad.MonadRandom,我想保持这种方式,因为我的整个项目使用Control.Monad.MonadRandom。

有没有办法这样做,你能帮我这么做吗?

以下是代码的外观。 Pattern只是Data.Vector的别名Double和Weights是Data.Vector(Data.Vector Double)的别名(即矩阵)

train :: MonadRandom m => [Pattern] -> Int -> m Weights
train pats nr_hidden = do
  ws_start <- ws_start''
  foldM updateWS ws_start pats
    where ws_start'  = take p (repeat $ take nr_hidden $ repeat $ (normal 0.0 0.01))
         ws_start'' = vector2D <$> (sequence $ map sequence ws_start')
         p = length pats

谢谢。

2 个答案:

答案 0 :(得分:6)

快速回答

如何在Data.Random.RVar内使用Control.Monad.MonadRandom

{-# LANGUAGE ScopedTypeVariables #-}

import Control.Monad.Random as CMR
import Data.Random          as DR
import Data.Word (Word32)

gimmeRandom :: forall m . CMR.MonadRandom m => m Int
gimmeRandom = do
  r <- runRVar (uniform 0 100) (getRandom :: m Word32)
  return r

说明

实际上,您希望在具有类似语义的正式不同的 Monad中运行Monad。

  • Data.Random.MonadRandomControl.Monad.Random 正式不同,因为它们是在不同的地方独立定义的,没有一个是另一个的实例(没有instance DR.MonadRandom m => CMR.MonadRandom m或者相反)。
  • Monads具有相似的语义,因为它们都提供来自某些随机源的随机数,因此期望我们可以以某种方式组合它们是有道理的。

我们假设您在Control.Monad.Random界面中有一些代码:

import Control.Monad.Random as CMR

gimmeRandom :: CMR.MonadRandom m => m Int
gimmeRandom = do
  r <- getRandomR (0, 100)
  return r

我们可以像evalRand gimmeRandom StdGen一样运行它,它会给我们Int

现在,您需要使用getRandomR提供的众多可用分发之一而不是Data.Random

对于此示例,我们将尝试将getRandomR (0, 100)替换为uniform 0 100 :: RVar Int。如何在Int环境中从RVar Int获取CMR.MonadRandom

我们希望运行RVar monad,我们可能必须提供随机数源,正如语义所暗示的那样。我们正在为CMR寻找像evalRand这样的monad-escaping函数。这些转义函数的类型为m a -> someStuffNeededToRunTheMonad -> a

docs about RVar中,有一个例子:

-- In a monad, using a RandomSource:
runRVar (uniform 1 100) DevRandom :: IO Int

让我们检查runRVar

runRVar :: RandomSource m s => RVar a -> s -> m a

是的,这是一种逃避功能:给定RVar和随机数源,它会返回我们自己的monad RVarm的随机结果。但是,这需要instance RandomSource m s表示s是我们的monad m的随机源。我们来看看那个例子。

我们的monad m是什么?我们希望在RVar中运行gimmeRandom,因此monad为CMR.MonadRandom m => m(所有实现CMR.MonadRandom的monad)。什么是随机源s?不知道了。 Let us look in the docs存在RandomSource个实例:

RandomSource IO DevRandom
...
Monad m0 => RandomSource m0 (m0 Word32)
Monad m0 => RandomSource m0 (m0 Word64)
...

啊哈!这表示任何 monad m0都是RandomSource的实例,而来自此monad的值(例如m0 Word32)。这当然也适用于我们的monad CMR.MonadRandom。我们还可以看到sm0 Word32必须是随机源生成的随机值。

我们应该在s中作为runRVar (uniform 0 100) s传递什么?在我们的 monad中生成随机数的东西,类型CMR.MonadRandom m => m Word32。生成任意内容的CMR函数是什么,例如一些Word32getRandom。所以基本上我们想写:

gimmeRandom :: CMR.MonadRandom m => m Int
gimmeRandom = do
  r <- runRVar (uniform 0 100) getRandom
  return r

嗯,那不编译:

Could not deduce (RandomSource m (m0 a0))
  arising from a use of `runRVar'
from the context (CMR.MonadRandom m)
  bound by the type signature for
             gimmeRandom :: CMR.MonadRandom m => m Int

RandomSource m (m0 a0)?这很奇怪,mm0似乎被编译器识别为不同的 monad;我们希望它们与RandomSource m0 (m0 Word64)中的相同。

让我们把完整的签名放到那个地方:

r <- runRVar (uniform 0 100) (getRandom :: CMR.MonadRandom m => m Word32)

仍然是同样的错误。这是因为该类型签名中的m实际上是任何实现CMR.MonadRandom monad,而不一定是MonadRandom类型签名中的gimmeRandom

(这与在lambda术语(\x -> (\x -> f x))中的阴影概念相同,其中内部\xf x中使用的;或者是∀x . F(x) → ∀x . G(x)中的一阶逻辑} x中的G(x)是最里面定义的一个,不一定是相同的,甚至不是同一类型,外部∀x中的那个;或者实际上是任何一个内部作用域中具有变量隐藏/阴影的其他编程语言 - 就在这里它是类型变量阴影)。

所以我们要做的唯一事情就是告诉编译器在getRandom调用中,我们不希望它适用于任何MonadRandom,但正是为了{{1}我们在MonadRandom m类型签名中有。

我们可以使用gimmeRandom扩展名

来实现
ScopedTypeVariables

这样,{-# LANGUAGE ScopedTypeVariables #-} [...] gimmeRandom :: forall m . CMR.MonadRandom m => m Int gimmeRandom = do r <- runRVar (uniform 0 100) (getRandom :: m Word32) return r 中的m将从顶级类型签名中选择 getRandom :: m ...完全匹配。

这会编译并解决问题:我们可以使用CMR.MonadRandom m接口在代码中使用Data.Random中的分发。我们可以轻松地用另一个发行版替换MonadRandom

总结,我们有

  • 确定我们使用来自不同包但具有相同/重叠语义的两个不同的monad
  • 了解如何运行/转义我们想要在我们自己内部使用的monad(使用uniform
  • 通过查看类型类限制和为这些限制提供的实例,找到了传递到转义函数的内容
  • 编写了正确的代码(runRVar
  • 通过说明哪个精确的monad runRVar (uniform 0 100) getRandom将使用来编译。

如果您想知道为什么我们可以从我们可以选择的实例中任意选择getRandom,我们只需要以某种形式提供随机源,并且 Word32 < / em>是Word32作为生成其他随机内容的输入之一。

答案 1 :(得分:2)

以下是一些示例代码(低效率)仅使用Control.Monad.Random中的函数从平均mu和标准差sigma生成正态分布的样本。

import Control.Monad.Random

-- |Generates uniform random variables.
unif :: (MonadRandom m) => m Double
unif = getRandomR (0,1)

-- |Generate two samples from the standard normal distribution, using
--  the Box-Muller method.
stdNormals :: (MonadRandom m) => m (Double,Double)
stdNormals = do
    u <- unif
    v <- unif
    let r = sqrt((-2) * log u)
    let arg1 = cos (2 * pi * v)
    let arg2 = sin (2 * pi * v)
    return (r * arg1, r * arg2)

-- |Generate a single sample from the standard normal distribution, by
--  generating two samples and throwing away the second one.
stdNormal :: (MonadRandom m) => m Double
stdNormal = do
    (x,_) <- stdNormals
    return x

-- |Generate a sample from the standard normal distribution with a given
--  mean and variance.
normal :: (MonadRandom m) => Double -> Double -> m Double
normal mu sigma = do
    x <- stdNormal
    return $ mu + sigma * x