Caret glmnet vs cv.glmnet

14

Es scheint eine Menge Verwirrung im Vergleich zwischen der Verwendung von glmnetinside caretzur Suche nach einem optimalen Lambda und der Verwendung cv.glmnetderselben Aufgabe zu geben.

Viele Fragen wurden gestellt, zB:

Klassifizierungsmodell train.glmnet vs. cv.glmnet?

Was ist der richtige Weg, um glmnet mit caret zu verwenden?

Quervalidierung von "glmnet" mit "caret"

Es wurde jedoch keine Antwort gegeben, was möglicherweise auf die Reproduzierbarkeit der Frage zurückzuführen ist. Nach der ersten Frage gebe ich ein ganz ähnliches Beispiel, habe aber die gleiche Frage: Warum sind die geschätzten Lambdas so unterschiedlich?

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)

Zusammenfassend werden die optimalen Lambdas wie folgt angegeben:

  • 0.055 mit cv.glmnet()

  • 0,001 mit train()

Ich weiß, dass die Verwendung standardize=FALSEin cv.glmnet()nicht ratsam ist, aber ich möchte wirklich beide Methoden mit den gleichen Voraussetzungen vergleichen. Als wesentliche Erklärung denke ich, dass der Stichprobenansatz für jede Falte ein Problem sein könnte - aber ich verwende die gleichen Samen und die Ergebnisse sind sehr unterschiedlich.

Also bin ich wirklich gespannt, warum die beiden Ansätze so unterschiedlich sind, obwohl sie sich ziemlich ähnlich sein sollten? - Ich hoffe, die Community hat eine Idee, worum es hier geht

Jogi
quelle

Antworten:

16

Ich sehe hier zwei Probleme. Erstens ist Ihr Trainingssatz im Verhältnis zu Ihrem Testsatz zu klein. Normalerweise möchten wir ein Trainingsset, das mindestens so groß ist wie das Testset. Ein weiterer Hinweis ist, dass Sie für die Kreuzvalidierung das Testset überhaupt nicht verwenden, da der Algorithmus im Grunde genommen Testsets für Sie mit dem "Trainingsset" erstellt. Daher ist es besser, wenn Sie einen größeren Teil der Daten als ersten Trainingssatz verwenden.

Zweitens ist 3-fach zu klein für einen verlässlichen Lebenslauf. Normalerweise werden 5-10 Falten empfohlen ( nfolds = 5für cv.glmnetund number=5für caret). Mit diesen Änderungen erhielt ich die gleichen Lambda-Werte für beide Methoden und nahezu identische Schätzungen:

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)

Ergebnis:

> 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
Statistiken
quelle
Vielen Dank für Ihre Antwort - es macht für mich vollkommen Sinn. Da ich CV-Neuling bin, habe ich die a) Größe der Stichprobe und b) die Falten nicht berücksichtigt.
Jogi
Danke für den Beitrag! Wenn ich es richtig verstanden habe, teilt man den Datensatz normalerweise in einen großen Trainingssatz und einen kleineren Testsatz (= Holdout) auf und führt den k-fachen Lebenslauf für den Trainingssatz durch. Zum Schluss validiert man das Test-Set anhand der Ergebnisse des Lebenslaufs, oder?
Jogi
@Jogi Das wäre der Weg, es zu tun. Sie können auch nur den gesamten Datensatz für den Lebenslauf verwenden, wenn Sie keine weitere Validierung benötigen, da der Lebenslauf bereits die besten Parameter auf der Grundlage der durchschnittlichen Leistung des Modells bei jeder Wiederholung von Testsätzen auswählt.
STATUS