我想采用给定均值和标准差的正态分布进行采样。我知道如何在各种上下文中执行此操作,如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
谢谢。
答案 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.MonadRandom
和Control.Monad.Random
正式不同,因为它们是在不同的地方独立定义的,没有一个是另一个的实例(没有instance DR.MonadRandom m => CMR.MonadRandom m
或者相反)。我们假设您在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 RVar
中m
的随机结果。但是,这需要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
。我们还可以看到s
,m0 Word32
必须是随机源生成的随机值。
我们应该在s
中作为runRVar (uniform 0 100) s
传递什么?在我们的 monad中生成随机数的东西,类型CMR.MonadRandom m => m Word32
。生成任意内容的CMR
函数是什么,例如一些Word32
? getRandom。所以基本上我们想写:
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)
?这很奇怪,m
和m0
似乎被编译器识别为不同的 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))
中的阴影概念相同,其中内部\x
是f 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
。
总结,我们有
uniform
)runRVar
)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