使用插入符号的完全可重现的并行模型

Posted

技术标签:

【中文标题】使用插入符号的完全可重现的并行模型【英文标题】:Fully reproducible parallel models using caret 【发布时间】:2012-11-04 09:38:06 【问题描述】:

当我在插入符号中运行 2 个随机森林时,如果我设置一个随机种子,我会得到完全相同的结果:

library(caret)
library(doParallel)

set.seed(42)
myControl <- trainControl(method='cv', index=createFolds(iris$Species))

set.seed(42)
model1 <- train(Species~., iris, method='rf', trControl=myControl)

set.seed(42)
model2 <- train(Species~., iris, method='rf', trControl=myControl)

> all.equal(predict(model1, type='prob'), predict(model2, type='prob'))
[1] TRUE

但是,如果我注册一个并行后端来加速建模,每次运行模型时都会得到不同的结果:

cl <- makeCluster(detectCores())
registerDoParallel(cl)

set.seed(42)
myControl <- trainControl(method='cv', index=createFolds(iris$Species))

set.seed(42)
model1 <- train(Species~., iris, method='rf', trControl=myControl)

set.seed(42)
model2 <- train(Species~., iris, method='rf', trControl=myControl)

stopCluster(cl)

> all.equal(predict(model1, type='prob'), predict(model2, type='prob'))
[1] "Component 2: Mean relative difference: 0.01813729"
[2] "Component 3: Mean relative difference: 0.02271638"

有没有办法解决这个问题?一个建议是使用 doRNG 包,但 train 使用嵌套循环,目前不支持:

library(doRNG)
cl <- makeCluster(detectCores())
registerDoParallel(cl)
registerDoRNG()

set.seed(42)
myControl <- trainControl(method='cv', index=createFolds(iris$Species))

set.seed(42)
> model1 <- train(Species~., iris, method='rf', trControl=myControl)
Error in list(e1 = list(args = seq(along = resampleIndex)(), argnames = "iter",  : 
  nested/conditional foreach loops are not supported yet.
See the package's vignette for a work around.

更新: 我认为这个问题可以使用doSNOWclusterSetupRNG 来解决,但我无法做到。

set.seed(42)
library(caret)
library(doSNOW)
cl <- makeCluster(8, type = "SOCK")
registerDoSNOW(cl)

myControl <- trainControl(method='cv', index=createFolds(iris$Species))

clusterSetupRNG(cl, seed=rep(12345,6))
a <- clusterCall(cl, runif, 10000)
model1 <- train(Species~., iris, method='rf', trControl=myControl)

clusterSetupRNG(cl, seed=rep(12345,6))
b <- clusterCall(cl, runif, 10000)
model2 <- train(Species~., iris, method='rf', trControl=myControl)

all.equal(a, b)
[1] TRUE
all.equal(predict(model1, type='prob'), predict(model2, type='prob'))
[1] "Component 2: Mean relative difference: 0.01890339"
[2] "Component 3: Mean relative difference: 0.01656751"

stopCluster(cl)

foreach 有什么特别之处,为什么不使用我在集群上发起的种子?对象ab 是相同的,那么为什么不是model1model2

【问题讨论】:

也许this的问题会提供一些有用的信息...? 它确实提供了有用的信息。不幸的是,使用snow 需要修改插入符号源代码,而使用doRNG 会失败。 现在可以使用library(doMC) - 请参阅caret.r-forge.r-project.org/parallel.html 【参考方案1】:

使用caret 包在并行模式下运行完全可重现模型的一种简单方法是在调用列车控制时使用种子参数。至此上述问题已解决,请查看 trainControl 帮助页面了解更多信息。

library(doParallel); library(caret)

#create a list of seed, here change the seed for each resampling
set.seed(123)

#length is = (n_repeats*nresampling)+1
seeds <- vector(mode = "list", length = 11)

#(3 is the number of tuning parameter, mtry for rf, here equal to ncol(iris)-2)
for(i in 1:10) seeds[[i]]<- sample.int(n=1000, 3)

#for the last model
seeds[[11]]<-sample.int(1000, 1)

 #control list
 myControl <- trainControl(method='cv', seeds=seeds, index=createFolds(iris$Species))

 #run model in parallel
 cl <- makeCluster(detectCores())
 registerDoParallel(cl)
 model1 <- train(Species~., iris, method='rf', trControl=myControl)

 model2 <- train(Species~., iris, method='rf', trControl=myControl)
 stopCluster(cl)

 #compare
 all.equal(predict(model1, type='prob'), predict(model2, type='prob'))
[1] TRUE

【讨论】:

这是 caret 包中的新功能,因为我提出了这个问题。感谢您让我了解最新信息! @BBrill 我有一个问题,如果我在 trainControl 函数中设置种子=NA 会怎样?【参考方案2】:

所以 caret 使用 foreach 包进行并行化。很可能有一种方法可以在每次迭代时设置种子,但我们需要在train 中设置更多选项。

或者,您可以创建一个自定义建模函数,模仿随机森林的内部函数并自己设置种子。

最大

【讨论】:

【参考方案3】:

您使用的是哪个版本的插入符号?

@BBrill 的回答是正确的。但是,从 v6.0.64(2016 年 1 月 15 日)开始,插入符号考虑了这个问题。您可以提供您自定义的trControl$seeds,但您不必这样做。如果trControl$seedsNULL,caert 会自动为您生成这些,即使在并行训练时也能确保重现性。

这个行为可以在https://github.com/topepo/caret/commit/9f375a1704e413d0806b73ab8891c7fadc39081c找到

拉取请求:https://github.com/topepo/caret/pull/353

相关代码sn-ps:

    if(is.null(trControl$seeds) || all(is.na(trControl$seeds)))  
      seeds <- sample.int(n = 1000000L, size = num_rs * nrow(trainInfo$loop) + 1L)
      seeds <- lapply(seq(from = 1L, to = length(seeds), by = nrow(trainInfo$loop)),
                      function(x)  seeds[x:(x+nrow(trainInfo$loop)-1L)] )
      seeds[[num_rs + 1L]] <- seeds[[num_rs + 1L]][1L]
      trControl$seeds <- seeds
     else 
      (... omitted ...)
    

更多详情,您可以

【讨论】:

以上是关于使用插入符号的完全可重现的并行模型的主要内容,如果未能解决你的问题,请参考以下文章

在 Macbook 中并行执行随机森林的小速度增益(使用 R,插入符号)

比较插入符号模型对象的最佳功能

使用并行训练带有插入符号的随机森林

如果使用 doParallel 和 recipes 则出现插入错误

插入符号训练二进制 glm 通过 doParallel 在并行集群上失败

引导子菜单插入符号在 iPad 3 上不可见