Différents résultats de randomForest via caret et du package randomForest de base

14

Je suis un peu confus: en quoi les résultats d'un modèle formé via le signe d'insertion peuvent-ils différer du modèle de l'emballage d'origine? J'ai lu si le prétraitement est nécessaire avant la prédiction à l'aide de FinalModel de RandomForest avec package caret? mais je n'utilise aucun prétraitement ici.

J'ai formé différentes forêts aléatoires en utilisant le package caret et en ajustant différentes valeurs de mesure.

> cvCtrl = trainControl(method = "repeatedcv",number = 10, repeats = 3, classProbs = TRUE, summaryFunction = twoClassSummary)
> newGrid = expand.grid(mtry = c(2,4,8,15))
> classifierRandomForest = train(case_success ~ ., data = train_data, trControl = cvCtrl, method = "rf", metric="ROC", tuneGrid = newGrid)
> curClassifier = classifierRandomForest

J'ai trouvé que mtry = 15 était le meilleur paramètre sur les données de formation:

> curClassifier
 ...
Resampling results across tuning parameters:

mtry  ROC    Sens   Spec   ROC SD   Sens SD  Spec SD
 4    0.950  0.768  0.957  0.00413  0.0170   0.00285
 5    0.951  0.778  0.957  0.00364  0.0148   0.00306
 8    0.953  0.792  0.956  0.00395  0.0152   0.00389
10    0.954  0.797  0.955  0.00384  0.0146   0.00369
15    0.956  0.803  0.951  0.00369  0.0155   0.00472

ROC was used to select the optimal model using  the largest value.
The final value used for the model was mtry = 15. 

J'ai évalué le modèle avec une courbe ROC et une matrice de confusion:

##ROC-Curve
predRoc = predict(curClassifier, test_data, type = "prob")
myroc = pROC::roc(test_data$case_success, as.vector(predRoc[,2]))
plot(myroc, print.thres = "best")

##adjust optimal cut-off threshold for class probabilities
threshold = coords(myroc,x="best",best.method = "closest.topleft")[[1]] #get optimal cutoff threshold
predCut = factor( ifelse(predRoc[, "Yes"] > threshold, "Yes", "No") )


##Confusion Matrix (Accuracy, Spec, Sens etc.)
curConfusionMatrix = confusionMatrix(predCut, test_data$case_success, positive = "Yes")

La matrice de confusion et la précision qui en résultent:

Confusion Matrix and Statistics
      Reference
Prediction   No  Yes
   No  2757  693
   Yes  375 6684

           Accuracy : 0.8984
 ....

Maintenant, j'ai formé un Random Rorest avec les mêmes paramètres et les mêmes training_data en utilisant le package randomForest de base:

randomForestManual <- randomForest(case_success ~ ., data=train_data, mtry = 15, ntree=500,keep.forest=TRUE)
curClassifier = randomForestManual

Encore une fois, j'ai créé des prédictions pour les mêmes données de test que ci-dessus et évalué la matrice de confusion avec le même code que ci-dessus. Mais maintenant, j'ai obtenu différentes mesures:

Confusion Matrix and Statistics

      Reference
Prediction   No  Yes
       No  2702  897
       Yes  430 6480

           Accuracy : 0.8737 
           ....

Quelle est la raison? Qu'est-ce que je rate?

Malte
la source
3
Avez-vous utilisé la même valeur pour la graine aléatoire pour les deux modèles?
mmmmmmmmmm
Je le pense. J'ai défini la graine plus tôt dans le code lors de la division de l'ensemble de données en données de formation et de test, puis j'ai formé le modèle caret, puis formé le modèle rf "original". Ainsi, la graine devrait rester la même une fois fixée au début, n'est-ce pas?
Malte
J'ai essayé d'insérer un autre set.seed directement avant d'entraîner le modèle rf "original". Ne résout pas le problème, malheureusement.
Malte
3
Vous devriez tester cela en utilisant l' seedsargument detrainControl
topepo

Réponses:

4

Je pense que la question, bien que quelque peu triviale et "programmatique" à première vue, touche à deux questions principales très importantes dans les statistiques modernes:

  1. reproductibilité des résultats et
  2. algorithmes non déterministes.

La raison des différents résultats est que les deux procédures sont entraînées à l'aide de différentes graines aléatoires. Les forêts aléatoires utilisent un sous-ensemble aléatoire à partir des variables de l'ensemble de données comme candidats à chaque division (c'est l' mtryargument et se rapporte à la méthode du sous-espace aléatoire ) ainsi que des sacs (agrégats bootstrap) l'ensemble de données d'origine pour diminuer la variance du modèle. Ces deux procédures d'échantillonnage aléatoire internes ne sont pas déterministes entre les différentes exécutions de l'algorithme. L'ordre aléatoire de l'échantillonnage est contrôlé par les graines aléatoires utilisées. Si les mêmes graines étaient utilisées, on obtiendrait exactement les mêmes résultats dans les deux cas où la randomForestroutine est appelée; à la fois en internecaret::trainainsi qu'en externe lors de l'ajustement manuel d'une forêt aléatoire. J'attache un simple extrait de code pour illustrer cela. Veuillez noter que j'utilise un très petit nombre d'arbres (argument:) ntreepour continuer à m'entraîner rapidement, il devrait généralement être beaucoup plus grand.

