使用enet(lambda=0)与lars(type="lasso")复现LASSO变量选择结果不一致
问题背景
我在研究中使用R语言elasticnet包的enet()函数,对260k个蒙特卡洛方法生成的合成数据集执行LASSO回归,并计算变量选择的性能指标。为验证结果可复现性,设置相同随机种子后改用lars包的lars(type="lasso")复现,但变量选择结果及性能指标存在明显差异——无论是"top 50"测试数据集还是大规模测试,结果均不一致。
原enet实现代码
set.seed(11) system.time(LASSO_fits <- lapply(X = datasets, function(i) elasticnet::enet(x = as.matrix(dplyr::select(i, starts_with("X"))), y = i$Y, lambda = 0, normalize = FALSE)))
提取选中变量(enet)
LASSO_Coeffs <- lapply(LASSO_fits, function(i) predict(i, x = as.matrix(dplyr::select(i, starts_with("X"))), s = 0.1, mode = "fraction", type = "coefficients")[["coefficients"]]) IVs_Selected <- lapply(LASSO_Coeffs, function(i) names(i[i > 0]))
lars复现代码
set.seed(11) # 确保可复现 system.time(LASSO.Lars.fits <- lapply(datasets, function(i) lars(x = as.matrix(select(i, starts_with("X"))), y = i$Y, type = "lasso")))
提取选中变量(lars)
LASSO.Lars.Coeffs <- lapply(LASSO.Lars.fits, function(i) predict(i, x = as.matrix(dplyr::select(i, starts_with("X"))), s = 0.1, mode = "fraction", type = "coefficients")[["coefficients"]]) IVs.Selected.by.Lars <- lapply(LASSO.Lars.Coeffs, function(i) names(i[i > 0]))
差异原因分析
核心算法不同
elasticnet::enet采用坐标下降法求解LASSO,而lars::lars使用**最小角回归(LARS)**算法生成正则化路径。两种算法的路径生成逻辑、变量选择顺序(尤其存在高度相关变量时)、收敛条件均有固有差异,即使目标函数一致,也会导致中间结果和最终变量选择的不同。正则化强度的
fraction定义不一致
两个包的predict()函数中mode="fraction"的含义不同:- elasticnet的
fraction是指从全正则化(系数全为0)到无正则化(OLS解)的路径比例; - lars的
fraction是指LARS路径的长度比例(从原点到OLS解的路径长度占比)。
这意味着s=0.1在两个包中对应的实际正则化强度并不相同,是结果差异的关键因素。
- elasticnet的
输入处理与参数默认值差异
尽管都设置了normalize=FALSE,但两个包对输入矩阵的内部数值处理(如截距项计算、矩阵存储精度)、收敛阈值(如enet的maxit、lars的eps)默认值不同,进一步放大了结果差异。
解决办法
统一正则化强度的指定方式
放弃mode="fraction",改用具体的lambda值来匹配正则化强度。例如先从其中一个拟合结果中提取对应目标fraction的lambda,再用该lambda值在另一个包中预测:# 从第一个lars拟合结果中获取对应fraction=0.1的lambda target_fraction <- 0.1 lars_fit_example <- LASSO.Lars.fits[[1]] lambda_idx <- which.min(abs(lars_fit_example$fraction - target_fraction)) matched_lambda <- lars_fit_example$lambda[lambda_idx] # 用匹配后的lambda在enet中预测 LASSO_Coeffs_matched <- lapply(LASSO_fits, function(i) predict(i, s = matched_lambda, mode = "lambda", type = "coefficients")[["coefficients"]])统一使用同一算法包
若需严格复现结果,建议全程使用同一个包的LASSO实现。同时推荐使用更主流、稳定性更好的glmnet包,其glmnet(x, y, alpha=1)对应纯LASSO,采用坐标下降法,结果一致性更强。对齐输入处理与收敛参数
- 确保两个拟合过程中输入的x矩阵、y向量完全一致(可通过
all.equal()检查单个数据集的输入差异); - 手动设置相同的收敛参数,例如给
enet()指定maxit=1e5,给lars()指定eps=1e-8,减少数值计算带来的误差。
- 确保两个拟合过程中输入的x矩阵、y向量完全一致(可通过
内容的提问来源于stack exchange,提问作者Marlen

