Короче говоря, в parallelSVM
нет реализованного метода для решения этой проблемы.Однако пакет использует пакеты foreach
и doParallel
для обработки своих параллельных операций.И достаточно усердно копаясь в stackoverflow, решение возможно!
Кредиты на этот ответ , на использование пакета doRNG
и этот ответ за предоставление мнеидея для более простого закрытого решения.
Решение:
В пакете parallelSVM
распараллеливание происходит с помощью функций parallelSVM::registerCores
.Эта функция просто вызывает doParallel::registerDoParallel
с количеством ядер, без дополнительных аргументов.Моя идея состоит в том, чтобы просто изменить функцию parallelSVM::registerCores
таким образом, чтобы она автоматически устанавливала начальное значение после создания нового кластера.
При выполнении параллельных вычислений, в которых вам нужно параллельное начальное число, необходимо обеспечить две вещи:
- Начальное число нужно передать каждому узлу в кластере
- Генератор должен быть асимптотически случайным в кластерах.
К счастью, пакет doRNG
обрабатывает первый и использует начальное число, которое в порядке2. Используя комбинацию unlockNamespace
и assign
, мы можем перезаписать parallelSVM::registerCores
, так что он включает вызов doRNG::registerDoRNG
с соответствующим начальным числом (функция в конце ответа).Делая это, мы на самом деле можем получить надлежащую воспроизводимость, как показано ниже:
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
кредитует этот ответ .Обратите внимание, что нам, возможно, не придется удваивать назначение, но здесь я просто повторил ответ, не проверяя, можно ли еще уменьшить код.
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()
}