Caret glmnet vs cv.glmnet

14

Wydaje się, że istnieje wiele zamieszania w porównaniu używania glmnetwewnątrz w caretcelu znalezienia optymalnej lambdy i korzystania cv.glmnetz tego samego zadania.

Zadano wiele pytań, np .:

Model klasyfikacji train.glmnet vs. cv.glmnet?

Jaki jest właściwy sposób używania glmnet z karetką?

Cross-validation `glmnet` za pomocą` caret`

ale nie udzielono odpowiedzi, co może wynikać z odtwarzalności pytania. Po pierwszym pytaniu podam dość podobny przykład, ale mam to samo pytanie: dlaczego szacowane lambdas są tak różne?

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)

Podsumowując, optymalne lambdas są podane jako:

  • 0,055 przy użyciu cv.glmnet()

  • 0,001 przy użyciu train()

Wiem, że używanie standardize=FALSEw cv.glmnet()nie jest wskazane, ale naprawdę chcę porównać obie metody, stosując te same warunki wstępne. Jako główne wyjaśnienie, myślę, że podejście do próbkowania dla każdej zakładki może stanowić problem - ale używam tych samych nasion, a wyniki są zupełnie inne.

Więc naprawdę utknąłem na tym, dlaczego te dwa podejścia są tak różne, a jednocześnie powinny być całkiem podobne? - Mam nadzieję, że społeczność ma pojęcie, o co tu chodzi

Jogi
źródło

Odpowiedzi:

17

Widzę tutaj dwa problemy. Po pierwsze, zestaw treningowy jest za mały w stosunku do zestawu testowego. Zwykle chcielibyśmy mieć zestaw treningowy, który jest co najmniej porównywalny pod względem wielkości do zestawu testowego. Inna uwaga jest taka, że ​​w przypadku Cross Validation wcale nie używasz zestawu testowego, ponieważ algorytm zasadniczo tworzy zestawy testowe za pomocą „zestawu szkoleniowego”. Lepiej więc będzie użyć większej ilości danych jako początkowego zestawu treningowego.

Po drugie, 3 fałdy są zbyt małe, aby twoje CV było wiarygodne. Zazwyczaj zaleca się 5-10 fałd ( nfolds = 5dla cv.glmneti number=5dla caret). Dzięki tym zmianom uzyskałem te same wartości lambda dla obu metod i prawie identyczne szacunki:

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)

Wynik:

> 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
STAtS
źródło
Bardzo dziękuję za odpowiedź - ma dla mnie idealny sens. Ponieważ jestem nowicjuszem w CV, nie uwzględniłem a) wielkości próbki ib) fałdów.
Jogi,
Dzięki za post! Więc jeśli mam rację, zwykle dzieli się zestaw danych na duży zestaw treningowy i mniejszy zestaw testowy (= blokada) i wykonuję k-fold CV na zestawie treningowym. Na koniec sprawdza się na zestawie testowym, używając wyników CV, prawda?
Jogi,
@ Jogi To byłby sposób na zrobienie tego. Możesz także użyć całego zestawu danych dla CV, jeśli nie potrzebujesz dalszej weryfikacji, ponieważ CV już wybiera najlepsze parametry na podstawie średniej wydajności modelu na każdej iteracji zestawów testowych.
STAtS,