☰
Lasso调参与模型选择:从超参数alpha到特征筛选实战指南
2026/9/30 3:07:57 网站建设 项目流程

如果你做过回归类的机器学习项目,Lasso这个名字应该不陌生。但真正把它用出效果,大多数人卡在同一个地方:超参数 alpha 到底怎么选。选小了,一堆无关特征挤进来,模型跟复读机一样把噪声全背下来;选大了,系数全被压成零,模型干脆躺平输出一个常数。更让人头疼的是,alpha 背后还牵着一整套模型选择的问题——用哪些特征、选多大的正则强度、怎么判断模型真的变好了而不是碰运气。

这篇文章就围绕“用机器学习实现 Lasso 超参数调整与模型选择”这件事,把 Lasso 的数学直觉、alpha 的搜索策略、模型选择的完整流程一次说透。我不打算只丢给你一段调包代码,而是按实际项目里做特征筛选和回归建模的顺序,把“为什么这么选”“换了场景怎么适配”“哪些坑我替你踩过了”都讲清楚。适合正在做回归任务、想用 Lasso 做特征选择、或者被调参折磨得想放弃的读者,也适合准备面试时想把 Lasso 和模型选择讲明白的人。

1. Lasso 到底在做什么:先搞清楚超参数 alpha 在管什么事

很多教程上来就让跑LassoCV,然后告诉你alpha_是自动选的。但如果你不知道 alpha 在惩罚什么,后面所有调参技巧都只是瞎试。这一节先花点时间把 Lasso 的内部机制拆开。

1.1 损失函数拆开看,alpha 惩罚的是什么

Lasso 的损失函数长这样:

min || y - X * w ||^2 + alpha * ||w||_1

前半部分是普通最小二乘的残差平方和,后半部分是 L1 正则项。L1 范数就是系数绝对值之和,alpha 是惩罚强度。这里要建立的第一直觉是:alpha 不是“噪声参数”,它直接决定了模型对特征系数的信任程度。

类比一下:假设你在评估一套房产的采光条件,整理出了 20 个指标,包括户型、朝向、楼层、周边遮挡情况等。普通线性回归是对所有指标一视同仁,只要相关性够高就保留;Lasso 干的事情是,先花力气找到一组“关键指标”,其余指标直接判定为与采光无关,系数归零。alpha 就是这轮筛选中“淘汰线”的高低——淘汰线设得越高,能留下来的指标越少。

这个“直接归零”的性质,就是 Lasso 区别于岭回归的核心。岭回归用 L2 正则把系数压缩到接近零,但很少真正变成零;Lasso 的 L1 正则会因为约束域的顶点效应,把不重要的系数精确压缩到零。这也是为什么用 Lasso 做特征选择,比单纯看线性回归的 p 值或岭回归的系数大小更符合直觉。

1.2 为什么 alpha 小的时候模型“飘”,alpha 大的时候模型“木”

把 alpha 取值拉到两个极端,你会看到两种完全不同的模型形态。

alpha 非常小,比如 0.0001,正则项几乎不起作用,Lasso 退化成普通最小二乘。此时模型会把所有特征都用上,即便某些特征和标签之间只是巧合相关。在训练集上拟合误差会很低,但一旦喂进新数据,那些噪声特征一变,预测就乱跳。这就是典型的“飘”——方差大、泛化差。

alpha 非常大,比如 1000,L1 正则的惩罚力度远超数据本身的信号,模型发现把所有系数都压到零、只预测一个全局均值,损失反而更小。此时模型变成一条水平线,预测值全是均值,完全无视输入特征。这就是“木”——偏差大、欠拟合。

真正合适的 alpha 落在两者之间的某个区间:既能压制噪声特征的系数,又不至于把所有信号都抹掉。问题在于,这个区间在哪里,数据不会直接告诉你,必须通过搜索和验证去找。

1.3 坐标下降法:Lasso 求解器在背后干的事

理解了 alpha 的作用之后,还得知道 Lasso 是怎么被求解出来的。目前 scikit-learn 里的 Lasso 默认用坐标下降法(coordinate descent)。

坐标下降法的思路是:先固定其他所有系数,只优化当前这一个系数的最优值,然后换下一个系数继续优化,反复迭代直到收敛。由于 L1 正则项在零处不可导,坐标下降在每次更新时会引入一个“软阈值”操作——把系数向零方向收缩一段距离,收缩距离超过当前幅度的就直接归零。

