【问题标题】:Debugging foreach in R using dorng使用 dorng 在 R 中调试 foreach
【发布时间】:2020-07-28 04:16:42
【问题描述】:

我正在使用 foreach 循环在 R 中并行化一个任务,并使用 dorng 运算符获得可重现的结果。这是一个复杂的代码,即使我使用常规的for 循环运行相同的代码,我也无法识别出一个错误。

我的基本问题是:假设我有可重现的结果,我该如何调试 foreach 循环中的函数?以下是我目前的尝试。

在 doRNG 包的 vignette 中,它说将使用 R 数生成器“L'Ecuyer-CMRG”在每次迭代开始时生成并设置随机种子序列。随机种子的序列可以在foreach循环之前使用set.seed定义:

library(doRNG)
library(doParallel)
library(foreach)

registerDoParallel(2)
set.seed(1234)
f01 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  out <- 1 + i
}
r01 <- attr(f01, "rng")

set.seed(1234)
f02 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  out <- 1 + i
}
r02 <- attr(f02, "rng")

对象r01r02 包含f01f02 中使用的种子序列,

identical(f01, f02)
identical(r01, r02)

这样foreach 循环的结果及其种子与预期的相同!

然后,让我们考虑foreach 循环会给我一个随机错误的情况:

set.seed(1234)
f03 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
  u <- floor(runif(1, 1, 101))
  
  if (i == as.integer(u)){
    out <- "a" + "b"
  } else {
    out <- 1 + i
  }

}
Error in { : task 67 failed - "non-numeric argument to binary operator"

错误发生在第 67 次迭代,很容易理解错误。不幸的是,我的情况不一样。

我希望能够使用debug 并遍历我的函数以了解错误。据我所知,我不能在 R 中的 foreach 循环中使用 debug

然后,我考虑在常规的for 循环中捕获错误,但是运行我的代码非常慢,而且显然我无法在迭代次数少的情况下观察到错误。我需要使用foreach 了解错误。

虽然我无法从f03 恢复种子序列,但我知道它们将与r01r02 相同。对于第 67 次迭代,我有

r01[[67]]
[1]       10407  1484283582  -741709185   513087691   132931819
[6]  1318506528 -1383054295

因此,我想将我的种子固定在 r01[[67]] 应该会给我同样的错误:

i <- 67
set.seed(r01[[67]], kind = "L'Ecuyer-CMRG")
u <- floor(runif(1, 1, 101))
  if (i == as.integer(u)){
    out <- "a" + "b"
  } else {
    out <- 1 + i
}
u
[1] 74

这不是真的。

在 doRNG vignette,第 6 页,他们有一个在前一个循环的循环中使用种子的示例:

set.seed(1234)
ex01 <- foreach(i=1:5) %dorng% { runif(3) }
ex02 <- foreach(i=1:5, .options.RNG=attr(ex01, 'rng')[[2]]) %dorng% { runif(3) }
identical(ex02[1:4], ex01[2:5])

我错过了什么?

【问题讨论】:

    标签: r foreach


    【解决方案1】:

    我认为问题在于doRNG 为您提供了随机种子状态,而您将其用作set.seed 的输入,它只需要一个整数。我希望set.seed 仅采用提供给函数的第一个整数来设置随机种子状态。相反,你应该做的是在 R 中设置种子状态。首先我得到种子状态并验证我可以重现错误:

    library(doRNG)
    library(doParallel)
    library(foreach)
    
    registerDoParallel(2)
    kind <- RNGkind()
    kind
    [1] "Mersenne-Twister" "Inversion"        "Rejection"   
    
    set.seed(1234, kind = kind[1]) #explicitly set RNG kind so I can reproduce more easily if I run the code multiple times
    f01 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
      out <- 1 + i
    }
    r01 <- attr(f01, "rng")
    
    set.seed(1234, kind = kind[1])
    f03 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
      u <- floor(runif(1, 1, 101))
      
      if (i == as.integer(u)){
        out <- "a" + "b"
      } else {
        out <- 1 + i
      }
    
    }
    Error in { : task 39 failed - "non-numeric argument to binary operator"
    

    我得到了一个不同的失败迭代次数。不知道为什么,但我仍然像你一样得到错误。那我看看能不能直接设置.Random.seed复制:

    i <- 39
    RNGkind("L'Ecuyer-CMRG")
    .Random.seed <- r01[[39]]
    u <- floor(runif(1, 1, 101))
      if (i == as.integer(u)){
        out <- "a" + "b"
      } else {
        out <- 1 + i
    }
    Error in "a" + "b" : non-numeric argument to binary operator
    u
    [1] 39
    

    看起来我们可以!完整代码如下:

    library(doRNG)
    library(doParallel)
    library(foreach)
    
    registerDoParallel(2)
    kind <- RNGkind()
    kind
    
    set.seed(1234, kind = kind[1]) #explicitly set RNG kind so I can reproduce more easily if I run the code multiple times
    f01 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
      out <- 1 + i
    }
    r01 <- attr(f01, "rng")
    
    set.seed(1234, kind = kind[1])
    f02 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
      out <- 1 + i
    }
    r02 <- attr(f02, "rng")
    
    identical(f01, f02)
    identical(r01, r02)
    
    set.seed(1234, kind = kind[1])
    f03 <- foreach(i = 1:100, .combine = 'c')  %dorng% {
      u <- floor(runif(1, 1, 101))
      
      if (i == as.integer(u)){
        out <- "a" + "b"
      } else {
        out <- 1 + i
      }
    
    }
    
    identical(r01, r03)
    
    i <- 39
    RNGkind("L'Ecuyer-CMRG")
    .Random.seed <- r01[[39]]
    u <- floor(runif(1, 1, 101))
      if (i == as.integer(u)){
        out <- "a" + "b"
      } else {
        out <- 1 + i
    }
    u
    
    #restore RNG kind to original
    RNGkind(kind[1])
    

    【讨论】:

    • 这真的很有趣。有没有办法在不使用包 doRNG 的情况下实现类似的东西,而只是使用'parallel::clusterSetRNGStream'?我遇到了与 OP 类似的问题,并且在运行 22 小时后出现错误。由于我已经在我的脚本中使用 clusterSetRNGStream 我想知道我是否可以重现失败任务的确切条件,而不必在其他 22 小时内使用 doRNG 重新运行脚本......谢谢
    • 我想你可以。你只需要找出.Random.seed的正确值
    猜你喜欢
    • 2015-02-28
    • 2017-01-31
    • 2017-01-09
    • 1970-01-01
    • 1970-01-01
    • 2014-10-22
    • 1970-01-01
    • 2018-05-14
    • 1970-01-01
    相关资源
    最近更新 更多