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

ela*_*sca 8 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
Run Code Online (Sandbox Code Playgroud)

谢谢.

nh2*_*nh2 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
Run Code Online (Sandbox Code Playgroud)

说明

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

  • Data.Random.MonadRandom并且Control.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
Run Code Online (Sandbox Code Playgroud)

我们可以运行这个evalRand gimmeRandom StdGen,这给了我们一个Int.

现在代替getRandomR,您想要使用由提供的许多可用分发之一Data.Random.

对于此示例,我们将尝试替换getRandomR (0, 100)uniform 0 100 :: RVar Int.如何我们得到Int了的是RVar Int在我们的CMR.MonadRandom环境?

我们想运行RVarmonad,我们可能必须提供一个随机数源,正如语义所暗示的那样.我们正在寻找像evalRand for CMR 这样的monad-escaping函数.这些转义函数有类型m a -> someStuffNeededToRunTheMonad -> a.

关于RVar文档中,有一个例子:

-- In a monad, using a RandomSource:
runRVar (uniform 1 100) DevRandom :: IO Int
Run Code Online (Sandbox Code Playgroud)

我们来看看runRVar:

runRVar :: RandomSource m s => RVar a -> s -> m a
Run Code Online (Sandbox Code Playgroud)

是的,这是一种转义函数:给定一个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)
...
Run Code Online (Sandbox Code Playgroud)

啊哈!这就是说任何单子m0是一个实例RandomSource连同值从这个单子来(例如m0 Word32).这当然也适用于我们的monad CMR.MonadRandom.我们也可以看到s,m0 Word32必须由随机性源生成的随机值.

我们应该通过在作为srunRVar (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
Run Code Online (Sandbox Code Playgroud)

嗯,那不编译:

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
Run Code Online (Sandbox Code Playgroud)

RandomSource m (m0 a0)?这是奇怪的mm0似乎被确认为不同的编译器的单子; 我们希望它们是一样的,就像在RandomSource m0 (m0 Word64).

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

r <- runRVar (uniform 0 100) (getRandom :: CMR.MonadRandom m => m Word32)
Run Code Online (Sandbox Code Playgroud)

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

(这与阴影中的阴影概念相同,(\x -> (\x -> f x))其中内部\x是用于的f x;或者在一阶逻辑中?x . F(x) ? ?x . G(x),其中xin G(x)是最内部定义的一个并且不需要相同,甚至不是相同的类型,作为外部的那个?x;或者实际上在内部作用域中具有变量隐藏/阴影的任何其他编程语言 - 只是在这里它是类型变量阴影).

因此,我们唯一需要做的就是告诉编译器在getRandom调用中,我们不希望它适用于任何类型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
Run Code Online (Sandbox Code Playgroud)

这使得从顶级类型签名中精确选择min .getRandom :: m ... CMR.MonadRandom m

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

总而言之,我们有

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

如果你想知道为什么我们Word32从我们可以选择的实例中任意选择,我们只需要以某种形式给出随机源,而Word32Data.Random生成其他随机内容的输入之一.