Diferentes resultados de randomForest a través de caret y el paquete básico randomForest

14

Estoy un poco confundido: ¿cómo pueden los resultados de un modelo entrenado a través de caret diferir del modelo en el paquete original? Yo leo si se necesita un procesamiento previo antes de la predicción usando FinalModel de RandomForest con el paquete caret? pero no uso ningún preprocesamiento aquí.

Entrené a diferentes bosques aleatorios usando el paquete de caret y sintonizando diferentes valores mtry.

> 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

Encontré que mtry = 15 es el mejor parámetro en training_data:

> 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. 

Evalué el modelo con una curva ROC y una matriz de confusión:

##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 matriz de confusión resultante y la precisión:

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

           Accuracy : 0.8984
 ....

Ahora entrené un Rorest Random con los mismos parámetros y los mismos datos de entrenamiento usando el paquete básico randomForest:

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

Nuevamente, creé predicciones para los mismos test_data de arriba y evalué la matriz de confusión con el mismo código que arriba. Pero ahora tengo diferentes medidas:

Confusion Matrix and Statistics

      Reference
Prediction   No  Yes
       No  2702  897
       Yes  430 6480

           Accuracy : 0.8737 
           ....

¿Cuál es la razón? ¿Qué me estoy perdiendo?

Malte
fuente
3
¿Usó el mismo valor para la semilla aleatoria para ambos modelos?
mmmmmmmmmm
Creo que sí. Establecí la semilla antes en el código al dividir el conjunto de datos en datos de entrenamiento y prueba, luego entrené el modelo de caret, luego entrené el modelo rf "original". Entonces, la semilla debe permanecer igual una vez establecida al principio, ¿no?
Malte
Traté de insertar otro set.seed directamente antes de entrenar el modelo rf "original". No resuelve el problema, desafortunadamente.
Malte
3
Debe probar esto usando el seedsargumento detrainControl
topepo

Respuestas:

4

Creo que la pregunta, aunque algo trivial y "programática" en primera lectura, toca dos cuestiones principales que son muy importantes en las estadísticas modernas:

  1. reproducibilidad de resultados y
  2. algoritmos no deterministas.

La razón de los diferentes resultados es que los dos procedimientos se entrenan utilizando diferentes semillas aleatorias. Los bosques aleatorios usan un subconjunto aleatorio de las variables del conjunto de datos completo como candidatos en cada división (esa es lamtry argumento y se relaciona con el método de subespacio aleatorio ), así como bolsas el conjunto de datos original de (agregados de arranque) para disminuir la varianza del modelo. Estos dos procedimientos de muestreo aleatorio interno no son deterministas entre diferentes ejecuciones del algoritmo. El orden aleatorio en que se realiza el muestreo está controlado por las semillas aleatorias utilizadas. Si se usaran las mismas semillas, se obtendrían exactamente los mismos resultados en ambos casos donde randomForestse llama la rutina; ambos internamente encaret::trainasí como externamente cuando se ajusta un bosque aleatorio manualmente. Adjunto un fragmento de código simple para mostrar esto. Tenga en cuenta que uso un número muy pequeño de árboles (argumento:) ntreepara mantener el entrenamiento rápido, generalmente debería ser mucho más grande.

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) 

En este punto, tanto el caret.trainobjeto fitRFcaretcomo el randomForestobjeto definido manualmente fitRFmanualhan sido entrenados usando los mismos datos pero importantemente usando las mismas semillas aleatorias al ajustar su modelo final. Por lo tanto, cuando intentaremos predecir el uso de estos objetos y porque no procesamos previamente nuestros datos , obtendremos las mismas respuestas exactas.

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

Solo para aclarar esto más adelante, apunte un poco más: predict(xx$finalModel, testData)y predict(xx, testData)será diferente si se configura la preProcessopción cuando se usa train. Por otro lado, cuando se usa finalModeldirectamente, es equivalente usar elpredict función del modelo ajustado ( predict.randomForestaquí) en lugar de predict.train; no tiene lugar el preprocesamiento. Obviamente, en el escenario descrito en la pregunta original donde no se realiza el procesamiento previo, los resultados serán los mismos cuando se utiliza el objeto finalModelajustado manualmente randomForesto el caret.trainobjeto.

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

Te sugiero encarecidamente que siempre establezca la semilla aleatoria utilizada por R, MATLAB o cualquier otro programa utilizado. De lo contrario, no puede verificar la reproducibilidad de los resultados (lo cual está bien, puede que no sea el fin del mundo) ni excluir un error o factor externo que afecte el desempeño de un procedimiento de modelado (que sí, apesta). Muchos de los principales algoritmos de ML (p. Ej., Aumento de gradiente, bosques aleatorios, redes neuronales extremas) emplean ciertos procedimientos internos de remuestreo durante sus fases de entrenamiento, estableciendo los estados de semilla aleatorios antes (o a veces incluso dentro) de su fase de entrenamiento puede ser importante.

usεr11852 dice Reinstate Monic
fuente
La parte importante fue establecer el argumento de semillas explícitamente en "trainControl" usando el argumento "semillas"
Malte
Sí, por supuesto. Quería asegurarme de que el problema de por qué esto es necesario, se aclaró completamente.
usεr11852 dice Reinstate Monic el
¿Cómo corro trainpara que sea exactamente equivalente a randomForest? Intenté method="none"pero no estoy seguro de cómo establecer la semilla en el valor único. Gracias.
Simon Woodward
Disculpas, pero no está claro si tiene alguna preProcesso cómo randomForestestá entrenado para empezar. En general, suponiendo que no tengamos pasos de preprocesamiento, debemos asegurarnos de que tanto la semilla como los hiperparámetros (aquí solo mtry) utilizados sean iguales.
usεr11852 dice Reinstate Monic
0

Las predicciones de curClassifierno son lo mismo que las predicciones del curClassifier$finalModel enlace . Has reproducido finalModely lo estás comparando con el predict.trainobjeto.

tomaz
fuente
1
Si bien lo que usted dice es cierto, desafortunadamente es un poco engañoso en la configuración actual ya que el OP no procesa previamente. Ambos predictdeberían (y lo hacen en realidad) dar las mismas predicciones en el caso de que el OP explore. Aclaro un poco más este punto en mi publicación.
usεr11852 dice Reinstate Monic