Caret glmnet vs cv.glmnet

14

Il semble y avoir beaucoup de confusion dans la comparaison de l'utilisation à l' glmnetintérieur caretpour rechercher un lambda optimal et à utiliser cv.glmnetpour faire la même tâche.

De nombreuses questions ont été posées, par exemple:

Modèle de classification train.glmnet vs cv.glmnet?

Quelle est la bonne façon d'utiliser glmnet avec caret?

Validation croisée de `glmnet` à l'aide de` caret`

mais aucune réponse n'a été donnée, ce qui pourrait être dû à la reproductibilité de la question. Après la première question, je donne un exemple assez similaire mais j'ai la même question: pourquoi les lambdas estimés sont-ils si différents?

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)

Pour résumer, les lambdas optimales sont données comme:

  • 0,055 en utilisant cv.glmnet()

  • 0,001 en utilisant train()

Je sais que l' utilisation standardize=FALSEdanscv.glmnet() n'est pas recommandée, mais je veux vraiment comparer les deux méthodes en utilisant les mêmes conditions préalables. Comme explication principale, je pense que l'approche d'échantillonnage pour chaque pli pourrait être un problème - mais j'utilise les mêmes graines et les résultats sont assez différents.

Je suis donc vraiment coincé sur la raison pour laquelle les deux approches sont si différentes, alors qu'elles devraient être assez similaires? - J'espère que la communauté a une idée du problème ici

Jogi
la source

Réponses:

17

Je vois deux problèmes ici. Tout d'abord, votre ensemble d'entraînement est trop petit par rapport à votre ensemble de tests. Normalement, nous voudrions un ensemble de formation qui soit au moins comparable en taille à l'ensemble de test. Une autre remarque est que pour la validation croisée, vous n'utilisez pas du tout l'ensemble de test, car l'algorithme crée essentiellement des ensembles de test pour vous en utilisant l '"ensemble de formation". Il serait donc préférable d'utiliser davantage de données comme ensemble de formation initiale.

Deuxièmement, 3 plis est trop petit pour que votre CV soit fiable. En règle générale, 5 à 10 plis sont recommandés ( nfolds = 5pourcv.glmnet et number=5pour caret). Avec ces changements, j'ai obtenu les mêmes valeurs lambda entre les deux méthodes et des estimations presque identiques:

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)

Résultat:

> 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
Statistiques
la source
Merci beaucoup pour votre réponse - cela me semble parfaitement logique. Étant donné que je suis un nouveau venu chez CV, je n'ai pas tenu compte a) de la taille de l'échantillon et b) des plis.
Jogi
Merci pour le post! Donc, si j'ai bien compris, on divise généralement l'ensemble de données en un grand ensemble de formation et un plus petit ensemble de test (= holdout) et effectuez le k-fold CV sur l'ensemble de formation. Enfin on valide sur l'ensemble de test, en utilisant les résultats du CV non?
Jogi
@Jogi Ce serait la façon de procéder. Vous pouvez également simplement utiliser l'ensemble de données pour CV si vous n'avez pas besoin de validation supplémentaire, car CV sélectionne déjà les meilleurs paramètres en fonction des performances moyennes du modèle à chaque itération des ensembles de tests.
StAtS