这里有一个工程细节值得注意:alpha的值会直接影响坐标下降的迭代次数。alpha 越大,系数被快速压到零,收敛快;alpha 越小,每一步收缩不明显,需要迭代很多轮才能稳定。所以如果你自己写 Lasso 的迭代求解,当 alpha 特别小的时候,收敛判据(tol)要调低,否则很容易在还没收敛时就停止,拿到一个半成品解。

在 scikit-learn 里,Lasso 默认的tol是 1e-4,max_iter是 1000。大多数场景够用,但如果你发现结果对random_state非常敏感或者dual_gap_(对偶间隙,收敛程度的指标)偏大,就需要手动调高max_iter或调小tol。我建议顺手打出lasso.n_iter_,如果看到迭代次数顶到 1000 的上限,说明数据量或特征维度超过了默认预设计,最好主动加迭代上限,否则结果可能不稳定。

2. 超参数调整:三种主流的 alpha 选法

Lasso 的超参数调整本质上是“找 alpha”。数据变了、特征变了、噪声水平变了,最优 alpha 都会跟着变,不存在一劳永逸的固定值。从我实践过的项目看,想认真调 alpha,基本绕不开下面三种方法,它们之间不是对立关系,更像层层递进的套路。

2.1 网格搜索加交叉验证:最直接也最容易踩坑的路径

最朴素的思路是:把 alpha 从大到小排一排,每个 alpha 跑一次交叉验证,看谁的验证集平均误差最低,选它。scikit-learn 里可以直接用GridSearchCV配合Lasso,也可以直接借用逻辑回归的LogisticRegressionCV思路。

但直接上均匀网格有一个陷阱:alpha 和模型误差之间不是线性关系。通常在很小的 alpha 区间里,模型误差变化剧烈;在较大的 alpha 区间,误差曲线又相对平缓。如果你用均匀网格[0.05, 0.1, 0.15, ..., 0.95]去搜,很可能把真正最优的 0.02 漏掉,反倒选出一个局部平稳但整体偏大的 alpha。

我的习惯是先用宽区间加对数网格确定数量级,再围绕最优数量级做细网格搜索。比如先搜np.logspace(-4, 0, 50),看到误差曲线最低点在 0.03 附近,再搜np.linspace(0.01, 0.06, 20)精确定位。本质上就是先粗后细、从宏观到微观,跟找工作时先筛行业再筛公司一个逻辑。

另外,GridSearchCV内部做交叉验证时,每一折都会重新用训练子集做标准化。如果你图省事,提前在完整数据集上算了 z-score 再传给GridSearchCV,会造成信息泄露——验证折的均值方差也参与了标准化计算。正确做法是把标准化放进 Pipeline,让每一折的标准化只在训练子集上完成。这条经验我单独拎出来,是因为真的见过不少线下评估漂亮、上线就崩的案例,源头就是这种看似不起眼的预处理顺序。

2.2 LassoCV:用 alpha 路径一次算完所有候选

LassoCV是 scikit-learn 里专门为 Lasso 调参提供的封装,内部做了两件关键事:自动生成 alpha 搜索路径,以及对路径上的每个候选 alpha 做交叉验证。

alpha 路径的生成方式值得单独讲一下。它首先计算alpha_max,也就是“恰好把所有系数压成零”的最小惩罚强度——这个值可以通过特征矩阵和标签向量的内积直接推算出来,不需要训练模型。然后从alpha_max出发,按等比数列向下递减,默认生成 100 个候选 alpha,直到alpha_max * epsilon作为下限,其中 epsilon 默认是 1e-3。这比手动打网格科学得多,因为它天然覆盖了从满惩罚到近无惩罚的全区间。

计算上,由于 Lasso 的系数是 alpha 的连续路径函数,相邻 alpha 的解很接近,所以LassoCV在求解时会把前一个 alpha 的系数作为后一个 alpha 的初始值,这种“热启动”方式让整个路径的计算比独立拟合 100 个模型快得多。

但用LassoCV有一个容易被忽略的细节:它返回的alpha_是验证集平均误差最小的点,但这个点往往偏小,因为在误差曲线的低洼地带,稍微减小 alpha 会增加模型复杂度,但验证误差下降不明显。统计学里有个经验法则叫“1-SE 规则”:选误差在最小值一个标准误范围以内、但模型更简单的点,而不是绝对最小值点。LassoCV默认不实现这条规则,所以实际项目中,我一般会把LassoCV算出的alpha_path和对应误差取出来,人工看一眼误差曲线,必要时手动往右(更大的 alpha)挪一点,选一个简单性和误差之间更平衡的点。

2.3 LassoLarsIC:不交叉验证,用信息准则快速圈定范围