library(caret)

set.seed(321)
trainData <- twoClassSim(5000, linearVars = 3, noiseVars = 9)
testData  <- twoClassSim(5000, linearVars = 3, noiseVars = 9)

set.seed(432)
mySeeds <- sapply(simplify = FALSE, 1:26, function(u) sample(10^4, 3))
cvCtrl = trainControl(method = "repeatedcv", number = 5, repeats = 5, 
                      classProbs = TRUE, summaryFunction = twoClassSummary, 
                      seeds = mySeeds)

fitRFcaret = train(Class ~ ., data = trainData, trControl = cvCtrl, 
                   ntree = 33, method = "rf", metric="ROC")

set.seed( unlist(tail(mySeeds,1))[1])
fitRFmanual <- randomForest(Class ~ ., data=trainData, 
                            mtry = fitRFcaret$bestTune$mtry, ntree=33) 

À ce stade, l' caret.trainobjet fitRFcaretainsi que l' randomForestobjet défini manuellement fitRFmanualont été formés en utilisant les mêmes données mais surtout en utilisant les mêmes graines aléatoires lors de l'ajustement de leur modèle final. En tant que tel, lorsque nous essaierons de prédire l'utilisation de ces objets et que nous n'effectuons aucun prétraitement de nos données, nous obtiendrons les mêmes réponses exactes.

all.equal(current =  as.vector(predict(fitRFcaret, testData)), 
          target = as.vector(predict(fitRFmanual, testData)))
# TRUE

Juste pour clarifier ce point un peu plus loin: predict(xx$finalModel, testData)et predict(xx, testData)sera différent si l'on définit l' preProcessoption lors de l'utilisation train. En revanche, lorsque vous utilisez finalModeldirectement, il est équivalent d'utiliser la predictfonction du modèle ajusté ( predict.randomForestici) au lieu de predict.train; aucun prétraitement n'a lieu. De toute évidence, dans le scénario décrit dans la question d'origine où aucun prétraitement n'est effectué, les résultats seront les mêmes lors de l'utilisation de l' objet finalModelajusté manuellement randomForestou de l' caret.trainobjet.

all.equal(current =  as.vector(predict(fitRFcaret$finalModel, testData)), 
          target = as.vector(predict(fitRFmanual, testData)))
 # TRUE

all.equal(current =  as.vector(predict(fitRFcaret$finalModel, testData)),
          target = as.vector(predict(fitRFcaret, testData)))
# TRUE

Je suggère fortement que vous définissiez toujours la graine aléatoire utilisée par R, MATLAB ou tout autre programme utilisé. Sinon, vous ne pouvez pas vérifier la reproductibilité des résultats (ce qui n'est peut-être pas la fin du monde) ni exclure un bogue ou un facteur externe affectant les performances d'une procédure de modélisation (qui oui, ça craint en quelque sorte). Un grand nombre des principaux algorithmes ML (par exemple, l'augmentation du gradient, les forêts aléatoires, les réseaux de neurones extrêmes) utilisent certaines procédures de rééchantillonnage internes pendant leurs phases de formation, la définition des états de départ aléatoires avant (ou parfois même à l'intérieur) de leur phase de formation peut être importante.

usεr11852 dit Reinstate Monic
la source
L'important était de définir explicitement l'argument Seeds dans "trainControl" en utilisant l'argument "Seeds"
Malte
Oui bien sûr. Je voulais m'assurer que la question de savoir pourquoi cela était nécessaire était entièrement clarifiée.
usεr11852 dit Réintégrer Monic le
Comment puis-je exécuter trainpour qu'il soit exactement équivalent à randomForest? J'ai essayé method="none"mais je ne sais pas comment mettre la graine à la valeur unique. Merci.
Simon Woodward
Toutes mes excuses, mais il est difficile de savoir si vous en avez preProcesset comment les randomForestformer pour commencer. En général, en supposant que nous n'avons pas d'étapes de prétraitement, nous devons nous assurer que la graine et les hyperparamètres (ici seulement mtry) utilisés sont les mêmes.
usεr11852 dit Réintégrer Monic
0

Les prédictions de curClassifierne sont pas les mêmes que celles du curClassifier$finalModel lien . Vous avez reproduit le finalModelet le comparez à l' predict.trainobjet.

tomaz
la source
1
Bien que ce que vous dites soit vrai, il est malheureusement un peu trompeur dans le réglage actuel car aucun pré-traitement n'est effectué par l'OP. Les deux predictdevraient (et font en fait) donner les mêmes prédictions dans le cas où l'OP explore. Je clarifie un peu plus ce point dans mon post.
usεr11852 dit Réintégrer Monic