8

我想对具有给定均值和标准差的正态分布进行采样。我知道如何在 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

谢谢你。

4

2 回答 2

6

快速回答

如何使用 aData.Random.RVar里面的 a 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.MonadRandom并且Control.Monad.Random形式上是不同的,因为它们在不同的地方独立定义,并且没有一个是另一个的实例(没有instance DR.MonadRandom m => CMR.MonadRandom m或相反)。
  • Monad 具有相似的语义,因为它们都提供来自某个随机源的随机数,因此期望我们可以以某种方式组合它们是有意义的。

假设您在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 IntCMR.MonadRandom

我们想要运行RVarmonad,正如语义所暗示的那样,我们可能必须为其提供一个随机数源。我们正在为 CMR寻找像evalRand这样的单子转义函数。这些转义函数有 type m a -> someStuffNeededToRunTheMonad -> a

有关 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随机数的和源,它返回我们RVar自己的 monad 内部的随机结果m。然而,这需要有一个instance RandomSource m s表示它s是我们的 monad 的随机源m。让我们寻找那个例子。

我们的 monad 是什么m?我们想运行RVarin gimmeRandom,所以 monad 是CMR.MonadRandom m => m(所有实现的 monad CMR.MonadRandom)。随机性来源是s什么?还没有头绪。让我们查看文档中存在哪些RandomSource实例:

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

啊哈!这表示任何monadm0都是 的实例RandomSource以及来自该 monad 的值(例如m0 Word32)。这当然也适用于我们的 monad CMR.MonadRandom。我们还可以看到s, m0 Word32, 一定是随机源产生的随机值。

我们应该传入什么作为sin runRVar (uniform 0 100) s在我们的monad中生成随机数的东西,类型为CMR.MonadRandom m => m Word32. CMR生成任意事物的功能是什么,例如 some Word32随机获取。所以基本上我们想写:

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)? 这很奇怪,them和 them0似乎被编译器识别为不同的单子;我们希望它们相同,如RandomSource m0 (m0 Word64).

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

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

还是同样的错误。这是因为m在那个类型签名中,实际上是任何monad 实现CMR.MonadRandom,不一定是MonadRandom在我们的gimmeRandom类型签名中。

(这与 lambda 术语中的阴影概念相同,其中(\x -> (\x -> f x))inner\x是 in 中使用的;f x或者在一阶逻辑中如,作为外部的一个;或者实际上在任何其他编程语言中,在内部范围内具有变量隐藏/阴影——只是这里是类型变量阴影)。∀x . F(x) → ∀x . G(x)xG(x)∀x

所以我们唯一要做的就是告诉编译器,在getRandom调用中,我们不希望它用于 any MonadRandom,而正是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

这使得mingetRandom :: m ...完全 CMR.MonadRandom m从顶级类型签名中选择。

这确实编译并解决了问题:我们可以使用接口Data.Random在代码中使用分布。MonadRandom我们可以很容易地用uniform另一个发行版替换。

总而言之,我们有

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

If you are wondering why we chose Word32 somewhat arbitrarily from the instances we can pick from, we just have to give the randomness source in some form, and Word32 is one of the things Data.Random takes as input for generating other random stuff.

于 2012-12-19T06:06:48.727 回答
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 
于 2012-12-18T17:21:35.093 回答