I'm using glmnet
to fit a ridge regression model on some data and evaluate the model's test MSE. The lambda value I select is derived from cross-validation. I'm using the College dataset from ISLR2, predicting applications to each college.
I have tried the following two approaches, and while in theory I should have the same result, I don't and I'm not sure why.
First way
- I use
cv.glmnet()
to perform cross validation on the data, extracting the lambda with the lowest validation MSE - Then I fit a ridge regression model using
glmnet()
on the data with the previously computed lambda - Predict response on the test data using the fitted model and compute the test MSE
# Perform the cross validation
cv.ridge <- cv.glmnet(model.matrix(Apps~.,train), train$Apps, alpha=0, nfold=100)
# Store the best lambda value
best.lambda <- cv.ridge$lambda.min
# Fit ridge regression model with that lambda
ridge.fit <- glmnet(model.matrix(Apps~.,train), train$Apps, alpha=0, lambda=best.lambda)
# Predict the test response
pred.out <- predict(cv.ridge, newx = model.matrix(Apps~.,test), s=best.lambda)
# Compute test MSE
mean((pred.out- Y_test)^2)
>> 2206587
Second Way
- Once again I use
cv.glmnet()
to perform cross validation on the data - I then predict the response on the test data directly with the
cv.glmnet
object, using the lambda value with the lowest validation MSE - Compute the test MSE
# Perform the cross validation
cv.ridge <- cv.glmnet(model.matrix(Apps~.,train), train$Apps, alpha=0, nfold=100)
# Store the best lambda value
best.lambda <- cv.ridge$lambda.min
# Predict the test response
pred.out <- predict(cv.ridge, newx = model.matrix(Apps~.,test), s=best.lambda)
# Compute test MSE
mean((pred.out- Y_test)^2)
>>> 2204831
Why do these two approaches have different test MSE's? The only difference between the two ways is that in the second way I use the cv.glmnet
object instead of the glmnet
object in the predict()
call.
I checked the coefficients of the models and they are not the same either.
# Coefficients from the glmnet() call on the specified lambda
coef(ridge.fit)
# Coefficients of the cv.glmnet() call given the same specified lambda
predict(cv.ridge, type='coefficients', s=best.lambda)
The coefficients are slightly different. Which I guess is why the test MSE's differ. But I'm not sure why this should be the case.
In both ways, since the lambda constraint specified is identical and the data used to fit the model is identical, shouldn't the resulting two ridge regression models be the same?
Follow up :
I have tried setting the s
parameter in the predict
call but that doesn't seem to work either.
cv.ridge
MSE :
# Fit cross-validated ridge regression model
cv.ridge <- cv.glmnet(model.matrix(Apps~.,College), College$Apps, alpha=0, nfold=100)
# Make prediction using lambda that minimizes cross-val MSE
pred.out <- predict(cv.ridge, model.matrix(Apps~.,College), s="lambda.min")
# Compute MSE
mean((pred.out- College$Apps)^2)
>> 1358455
glmnet
way :
# Explicitly fit a ridge regression model on the same data using the previously computed lambda that minimizes CV mse
ridge.fit <- glmnet(model.matrix(Apps~.,College), College$Apps, alpha=0, lambda=cv.ridge$lambda.min)
# Make prediction
pred.out <- predict(ridge.fit, model.matrix(Apps~.,College))
# Compute MSE
mean((pred.out- College$Apps)^2)
>> 1359837
The issue persists.
Note that in the glmnet
way I didn't set any s
parameter since the provided glmnet
object only contains the fitted model with the lambda which minimizes CV MSE. The same lambda value used when the cv.ridge
object does the prediction as well.
Setting s='lambda.min'
doesn't change the result either.