Полностью воспроизводимые параллельные модели с использованием каретки
Когда я запускаю 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.
ОБНОВЛЕНИЕ: я думал, что эту проблему можно решить с помощью doSNOW
а также clusterSetupRNG
, но я не мог туда добраться.
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, и почему он не использует семена, которые я инициировал в кластере? объекты a
а также b
идентичны, так почему бы и нет model1
а также model2
?
3 ответа
Один простой способ запустить полностью воспроизводимую модель в параллельном режиме, используя caret
пакет использует аргумент seed при вызове управления поездом. Здесь вышеупомянутый вопрос решен, проверьте страницу справки 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 использует пакет foreach для распараллеливания. Скорее всего, есть способ установить начальное число на каждой итерации, но нам нужно будет настроить больше параметров в train
,
В качестве альтернативы вы можете создать собственную функцию моделирования, которая имитирует внутреннюю функцию для случайных лесов, и установить начальное значение самостоятельно.
Максимум
Какую версию карета вы использовали?
Ответ @BBrill правильный. Однако начиная с версии 6.0.64 (15 января 2016 г.) эта проблема учитывается. Вы можете предоставить свой индивидуальный файл , но это не обязательно. Если
trControl$seeds
является
NULL
, caert автоматически сгенерирует их для вас, что обеспечивает воспроизводимость даже при параллельном обучении.
Это поведение можно найти на https://github.com/topepo/caret/commit/9f375a1704e413d0806b73ab8891c7fadc39081c.
Запрос на извлечение: https://github.com/topepo/caret/pull/353
Связанные фрагменты кода:
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 ...)
}
Для более подробной информации вы можете