Caret glmnet vs cv.glmnet

14

Parece haber mucha confusión en la comparación de usar glmnetdentro caretpara buscar una lambda óptima y usar cv.glmnetpara hacer la misma tarea.

Se plantearon muchas preguntas, por ejemplo:

Modelo de clasificación train.glmnet vs. cv.glmnet?

¿Cuál es la forma correcta de usar glmnet con caret?

Validación cruzada de `glmnet` usando` caret`

pero no se ha dado respuesta, lo que podría deberse a la reproducibilidad de la pregunta. Después de la primera pregunta, doy un ejemplo bastante similar, pero tengo la misma pregunta: ¿Por qué las lambdas estimadas son tan diferentes?

library(caret)
library(glmnet)
set.seed(849)
training <- twoClassSim(50, linearVars = 2)
set.seed(849)
testing <- twoClassSim(500, linearVars = 2)
trainX <- training[, -ncol(training)]
testX <- testing[, -ncol(testing)]
trainY <- training$Class

# Using glmnet to directly perform CV
set.seed(849)
cvob1=cv.glmnet(x=as.matrix(trainX),y=trainY,family="binomial",alpha=1, type.measure="auc", nfolds = 3,lambda = seq(0.001,0.1,by = 0.001),standardize=FALSE)

cbind(cvob1$lambda,cvob1$cvm)

# best parameter
cvob1$lambda.mi

# best coefficient
coef(cvob1, s = "lambda.min")


# Using caret to perform CV
cctrl1 <- trainControl(method="cv", number=3, returnResamp="all",classProbs=TRUE,summaryFunction=twoClassSummary)
set.seed(849)
test_class_cv_model <- train(trainX, trainY, method = "glmnet", trControl = cctrl1,metric = "ROC",
                             tuneGrid = expand.grid(alpha = 1,lambda = seq(0.001,0.1,by = 0.001)))


test_class_cv_model 

# best parameter
test_class_cv_model$bestTune

# best coefficient
coef(test_class_cv_model$finalModel, test_class_cv_model$bestTune$lambda)

Para resumir, las lambdas óptimas se dan como:

  • 0.055 usando cv.glmnet()

  • 0.001 usando train()

Sé que usar standardize=FALSEin cv.glmnet()no es aconsejable, pero realmente quiero comparar ambos métodos usando los mismos requisitos previos. Como explicación principal, creo que el enfoque de muestreo para cada pliegue podría ser un problema, pero uso las mismas semillas y los resultados son bastante diferentes.

Entonces, ¿estoy realmente atrapado en por qué los dos enfoques son tan diferentes, aunque deberían ser bastante similares? - Espero que la comunidad tenga alguna idea de cuál es el problema aquí.

Jogi
fuente

Respuestas:

16

Veo dos problemas aquí. Primero, su conjunto de entrenamiento es demasiado pequeño en relación con su conjunto de pruebas. Normalmente, desearíamos un conjunto de entrenamiento que sea al menos comparable en tamaño al conjunto de prueba. Otra nota es que para la validación cruzada, no está utilizando el conjunto de prueba en absoluto, porque el algoritmo básicamente crea conjuntos de prueba para usted utilizando el "conjunto de entrenamiento". Por lo tanto, sería mejor utilizar más datos como conjunto de entrenamiento inicial.

Segundo, 3 pliegues es demasiado pequeño para que tu CV sea confiable. Por lo general, se recomiendan 5-10 pliegues ( nfolds = 5por cv.glmnety number=5para caret). Con estos cambios, obtuve los mismos valores lambda en los dos métodos y estimaciones casi idénticas:

set.seed(849)
training <- twoClassSim(500, linearVars = 2)
set.seed(849)
testing <- twoClassSim(50, linearVars = 2)
trainX <- training[, -ncol(training)]
testX <- testing[, -ncol(testing)]
trainY <- training$Class

# Using glmnet to directly perform CV
set.seed(849)
cvob1=cv.glmnet(x=as.matrix(trainX), y=trainY,family="binomial",alpha=1, 
                type.measure="auc", nfolds = 5, lambda = seq(0.001,0.1,by = 0.001),
                standardize=FALSE)

cbind(cvob1$lambda,cvob1$cvm)

# best parameter
cvob1$lambda.min

# best coefficient
coef(cvob1, s = "lambda.min")


# Using caret to perform CV
cctrl1 <- trainControl(method="cv", number=5, returnResamp="all",
                       classProbs=TRUE, summaryFunction=twoClassSummary)
set.seed(849)
test_class_cv_model <- train(trainX, trainY, method = "glmnet", 
                             trControl = cctrl1,metric = "ROC",
                             tuneGrid = expand.grid(alpha = 1,
                                                    lambda = seq(0.001,0.1,by = 0.001)))

test_class_cv_model 

# best parameter
test_class_cv_model$bestTune

# best coefficient
coef(test_class_cv_model$finalModel, test_class_cv_model$bestTune$lambda)

Resultado:

> cvob1$lambda.min
[1] 0.001

> coef(cvob1, s = "lambda.min")
8 x 1 sparse Matrix of class "dgCMatrix"
1
(Intercept) -0.781015706
TwoFactor1  -1.793387005
TwoFactor2   1.850588656
Linear1      0.009341356
Linear2     -1.213777391
Nonlinear1   1.158009360
Nonlinear2   0.609911748
Nonlinear3   0.246029667

> test_class_cv_model$bestTune
alpha lambda
1     1  0.001

> coef(test_class_cv_model$finalModel, test_class_cv_model$bestTune$lambda)
8 x 1 sparse Matrix of class "dgCMatrix"
1
(Intercept) -0.845792624
TwoFactor1  -1.786976586
TwoFactor2   1.844767690
Linear1      0.008308165
Linear2     -1.212285068
Nonlinear1   1.159933335
Nonlinear2   0.676803555
Nonlinear3   0.309947442
ESTADÍSTICAS
fuente
Muchas gracias por su respuesta, tiene mucho sentido para mí. Como soy un novato en CV, no tomé en cuenta el a) el tamaño de la muestra yb) los pliegues.
Jogi
¡Gracias por la publicacion! Entonces, si lo entendí bien, generalmente uno divide el conjunto de datos en un conjunto de entrenamiento grande y un conjunto de prueba más pequeño (= holdout) y realiza el CV k-fold en el conjunto de entrenamiento. Finalmente, uno valida en el conjunto de prueba, utilizando los resultados del CV, ¿verdad?
Jogi
@Jogi Esa sería la forma de hacerlo. También puede usar todo el conjunto de datos para CV si no necesita más validación, ya que CV ya selecciona los mejores parámetros en función del rendimiento promedio del modelo en cada iteración de conjuntos de pruebas.
ESTADOS