web-dev-qa-db-fra.com

Modèles parallèles entièrement reproductibles utilisant le curseur

Lorsque j'exécute 2 forêts aléatoires dans caret, j'obtiens exactement les mêmes résultats si je définis une graine aléatoire:

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

Cependant, si j'enregistre un back-end parallèle pour accélérer la modélisation, j'obtiens un résultat différent chaque fois que j'exécute le modèle:

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"

Existe-t-il un moyen de résoudre ce problème? Une suggestion a été d'utiliser le package doRNG , mais train utilise des boucles imbriquées, qui ne sont actuellement pas prises en charge:

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.

MISE À JOUR: Je pensais que ce problème pouvait être résolu en utilisant doSNOW et clusterSetupRNG, mais je n'y arrivais pas vraiment.

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)

Quelle est la particularité de foreach, et pourquoi n'utilise-t-il pas les graines que j'ai initiées sur le cluster? les objets a et b sont identiques, alors pourquoi pas model1 et model2?

44
Zach

Une façon simple d'exécuter un modèle entièrement reproductible en mode parallèle à l'aide du package caret consiste à utiliser l'argument semences lors de l'appel du contrôle de train. Ici, la question ci-dessus est résolue, consultez la page d'aide de trainControl pour plus d'informations.

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
49
BBrill

Caret utilise donc le package foreach pour se paralléliser. Il existe très probablement un moyen de définir la valeur de départ à chaque itération, mais nous aurions besoin de configurer plus d'options dans train.

Vous pouvez également créer une fonction de modélisation personnalisée qui imite celle interne pour les forêts aléatoires et définir la graine vous-même.

Max

9
topepo