LoginSignup
9
8

More than 5 years have passed since last update.

R の foreach で並列処理するときに乱数を固定する #rstatsj

Last updated at Posted at 2014-11-05

以前の記事に書いたように、R の foreach で並列処理を書くときは、次のように書きます。

R
library(doParallel)
ExecuteParallelProcess <- function() {
  cl <- makeCluster(detectCores())
  registerDoParallel(cl)
  on.exit(stopCluster(cl)) 
  foreach(i = 1:3, .combine=c) %dopar% {
    rnorm(1)
  }
}
ExecuteParallelProcess()
結果
[1]  0.6725327  0.8103935 -0.4231717

このプログラムは、正規分布から 3 つの乱数を取得するというものです。
科学技術計算の分野では、シミュレーション結果の再現性を保つために、乱数を固定したいという要求があります。
つまり、何度実行しても同じ結果が得られるように、毎回同じ乱数を生成したいということです。
この目的のために、R では通常 set.seed() 関数を用いて乱数を固定します。

R
ExecuteParallelProcess <- function() {
  set.seed(12345) # この行を追加
  cl <- makeCluster(detectCores())
  registerDoParallel(cl)
  on.exit(stopCluster(cl)) 
  foreach(i = 1:3, .combine=c) %dopar% {
    rnorm(1)
  }
}
ExecuteParallelProcess()
ExecuteParallelProcess()
結果
[1] -0.3014045  0.7398383 -0.7157335
[1] -0.98871268 -0.03876135  0.31606417

しかし、ご覧のように、並列計算を行う場合、set.seed() で乱数を固定することはできません。
乱数の固定の仕方は、使用する並列化パッケージによって異なるようですが、ここでは parallel パッケージの clusterSetRNGStream() 関数を使って乱数を固定してみましょう。

R
ExecuteParallelProcess <- function() {
  cl <- makeCluster(detectCores())
  clusterSetRNGStream(cl, 12345) # この行を追加
  registerDoParallel(cl)
  on.exit(stopCluster(cl)) 
  foreach(i = 1:3, .combine=c) %dopar% {
    rnorm(1)
  }
}
ExecuteParallelProcess()
ExecuteParallelProcess()
結果
[1] -1.45785035 -0.03518695  0.05799898
[1] -1.45785035 -0.03518695  0.05799898

これにより、並列処理においても乱数を固定することができるようになりました。

ところで、この記事に書いた方法でクラスタを implicit にしたい場合、クラスタオブジェクトを引数に取る clusterSetRNGStream() は使えません。

そこで便利なのが doRNG パッケージです。
doRNG パッケージの registerDoRNG() 関数を使えば、クラスタを implicit にしても乱数の固定ができます。

R
library(doRNG)
ExecuteParallelProcess <- function() {
  registerDoParallel(detectCores())
  registerDoRNG(12345) # この行を追加
  on.exit(stopImplicitCluster2()) 
  foreach(i = 1:3, .combine=c) %dopar% {
    rnorm(1)
  }
}
res1 <- ExecuteParallelProcess()
res2 <- ExecuteParallelProcess()
identical(res1, res2)
結果
[1] TRUE

また、doRNG を使った別の方法として、foreach.options.RNG オプションを指定して、%dorng% を使う方法もあります。

R
ExecuteParallelProcess <- function() {
  registerDoParallel(detectCores())
  on.exit(stopImplicitCluster2()) 
  foreach(i = 1:3, .combine=c, .options.RNG=12345) %dorng% { # この行を変更
    rnorm(1)
  }
}
res3 <- ExecuteParallelProcess()
res4 <- ExecuteParallelProcess()
identical(res3, res4)
結果
[1] TRUE

ついでに、上記二つの方法の結果が一致することを確認してみます。

R
identical(res1, res3)
結果
[1] TRUE

以上です。

参考文献

関連記事

9
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8