You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lasso-ElasticNet调参报错及数据展示问题求助

解决Lasso/ElasticNet模型训练时的缺失值性能指标报错问题

问题背景

用R的caret+glmnet对比Ridge、Lasso、ElasticNet预测美国实际GDP增长率(Real.GDP.Growth),数据集为125行×37列(1个因变量+36个自变量),以RMSE为核心评估指标。Ridge模型运行正常,但Lasso和ElasticNet训练时触发报错:

In nominalTrainWorkflow(x = x, y = y, wts = weights, info = trainInfo,  :
  There were missing values in resampled performance measures.

目前虽能得到结果,但存在异常,变量重要性图无法正常显示。测试发现lambda取值为10^seq(-5,-1,length=100)时无报错,但seq(-2,5,length=100)(更适配数据)会触发问题,计划使用alpha=seq(0,1,0.01)和lambda=seq(0,1,0.01)的调参网格。

报错原因

你的推测完全准确:当lambda值过大时,Lasso/ElasticNet会将所有自变量系数压缩为0,模型对所有样本的预测值变为因变量的均值(常数)。train()函数默认计算R²指标,而R²的计算公式要求预测值方差不为0,常数预测值的方差为0,导致R²计算产生NA,进而触发缺失值性能指标警告。

解决方案

1. 自定义性能评估函数,仅关注RMSE

既然核心评估指标是RMSE,可以跳过R²计算,避免NA值产生。修改trainControl指定专属汇总函数:

# 自定义仅计算RMSE的汇总函数
rmse_summary <- function(data, lev = NULL, model = NULL) {
  rmse_val <- RMSE(data$pred, data$obs)
  out <- c(RMSE = rmse_val)
  out
}

# 更新训练控制参数
custom <- trainControl(method = "repeatedcv",
                       number = 10,
                       repeats = 5,
                       verboseIter = F,
                       summaryFunction = rmse_summary)

这样train()只会计算RMSE,不会因R²的NA值报错。

2. 自动生成适配数据的lambda范围

不要手动指定lambda范围,让glmnet自动生成符合数据特性的序列。train()的glmnet方法默认会生成合理的lambda范围,也可以先预跑模型获取序列再调整:

# 预跑glmnet获取适配的lambda序列
pre_fit <- glmnet(x = as.matrix(US.train[, -1]), y = US.train$Real.GDP.Growth, alpha = 1)
# 截取序列中间部分生成调参网格(避免最大值导致常数预测)
lambda_grid <- seq(min(pre_fit$lambda), max(pre_fit$lambda)*0.95, length.out = 100)

# 使用该网格训练Lasso
lasso <- train(Real.GDP.Growth~. , US.train , method="glmnet",
               trControl=custom,
               tuneGrid = expand.grid(alpha = 1, lambda = lambda_grid))

glmnet生成的序列会从能压缩所有系数为0的最大值开始逐步减小,避免手动设置的lambda过大引发问题。

3. 过滤调参网格中会导致常数预测的lambda

如果坚持手动指定lambda范围,可以先找出不会让系数全为0的lambda上限:

# 找到能将所有系数压缩为0的最小lambda(Lasso为例)
max_safe_lambda <- glmnet(x = as.matrix(US.train[, -1]), y = US.train$Real.GDP.Growth, alpha = 1)$lambda[1]
# 设置略小于该值的lambda上限
lambda_grid <- seq(0.01, max_safe_lambda * 0.95, length.out = 100)

这样就能避免lambda过大导致的常数预测问题。

4. 确认数据标准化

glmnet默认会对自变量做标准化,但如果数据存在异常值或未正确标准化,可能影响lambda效果。可以手动标准化后再训练:

# 标准化自变量
US.train_scaled <- US.train
US.train_scaled[, -1] <- scale(US.train_scaled[, -1])
US.test_scaled <- US.test
US.test_scaled[, -1] <- scale(US.test_scaled[, -1])

# 使用标准化数据训练模型
lasso <- train(Real.GDP.Growth~. , US.train_scaled , method="glmnet",
               trControl=custom,
               tuneGrid = expand.grid(alpha = 1, lambda = seq(0.01,1,by = 0.01)))

数据展示替代方法

若dput(dataUS)超出字符限制,可通过以下方式展示数据信息:

  • 展示数据结构与前几行:str(dataUS)
  • 展示变量统计量:summary(dataUS)
  • 展示前20行数据:head(dataUS, 20)
  • 提供关键统计信息:因变量的均值、方差,自变量的类型(数值/分类)、缺失值比例等

修正后完整代码示例

#TRAIN-TEST
set.seed(240884)
train_rows <- sample(1:nrow(dataUS), 0.805*nrow(dataUS))
US.train <- as.data.frame(dataUS[train_rows,])
US.test <- as.data.frame(dataUS[-train_rows,])

library(glmnet)
library(Metrics)
library(caret)

# 自定义仅计算RMSE的汇总函数
rmse_summary <- function(data, lev = NULL, model = NULL) {
  rmse_val <- RMSE(data$pred, data$obs)
  out <- c(RMSE = rmse_val)
  out
}

#Custom CV rule
custom <- trainControl(method = "repeatedcv",
                       number = 10,
                       repeats = 5,
                       verboseIter = F,
                       summaryFunction = rmse_summary)

# Ridge Regression
set.seed(1234)
ridge <- train(Real.GDP.Growth~. ,US.train, method="glmnet",trControl=custom,
               tuneGrid = expand.grid(alpha = 0,lambda = seq(0.001,20,by = 0.1)))
ridge.predicted <- predict(ridge, s=ridge$lambda.1se, newdata=US.test)
rmse(US.test[,1],ridge.predicted)
# Plot Results
plot(ridge)
plot(ridge$finalModel, xvar = "lambda", label = T)
plot(ridge$finalModel, xvar = 'dev', label=T)
plot(varImp(ridge, scale=T))

# Lasso Regression - 使用glmnet自动生成的lambda范围
set.seed(1234)
pre_lasso <- glmnet(x = as.matrix(US.train[, -1]), y = US.train$Real.GDP.Growth, alpha = 1)
lambda_lasso <- seq(min(pre_lasso$lambda), max(pre_lasso$lambda)*0.95, length.out = 100)
lasso <- train(Real.GDP.Growth~. , US.train , method="glmnet",trControl=custom,
               tuneGrid = expand.grid(alpha = 1,lambda = lambda_lasso))
lasso.predicted <- predict(lasso, s=lasso$lambda.1se, newdata=US.test)
rmse(US.test[,1],lasso.predicted)
# Plot Results
plot(lasso)
plot(lasso$finalModel, xvar = 'lambda', label=T)
plot(varImp(lasso, scale=T))

# Elastic Net Regression - 自动生成lambda范围
set.seed(1234)
pre_en <- glmnet(x = as.matrix(US.train[, -1]), y = US.train$Real.GDP.Growth, alpha = 0.5)
lambda_en <- seq(min(pre_en$lambda), max(pre_en$lambda)*0.95, length.out = 50)
en <- train(Real.GDP.Growth~. , US.train , method="glmnet",trControl=custom,
            tuneGrid = expand.grid(alpha =seq(0,1,0.05),lambda = lambda_en))
Enet.predicted <- predict(en, s=en$lambda.1se, newdata=US.test)
rmse(US.test[,1],Enet.predicted)
# Plot Results
plot(en)
plot(en$finalModel, xvar = 'lambda', label=T)
plot(en$finalModel, xvar = 'dev', label=T)
plot(varImp(en))

# 对比RMSE
c(Ridge=rmse(US.test[,1],ridge.predicted),
  Lasso=rmse(US.test[,1],lasso.predicted),
  ElasticNet=rmse(US.test[,1],Enet.predicted))

# Compare Models
model_list <- list(Ridge=ridge,Lasso=lasso,ElasticNet=en)
res <- resamples(model_list)
summary(res)

# Best Model
ridge$bestTune
lasso$bestTune
en$bestTune
best <- en$finalModel
coef(best, s = en$bestTune$lambda)

内容的提问来源于stack exchange,提问作者siiiq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 14:15:41