交叉验证的代价是计算量,尤其是样本量大、特征维度高的时候,反复拟合 100 个 alpha 乘以 5 折,时间成本不低。如果你的目标是先快速了解数据集适不适合 Lasso,或者想找一个 baseline,LassoLarsIC是更轻量的选择。

它利用 LARS(Least Angle Regression)算法沿 alpha 路径逐个加入变量,可以一行代码同时算出路径上所有候选模型的 AIC 或 BIC 值,然后自动选择惩罚项最小的模型。由于不需要做 K 折交叉验证,速度通常比LassoCV快一个数量级。

这里需要理解的是 AIC 与 BIC 的区别:BIC 的惩罚项比 AIC 重,更容易选出更小的模型,所以当你想要一个强稀疏、特征数很少的模型时,优先看 BIC;当你更在意预测精度、可以容忍多一些特征时,选 AIC。

LassoLarsIC的内部计算还涉及自由度 df 的估计——Lasso 每保留一个非零系数,大约消耗一个自由度,同时要额外加上截距项。这个估计在特征高度相关时会被低估,导致 AIC/BIC 偏乐观,所以在多组学、高相关特征密集的数据集上,我只会把它当作“数量级参考”,最后仍以交叉验证定稿。它的最大价值是快,能在几秒内让你大致知道 alpha 落在哪个区间,再把这个区间交给LassoCV精调。

3. 模型选择:从选系数到选模型的全流程

模型选择不只有调 alpha 这一步,还包括:用什么数据形态来调、怎么评估候选模型、选出来的模型能不能稳定复现。做完超参调优后,这几个问题才是真正决定项目成败的关键。

3.1 数据预处理的顺序问题:标准化和缺失值先处理谁

Lasso 的算法实现默认输入数据是标准化之后的标准形态。因为 L1 正则把各个系数放在同一个惩罚尺度上,如果某个特征量纲特别大(比如收入以“元”为单位,另一个特征以“万元”为单位),Lasso 会倾向于优先压缩量纲大的特征,这很容易选错特征。

正确顺序是:先切分训练集和测试集,再在训练集上做缺失值填充和标准化,最后用同一个填充和标准化参数作用到测试集。千万不能反过来。写成代码便是把StandardScaler和 Lasso 放进同一个 Pipeline,避免转录出错。有人觉得多此一举,但我踩过一次很深的坑:特征中有两列高度相关的变量,因为我顺手用全量数据做了标准化再切分,导致训练集和测试集数据“握手”,最终在测试集上的表现比随机猜测还差。

另外,缺失值处理也要放在交叉验证内部。如果你先用均值填充了整列,再切折,填充均值包含了验证折的信息,同样属于泄露。最稳妥的结构是Pipeline([imputer, scaler, lasso]),让每一折都在训练子集内重新填充和标准化。

3.2 特征集合的稳定性与业务解释

Lasso 选出的非零系数集合,并不总是稳定的。特征之间有较强共线性时,Lasso 可能这轮把 A 留在模型里、把 B 删掉,下一轮把 B 留下、把 A 删掉。对这个现象要有清醒认识,别只看一轮结果就断言“X 是最重要的特征”。

判断稳定性的实操方法是做 bootstrap 稳定性分析:对数据做有放回抽样,重复跑 100 次 Lasso,统计每个特征被选中的频率。频率接近 100% 的特征是真正稳定的信号,频率在 50% 上下徘徊的特征基本是模型对数据噪声的响应,不适合写进业务结论里。

我也习惯把最终选出的特征集合与业务常识对照。如果模型告诉你某个指标的系数应该为正,但业务逻辑铁定是负相关,先别急着接受模型。Lasso 的系数符号受共线性影响可能反转,这种时候如果特征本身没有业务上的解释力,就要检查是否会误导后续决策。机器学习模型是工具,不是神谕,保留解释惯性很有必要。

3.3 模型评价怎么对齐目标

模型选择的最后一步是评价。回归任务最常用的指标包括 RMSE(均方根误差)、MAE(平均绝对误差)和 R²(决定系数)。用哪个,取决于业务目标。

如果是预测库存需求,偏好把误差控制在“单位”——MAE 更直观;如果是预测房价,大额偏差会造成巨大损失,RMSE 会把大误差用平方放大,更贴合风控需求。两个指标在同一个模型上经常此消彼长,实际使用中我会同时打印两者,再结合业务做决定。

另一个容易忽略的是残差分布。只打印指标的话,一个预测偏差恒定偏高 20% 的模型完全有可能在某类样本上表现极差,却被全局指标掩盖。因此,建模完成后我至少做一次残差 vs 预测值散点图,检查是否存在明显的漏斗形结构。如果残差随预测值变大而变大,说明模型在同方差假设上出了问题,可能需要考虑对标签做 log 变换,或在损失函数里增加权重。

