【问题标题】:Problem to reproduce results from parallelSVM in R在 R 中从 parallelSVM 重现结果的问题
【发布时间】:2020-01-25 02:14:26
【问题描述】:

我无法设置种子值以从 parallelSVM() 获得可重现的结果。

 library(e1071)
 library(parallelSVM)

 data(iris)
 x <- subset(iris, select = -Species)
 y <- iris$Species

set.seed(1)
model       <- parallelSVM(x, y)
parallelPredictions <- predict(model, x)

set.seed(1)
model2       <- parallelSVM(x, y)
parallelPredictions2 <- predict(model2, x)

all.equal(parallelPredictions,parallelPredictions2) 

我知道这不是为多核操作设置种子值的正确方法,但我不知道该怎么做。

我知道在使用 mclapply 时有一个选项,但这对我的情况没有帮助。


编辑:
我找到了一个解决方案,方法是使用trace 更改parallelSVM 中的函数trainSample(),并使用foreach 循环更改种子包的doRNG

有人知道更好的解决方案吗?

【问题讨论】:

    标签: r machine-learning parallel-processing random-seed reproducible-research


    【解决方案1】:

    简而言之,parallelSVM 中没有实现的方法来处理这个问题。然而,该包使用foreachdoParallel 包来处理它的并行操作。并且在stackoverflow上足够努力地挖掘解决方案是可能的!

    感谢 this answer,感谢 doRNG 软件包的使用,感谢 this answer,感谢 this answer 为我提供了一个更简单的封闭解决方案的想法。

    解决方案:

    parallelSVM 包中,并行化通过parallelSVM::registerCores 函数进行。这个函数只是简单地调用doParallel::registerDoParallel 的核心数,没有更多的参数。我的想法只是更改parallelSVM::registerCores 函数,使其在创建新集群后自动设置种子。

    在执行需要并行种子的并行计算时,需要确保两件事

    1. 需要将种子分配给集群中的每个节点
    2. 生成器必须是跨集群渐近随机的生成器。

    幸运的是,doRNG 包处理了第一个并使用了在 2 上可以使用的种子。使用unlockNamespaceassign 的组合,我们可以覆盖parallelSVM::registerCores,这样它就包括对@ 的调用987654338@ 带有适当的种子(答案末尾的函数)。这样做我们实际上可以获得正确的再现性,如下所示:

    library(parallelSVM)
    library(e1071)
    data(magicData)
    set.seed.parallelSWM(1) #<=== set seed as we would normally.
    #Example from help(parallelSVM)
    system.time(parallelSvm1 <- parallelSVM(V11 ~ ., data = trainData[,-1],
                                           numberCores = 4, samplingSize = 0.2, 
                                           probability = TRUE, gamma=0.1, cost = 10))
    system.time(parallelSvm2 <- parallelSVM(V11 ~ ., data = trainData[,-1],
                                           numberCores = 4, samplingSize = 0.2, 
                                           probability = TRUE, gamma=0.1, cost = 10))
    pred1 <- predict(parallelSvm1)
    pred2 <- predict(parallelSvm2)
    all.equal(pred1, pred2)
    [1] TRUE
    identical(parallelSvm1, parallelSvm2)
    [1] FALSE
    

    请注意,identical 没有能力正确评估parallel::parallelSvm 输出的对象,因此预测最好检查模型是否相同。

    为了安全,让我们检查一下问题中的可重现示例是否也是这种情况

    x <- subset(iris, select = -Species)
    y <- iris$Species
    set.seed.parallelSWM(1) #<=== set seed as we would normally (not necessary if above example has been run).
    model       <- parallelSVM(x, y)
    model2       <- parallelSVM(x, y)
    parallelPredicitions <- predict(model, x)
    parallelPredicitions2 <- predict(model2, x)
    all.equal(parallelPredicitions, parallelPredicitions2)
    [1] TRUE
    

    呼……

    最后,如果我们完成了,或者如果我们想要再次随机种子,我们可以通过执行重置种子

    set.seed.parallelSWM() #<=== set seed to random each execution (standard).
    #check:
    model       <- parallelSVM(x, y)
    model2       <- parallelSVM(x, y)
    parallelPredicitions <- predict(model, x)
    parallelPredicitions2 <- predict(model2, x)
    all.equal(parallelPredicitions, parallelPredicitions2)
    [1] "3 string mismatches"
    

    (输出会有所不同,因为没有设置 RNNG 种子)

    set.seed.parallelSWM 函数

    感谢this answer。请注意,我们可能不必加倍分配,但在这里我只是简单地复制了答案,而不检查代码是否可以进一步减少。

    set.seed.parallelSWM <- function(seed, once = TRUE){
        if(missing(seed) || is.character(seed)){
            out <- function (numberCores) 
            {
                cluster <- parallel::makeCluster(numberCores)
                doParallel::registerDoParallel(cluster)
            }
        }else{
            require("doRNG", quietly = TRUE, character.only = TRUE)
            out <- function(numberCores){
                cluster <- parallel::makeCluster(numberCores)
                doParallel::registerDoParallel(cluster)
                doRNG::registerDoRNG(seed = seed, once = once)
            }
        }
        unlockBinding("registerCores", as.environment("package:parallelSVM"))
        assign("registerCores", out, "package:parallelSVM")
        lockBinding("registerCores", as.environment("package:parallelSVM"))
        unlockBinding("registerCores", getNamespace("parallelSVM"))
        assign("registerCores", out, getNamespace("parallelSVM"))
        lockBinding("registerCores", getNamespace("parallelSVM"))
        #unlockBinding("registerCores", as.environment("package:parallelSVM"))
        invisible()
    }
    

    【讨论】:

    • 非常感谢您非常详细的回答。这比在包中操作函数要好得多。非常令人印象深刻。如果您的函数能够进入 parallelSVM 包,那就太好了!
    • 同意,几个包可以使用类似的工具。然而,这个包最后一次更新是在 2015 年,所以我怀疑它是否会被实施(也许是我未来的项目,对于一个包项目,嗯......)。
    • 确实如此。 registerCores(...) 在每次调用时启动一个新集群。最佳情况下,它应该检查一个活动集群,如果一个不存在,则只启动一个。目前,直到我有时间在 cluster &lt;- parallel::makeCluster(numberCores) 之前在 out 函数中添加 foreach::registerDoSEQ() 之前,将在集群重新打开之前关闭集群,关闭任何并行会话。感谢doParallel 的作者answer here
    • 深表敬意感谢您的帮助 @MirkoLudewig 和其他定量公平科学用户实现主要可重复模型(以及更大规模的负责任科学)的方法 如果可以的话,值得 +100,因为负责任的科学具有很高的道德原因。在您的职业生涯中一切顺利,让我们传播可重复(可再现)科学实验的伦理道德(在当今世界各地不那么普遍-尽管可能,尽管必要,但在我们附近没有更好的法律艺术程序。[信念根本不够]
    • @Oliver 我有一个问题,我无法根据我对您上面提出的 R-package-management 技巧的阅读水平做出决定吗?你是有意还是无意地将所有核心的进程设置为具有相同的一个种子值(尽管这在法律上会减少对 PRNG 依赖的生成模型的多样性的追求),还是你将这些进程中的每一个都设置为种子值?有点不同的种子(其中 N0, N0+1, N0+2, N0+3, ... 就足够了,因为至少这种方式的非相关输出的性质是非奇异的,但可重复的,种子伪随机数字生成算法)?
    猜你喜欢
    • 1970-01-01
    • 2014-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多