Caret glmnet vs cv.glmnet

14

Parece haver muita confusão na comparação entre usar glmnetdentro caretpara procurar uma lambda ideal e usar cv.glmnetpara fazer a mesma tarefa.

Muitas perguntas foram feitas, por exemplo:

Modelo de classificação train.glmnet vs. cv.glmnet?

Qual é a maneira correta de usar glmnet com cursor?

Validação cruzada de `glmnet` usando` caret`

mas nenhuma resposta foi dada, o que pode dever-se à reprodutibilidade da pergunta. Seguindo a primeira pergunta, dou um exemplo bastante semelhante, mas tenho a mesma pergunta: Por que as lambdas estimadas são tão 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, as lambdas ideais são fornecidas como:

  • 0,055 usando cv.glmnet()

  • 0,001 usando train()

Sei que usar standardize=FALSEin cv.glmnet()não é aconselhável, mas quero realmente comparar os dois métodos usando os mesmos pré-requisitos. Como explicação principal, acho que a abordagem de amostragem para cada dobra pode ser um problema - mas uso as mesmas sementes e os resultados são bem diferentes.

Então, eu estou realmente preocupado com o porquê das duas abordagens serem tão diferentes, embora devam ser bem parecidas. - Espero que a comunidade tenha alguma idéia de qual é o problema aqui

Jogi
fonte

Respostas:

16

Eu vejo duas questões aqui. Primeiro, seu conjunto de treinamento é muito pequeno em relação ao seu conjunto de testes. Normalmente, queremos um conjunto de treinamento que seja pelo menos comparável em tamanho ao conjunto de teste. Outra observação é que, para a Validação Cruzada, você não está usando o conjunto de testes, porque o algoritmo basicamente cria conjuntos de testes para você usando o "conjunto de treinamento". Portanto, seria melhor usar mais dados como seu conjunto de treinamento inicial.

Segundo, três dobras são pequenas demais para que seu currículo seja confiável. Normalmente, são recomendadas 5 a 10 dobras ( nfolds = 5para cv.glmnete number=5para caret). Com essas alterações, obtive os mesmos valores lambda entre os dois métodos e estimativas quase 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
Estatísticas
fonte
Muito obrigado pela sua resposta - faz todo o sentido para mim. Como sou iniciante no CV, não expliquei a) o tamanho da amostra eb) as dobras.
Jogi
Obrigado pelo post! Portanto, se eu entendi direito, normalmente se divide o conjunto de dados em um grande conjunto de treinamento e um menor conjunto de testes (= suporte) e executa o CV de dobra k no conjunto de treinamento. Finalmente, alguém valida no conjunto de testes, usando os resultados do currículo, certo?
Jogi
@Jogi Essa seria a maneira de fazê-lo. Você também pode usar todo o conjunto de dados para o CV se não precisar de validação adicional, pois o CV já seleciona os melhores parâmetros com base no desempenho médio do modelo em cada iteração dos conjuntos de testes.
StAtS 25/08