4. 实操:Lasso 超参数调整与模型选择的完整实现

说了半天原理,到这里直接上代码。我用加州房价数据集配合构造的高维特征工程,演示一个完整的 Lasso 调参和选择流程。

4.1 实验设计

先加载加州房价数据,基础特征是 8 个数值变量,包括收入中位数、房龄、房间数、人口等。为了让 Lasso 的筛选价值真正体现,我额外用PolynomialFeatures把特征扩充到 44 维(包含 8 个原始特征、28 个两两交互项和 8 个平方项)。这种情况下普通线性回归会严重过拟合,Lasso 则应该选出少量真正有效的特征。

数据处理流程按这个顺序来:

  1. 从fetch_california_housing()加载数据,将目标列单独取出。
  2. 切分训练集和测试集,比例 7:3,固定random_state=42。
  3. 构造 44 维交互特征,只基于训练集拟合PolynomialFeatures。
  4. 用Pipeline把StandardScaler、LassoCV串起来,避免预处理泄露。
  5. 同时跑一个LassoLarsIC作对比,查看 AIC/BIC 选出的 alpha 是否与交叉验证选出的 alpha 在同一数量级。
  6. 最终用测试集评估两个模型的 RMSE、MAE、R²,并记录选中了多少个特征。

4.2 完整代码与结果解读

import numpy as np import pandas as pd from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler, PolynomialFeatures from sklearn.pipeline import Pipeline from sklearn.linear_model import LassoCV, LassoLarsIC from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score # 1. 加载数据 data = fetch_california_housing() X = pd.DataFrame(data.data, columns=data.feature_names) y = data.target # 2. 切分数据 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42 ) # 3. 构造高维交互特征 poly = PolynomialFeatures(degree=2, include_bias=False) X_train_poly = poly.fit_transform(X_train) X_test_poly = poly.transform(X_test) # 4. LassoCV 调参,Pipeline 确保每一折内标准化 lasso_cv = Pipeline([ ("scaler", StandardScaler()), ("lasso", LassoCV( eps=1e-3, n_alphas=100, cv=5, max_iter=5000, random_state=42 )) ]) lasso_cv.fit(X_train_poly, y_train) # 5. LassoLarsIC 作对照 lars_ic = Pipeline([ ("scaler", StandardScaler()), ("lars", LassoLarsIC(criterion="bic", max_iter=5000)) ]) lars_ic.fit(X_train_poly, y_train) # 6. 指标评测 def evaluate_model(name, model, X_test, y_test): y_pred = model.predict(X_test) rmse = np.sqrt(mean_squared_error(y_test, y_pred)) mae = mean_absolute_error(y_test, y_pred) r2 = r2_score(y_test, y_pred) n_selected = np.sum(np.abs(model.named_steps["lasso"].coef_) > 1e-8) \ if "lasso" in model.named_steps else \ np.sum(np.abs(model.named_steps["lars"].coef_) > 1e-8) print(f"{name}: RMSE={rmse:.4f}, MAE={mae:.4f}, R2={r2:.4f}, 选中特征数={n_selected}") return y_pred print("LassoCV 选出的 alpha:", lasso_cv.named_steps["lasso"].alpha_) print("LassoLarsIC(BIC) 选出的 alpha:", lars_ic.named_steps["lars"].alpha_) evaluate_model("LassoCV", lasso_cv, X_test_poly, y_test) evaluate_model("LassoLarsIC", lars_ic, X_test_poly, y_test)

实际运行结果,LassoCV选出的 alpha 通常落在 0.001 到 0.01 这个量级,选中特征数大约 20 到 30 个;LassoLarsIC因为 BIC 惩罚更重,选中特征数通常会少一些。测试集上两者的 RMSE 差距通常在 1% 以内,但模型复杂度差别明显。

这个对比恰好说明了一个要点:LassoCV偏预测精度,LassoLarsIC偏模型简洁度。如果业务场景需要向别人解释模型,我会优先参考 BIC 选出的简洁版本;如果唯一目标是排行榜上的 RMSE,那就跟LassoCV走。

代码里有一个细节值得单独讲:我在LassoCV里设置了max_iter=5000。默认值是 1000,如果数据量大或者特征维度高,普通设置可能不足以保证收敛。设置完之后要检查收敛标志,scikit-learn 会给出警告。如果发现警告,继续调大max_iter,不要忽略;不收敛结果没有任何参考价值。

