【问题标题】:Issues with seed setting in parallelised foreach loop R并行化 foreach 循环 R 中种子设置的问题
【发布时间】:2022-11-28 13:38:20
【问题描述】:

我有一些要并行运行的测试。当我使用 foreach() 执行此操作时,我得到了 20 个测试迭代对的预期输出:

## Without seed
require(data.table)
require(foreach)
require(iterators)
require(doParallel)
require(doRNG)

numCores = 2
registerDoParallel(numCores)

iterations = 5
num_tests = 2:5

foreach( i = 1:iterations, .combine = 'rbind', .multicombine = TRUE, .inorder = FALSE ) %:%
  foreach( n = num_tests, .combine = 'rbind', .multicombine = TRUE, .inorder = FALSE ) %dopar% {
    
    ## Print iteration
    print(paste('Tests =',n,'Iteration =',i))
    
  }

输出:

result.1 "Tests = 2 Iteration = 1"
result.2 "Tests = 3 Iteration = 1"
result.3 "Tests = 4 Iteration = 1"
result.4 "Tests = 5 Iteration = 1"
result.1 "Tests = 2 Iteration = 2"
result.2 "Tests = 3 Iteration = 2"
result.3 "Tests = 4 Iteration = 2"
result.4 "Tests = 5 Iteration = 2"
result.1 "Tests = 2 Iteration = 3"
result.2 "Tests = 3 Iteration = 3"
result.3 "Tests = 4 Iteration = 3"
result.4 "Tests = 5 Iteration = 3"
result.1 "Tests = 2 Iteration = 4"
result.2 "Tests = 3 Iteration = 4"
result.3 "Tests = 4 Iteration = 4"
result.4 "Tests = 5 Iteration = 4"
result.1 "Tests = 2 Iteration = 5"
result.2 "Tests = 3 Iteration = 5"
result.3 "Tests = 4 Iteration = 5"
result.4 "Tests = 5 Iteration = 5"

但是,当我尝试向此循环添加一个步骤以设置随机种子时,如 doRNG 小插图中所述,我在每次迭代中得到不同数量的测试(14 个测试迭代对):

## With seed
numCores = 2
registerDoParallel(numCores)

iterations = 5
num_tests = 2:5
rng <- RNGseq( iterations * (iterations+1) / 2, 1234)

foreach( i = 1:iterations, .combine = 'rbind', .multicombine = TRUE, .inorder = FALSE ) %:%
  foreach( n = num_tests, r = rng[(i-1)*i/2 + 1:i], .combine = 'rbind', .multicombine = TRUE, .inorder = FALSE ) %dopar% {
    
    ##Set seed
    rngtools::setRNG(r)
    
    ## Print iteration
    print(paste('Tests =',n,'Iteration =',i))
    
  }

输出:

result.1 "Tests = 2 Iteration = 1"
result.1 "Tests = 2 Iteration = 2"
result.2 "Tests = 3 Iteration = 2"
result.1 "Tests = 2 Iteration = 3"
result.2 "Tests = 3 Iteration = 3"
result.3 "Tests = 4 Iteration = 3"
result.1 "Tests = 2 Iteration = 4"
result.2 "Tests = 3 Iteration = 4"
result.3 "Tests = 4 Iteration = 4"
result.4 "Tests = 5 Iteration = 4"
result.1 "Tests = 2 Iteration = 5"
result.2 "Tests = 3 Iteration = 5"
result.3 "Tests = 4 Iteration = 5"
result.4 "Tests = 5 Iteration = 5"

我正在寻找一种方法来并行设置种子,嵌套的 foreach() 循环具有正确的迭代次数。有任何想法吗?

【问题讨论】:

  • 也许看到this
  • 据我从小插图中可以看出,这与嵌套循环不兼容。尝试时出现以下错误:“当前不支持使用运算符 %:% 的嵌套和/或条件 foreach 循环。”
  • Section 5 描述了解决方法。
  • 是的——我认为这是他们为我的用例建议的解决方法,但如果我弄错了请告诉我。
  • 抱歉 :( 希望其他人可以提供帮助。

标签: r foreach data.table doparallel


【解决方案1】:

在我看来,小插图中的这个例子过于聪明。它在 foreach 循环中建立 r 作为 rng 的子列表作为终止条件。您使用了一些错误的索引/列表边界,这是导致您混淆输出的原因。

每次迭代都会为r 提供rng 子列表的一个元素。它摆脱了奇怪的子列表数学,但它令人难以置信的混乱,并且该行将在代码审查中获得“WTF”10 次中的 11 次。

至于最接近小插图示例的立即修复:

## With seed
numCores = 2
registerDoParallel(numCores)

iterations = 5
num_tests = 2:5
rng <- RNGseq( iterations * length(num_tests), 1234)

foreach( i = 1:iterations, .combine = 'rbind', .multicombine = TRUE, .inorder = FALSE ) %:%
  foreach( n = num_tests, r = rng[(i-1)*length(num_tests) + 1:length(num_tests)], .combine = 'rbind', .multicombine = TRUE, .inorder = FALSE ) %dopar% {
    
    ##Set seed
    rngtools::setRNG(r)
    
    ## Print iteration
    print(paste('Tests =',n,'Iteration =',i))
    
  }

这改变了

rng &lt;- RNGseq( iterations * (iterations+1) / 2, 1234)

rng &lt;- RNGseq( iterations * length(num_tests), 1234)

和变化

r = rng[(i-1)*i/2 + 1:i]

r = rng[(i-1)*length(num_tests) + 1:length(num_tests)]

可能更具可读性/可理解性的替代方案

rng 是一个RNGSeq 的列表(它们只是自己列出),我们要做的就是从该列表中为每个并行操作选择一个新的单个RNGSeq。我们需要为每个独立的并行迭代创建一个元素。

# one RNGseq for each inner+outer loop combination
iterations = 5
num_tests = 2:5
rng <- RNGseq(iterations * length(num_tests), 1234)

foreach( i = 1:iterations, .combine = 'rbind', .multicombine = TRUE, .inorder = FALSE ) %:%
    foreach( n = num_tests, .combine = 'rbind', .multicombine = TRUE, .inorder = FALSE ) %dopar% {
        
        # this is moved out of the loop termination conditions
        # and gets a new list element for each iteration
        # note the [[ ]]s to get a single element instead of a sublist!
        #         [-----------------------] conditions that change based on outer loop
        #                                     [---]  conditions that change based on inner loop
        r = rng[[ ((i-1)*length(num_tests)) + (n-1) ]]

        ##Set seed
        rngtools::setRNG(r)
        
        ## Print iteration
        print(paste('Tests =',n,'Iteration =',i))
        
    }

r的赋值需要(n-1),因为num_tests从2开始。使用当前nnum_tests中的索引会更加灵活:r = rng[ ((i-1)*i) + (which(num_tests == n)) ]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-19
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    • 1970-01-01
    • 2015-10-16
    • 1970-01-01
    相关资源
    最近更新 更多