8

与えられた平均と標準偏差で正規分布をサンプリングしたいと思います。Data.Random.RvarやData.Random.MonadRandomなどのさまざまなコンテキストでこれを行う方法を知っています。ただし、私の関数のコンテキストはControl.Monad.MonadRandomであり、プロジェクト全体でControl.Monad.MonadRandomを使用しているため、このままにしておきたいと思います。

そうする方法はありますか、それを手伝ってくれませんか?

コードは次のようになります。PatternはData.VectorDoubleの単なるエイリアスであり、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

簡単な答え

Data.Random.RVara の中で 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.MonadRandomControl.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、 が提供する多くの利用可能なディストリビューションの 1 つを使用する必要がありますData.Random

この例では、 で置き換えようとgetRandomR (0, 100)uniform 0 100 :: RVar Intます。私たちの環境Intでそれをどのように活用するのでしょうか?RVar IntCMR.MonadRandom

RVarセマンティックが示唆するように、おそらく乱数ソースを提供する必要があるモナドを実行したい。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独自の モナド 内のランダムな結果を返しますm。ただし、これには、モナド の乱数源でinstance RandomSource m sあると言うが必要です。そのインスタンスを探してみましょう。sm

モナドとはmRVarinを実行したいgimmeRandomので、モナドはCMR.MonadRandom m => m( を実装するすべてのモナドCMR.MonadRandom) です。乱数源とは何sですか? まだ手がかりはありません。インスタンスが存在するドキュメントを見てみましょう。RandomSource

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

あはは!これは、任意のモナドが、このモナドからの値(例: )を伴うm0のインスタンスであることを示しています。もちろん、これはモナドにも当てはまります。, , は、ランダム ソースによって生成されたランダム値でなければならないこともわかります。RandomSourcem0 Word32CMR.MonadRandomsm0 Word32

sinとして何を渡す必要がありrunRVar (uniform 0 100) sますか? モナドで乱数を生成するものCMR.MonadRandom m => m Word32、型の何か。CMRsome などの任意のものを生成する関数は何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として認識されるようです。のように、それらを同じにしたいのです。RandomSource m0 (m0 Word64)

その場所に完全な署名を入れましょう。

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

それでも同じエラーです。これはm、その型シグネチャの は実際には を実装する任意のモナドであり、型シグネチャCMR.MonadRandomの であるとは限らないためです。MonadRandomgimmeRandom

(\x -> (\x -> f x))(これは、内側\xが in で使用されるものであるラムダ用語、または in が最も内側に定義されたものであり、同じである必要はなく、同じ型でさえない のような一次論理と同じシャドーイングの概念ですf x。、外側のものとして;または実際には、内部スコープで変数の隠蔽/シャドウイングを使用する他のプログラミング言語で-ここでは変数のシャドウイングです)。∀x . F(x) → ∀x . G(x)xG(x)∀x

getRandomしたがって、私たちがしなければならない唯一のことは、呼び出しで、それが anyMonadRandomではなくMonadRandom mgimmeRandom型シグネチャにあるものであることをコンパイラに伝えることです。

ScopedTypeVariables拡張機能を使用してそれを行うことができます:

{-# LANGUAGE ScopedTypeVariables #-}

[...]

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

これにより、トップレベルの型シグネチャからminが正確getRandom :: m ...に選択されるようになります。 CMR.MonadRandom m

これはコンパイルされ、問題は解決されます。インターフェイスData.Randomを使用してコード内からディストリビューションを使用できMonadRandomます。uniformを別のディストリビューションに簡単に置き換えることができます。

要約すると、

  • 異なるパッケージの 2 つの異なるモナドを使用しているが、セマンティクスが同じ/重複していることを確認しました
  • 独自の内部で使用したいモナドを実行/エスケープする方法を見つけました( with runRVar
  • 型クラスの制限とそれらに提供されるインスタンスを調べることにより、エスケープ関数に何を渡すかを見つけました
  • 正しいコードを書いた ( runRVar (uniform 0 100) getRandom)
  • どの正確なモナドgetRandomが使用するかを指定してコンパイルしました。

なぜ選択Word32可能なインスタンスからいくらか恣意的に選択したのか疑問に思われている場合は、何らかの形でランダム性のソースを提供する必要があり、Word32Data.Random他のランダムなものを生成するための入力として受け取るものの 1 つです。

于 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 に答える