此外,LassoLarsIC在Pipeline里配合StandardScaler也极具讲究。LassoLarsIC基于 LARS 算法,对特征的尺度更敏感,更需要在每一折内完成标准化后参与模型训练。如果把标准化放在拟合之前全局做,结果同样会被污染。

5. 常见问题与排查技巧实录

实战中,Lasso 调参与模型选择的问题远不止“选哪个 alpha”。下面是我在多个项目里切切实实遇到过的坑,以及对应的排查思路,按出现频率排序。

5.1 训练集上 Lasso 被“驯服”了,验证集上却崩了

交叉验证表现不错,上线或测试集上一塌糊涂,首先检查是否在预处理上泄露了。最容易出问题的三个点:全局标准化、全局缺失值填充、特征选择用了全量数据。凡是涉及数据统计量的操作,都必须在交叉验证的训练折内完成。

排查办法很简单:把数据切一次,只保留训练折,重新跑一遍 Pipeline,再对比测试集指标。如果训练折内做预处理后模型指标明显变差,说明原结果就是被泄露“喂”出来的。

5.2 alpha 缩到极小后模型效果不升反降

理论上 alpha 越小,模型越接近线性回归,训练集误差应该越低。但如果你看到 alpha 很小、训练误差反而升高,多半是收敛出了问题。坐标下降在 alpha 极小时需要非常多轮迭代才能达到收敛精度,默认max_iter提前截断,模型停在一个次优解上,误差自然不降反升。

此时把max_iter调大到 10000 甚至 50000,再把tol从 1e-4 收紧到 1e-5 重新跑一轮,结果一般会恢复预期。

5.3 特征被压制到 0 之后,业务面没法解释

如果 Lasso 把业务上公认的重要特征全部归零,先不要怀疑数据错了,先检查特征标准化。量纲问题会让 Lasso 对个别特征的惩罚产生扭曲;另一个常见原因是重复特征——两个高度相关的变量进入模型后,Lasso 会随机保留其中一个,另一个被归零。出现这种情况时,我会对特征做相关性聚类,每个簇里选一个代表进入模型,而不是直接丢给 Lasso 去“赌”。

5.4 常见问题速查表

现象可能原因排查/解决办法
CV 中 R² 很高,测试集很烂数据泄露(全局标准化/填充/特征选择)把预处理放进 Pipeline,每折重算
LassoCV 选出的 alpha 极小误差曲线低谷平坦,CV 选点过激查看 alpha 路径图,结合 1-SE 规则选点
特征全被压成 0alpha 过大,或数据信号太弱缩小 alpha 搜索范围,先跑线性回归看相关性
相同代码两次结果不同坐标下降不收敛,或随机切分未固定调大 max_iter,固定 random_state
某关键特征系数为 0 但业务明确共线性导致 Lasso 丢弃矛盾特征做特征聚类,先消冗余再跑
LassoLarsIC 与 LassoCV 结果差异大AIC/BIC 与 CV 优化目标不同先看数据量:样本大信 CV,样本小信 BIC 作为参考
高相关特征下特征集合不稳定共线性使得 Lasso 路径不稳定用 bootstrap 统计选中频率,保留高频特征

5.5 关于特征稳定性的一点扩展建议

如果你已经在做 Lasso 模型选择,建议顺手记录每一轮交叉验证里哪些特征被保留,横跨多轮统计一个“保留频率”。这个频率比单纯看coef_的绝对值更有说服力。比如某个特征在 5 折交叉验证里被保留了 4 次,说明它跟标签的关联具有一致性;如果只被保留 1 次,那大概率是噪声或者与其它特征产生了巧合交互。

对高维稀疏场景,还可以把抽样换成分组稳定性检验:把数据随机切成 10 组,每次取 8 组训练、2 组验证,重复 20 次,统计特征命中率。做法不复杂,但能给模型选择提供一层很实用的保险。

写在最后的小经验

这类项目做多了之后,我个人的体会是:Lasso 的价值一半在预测,另一半在给业务方交付一个“讲得清楚”的模型。alpha 不是唯一需要关心的超参数,标准化方式、交叉验证结构、特征稳定性分析,每一项都比死磕 alpha 第三位小数更重要。

最后再分享一个小技巧:每次跑 LassoCV,都手动画一下alpha_path的平均误差曲线,横轴用对数刻度,同时标出最小值点和 1-SE 点。这条线十分钟内能帮你建立对数据噪声和模型复杂度的直观感知,比单纯打印一个alpha_值有用得多。后续换数据、换特征,我都会先看这条曲线再决定要不要继续调参,实测下来能少走很多弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询