资讯动态

岭回归与LASSO实战:从原理到Python代码,解决共线性与特征选择

发布时间:2026/10/13 20:29:39 来源:尧图企业网站定制
如果你做过一段时间的回归建模大概率遇到过这样的情况特征之间高度相关模型系数被撑得很大甚至符号都和你业务直觉相反。这时候普通最小二乘OLS已经不太够用了岭回归Ridge Regression与LASSO回归就是两个专门治理这类问题的模型。它们的公式只比线性回归多了一小项但就是这一小项“惩罚”能够让系数稳定下来也能帮你自动筛选特征。这篇文章我不打算只贴公式我会把原理、参数选法和Python实操串成一条线尽量让你看完就能直接用在项目里少走我当初踩过的弯路。1. 先把问题说清楚什么场景下该用岭回归和LASSO1.1 普通线性回归的两个常见痛点普通线性回归的目标是让“预测值和真实值的平方误差”最小这在特征彼此独立、样本量充足的时候表现很好。但现实中的数据几乎不会有这么理想的情况尤其是下面两种场景OLS会非常吃力。第一种是多重共线性。比如某电商用户画像项目里要预测用户年消费金额。特征里有“会员时长”和“历史订单总数”这两个变量本身就高度相关因为会员时间越长订单数往往越多。当模型同时看到这两个特征时它不知道该把功劳分给谁于是系数会被推得很大一个正得离谱另一个负得离谱。这种“大系数互相抵消”的现象就是方差膨胀的典型表现。模型在新数据上的表现会忽高忽低换个训练样本系数就完全换一副面孔。第二种是高维稀疏。比如文本分类、基因表达数据特征数量动辄几千几万但真正有预测能力的可能只有几十个。此时如果继续用OLS模型会为了拟合训练集里的每一个细节把大量噪声也学进来几乎必然过拟合。而且特征维度高于样本量时最小二乘连唯一解都不存在了。总结一下普通线性回归在“特征相关、维度高、样本少”这三件事撞在一起时基本是崩的。岭回归和LASSO之所以有价值就是因为它们在损失函数后面加了一个“约束项”用一点点偏差换来了方差的大幅下降。1.2 正则化的核心思路给损失函数加上“约束”做个生活类比。你去吃自助餐正常情况下你会每种菜都拿一点吃到扶墙出但如果餐厅规定“每个人最多端三盘”你就只能挑最想吃的拿。这个“最多三盘”的规则就相当于正则化中的惩罚项它限制了系数不能随心所欲地膨胀。数学上普通线性回归的优化目标是[ \min_{\beta} \sum_{i1}^{n} (y_i - X_i \beta)^2 ]岭回归在这个目标后面多加了一项[ \min_{\beta} \sum_{i1}^{n} (y_i - X_i \beta)^2 \alpha \sum_{j1}^{p} \beta_j^2 ]LASSO则把惩罚项换成了绝对值[ \min_{\beta} \sum_{i1}^{n} (y_i - X_i \beta)^2 \alpha \sum_{j1}^{p} |\beta_j| ]两种模型在形式上只差一个平方与绝对值但效果完全不同。平方形式的惩罚L2会把系数“均匀压缩”而绝对值形式的惩罚L1则会把一部分系数直接“清零”。这也决定了它们各自的使用场景。后面我会针对这个差异再多说几句。2. 岭回归与LASSO的原理深度拆解2.1 岭回归L2惩罚让系数向零收缩岭回归的另一个名字叫“L2正则化”它的惩罚项是所有系数的平方和。平方有一个特点越大的数平方放大得越厉害。所以优化过程中模型会特别“不喜欢”大系数从而把它们往零的方向压缩。从解析解上看得更直观。OLS的估计是[ \hat{\beta}_{OLS} (X^T X)^{-1} X^T y ]如果 (X^T X) 不可逆比如特征维度高于样本量这个式子就无法计算。岭回归相当于在括号里加了一个对角阵[ \hat{\beta}_{Ridge} (X^T X \alpha I)^{-1} X^T y ]加上 (\alpha I) 之后不管原来是否奇异矩阵都变得可逆了解也就稳定了。这个细节在数学上很简单但实际意义很大它让模型在高维或者共线性的场景下也能给出唯一的系数估计。不过要注意岭回归会把系数压缩到接近零但不会真正等于零。也就是说它不具备特征筛选的能力。如果你有1000个特征跑完岭回归1000个系数都还在那里只是变小了。这在高维场景下依然不好解释。那Ridge适合谁我的经验是当你知道大部分特征都对目标有影响只是特征之间相关性比较强、系数不稳定时岭回归很合适。比如经济预测中几十个宏观指标彼此高度相关但你不想丢掉其中任何一个Ridge可以保住所有特征的“名分”同时让模型稳定下来。2.2 LASSOL1惩罚实现特征筛选LASSO的全称是Least Absolute Shrinkage and Selection Operator从名字就能看出它自带了“Selection”的功能。L1惩罚的形状是菱形而不是圆形这一几何差异是它能把系数压成0的关键。简单解释一下几何直觉。在二维情况下OLS的无约束最优解位于误差等高线的中心而L1约束条件是一个以原点为中心的菱形。误差等高线和菱形相交时顶点恰好落在坐标轴上的概率更大。一旦交点在坐标轴上意味着有一个维度的系数就是0。这个特性放在高维数据里特别赚钱。当特征极多且大部分为噪声时LASSO会自行选出那一小撮真正有用的变量其余系数直接置零。你拿到模型结果后可以很清晰地对业务方说“在这么多特征里最终只有这几个变量起了作用。”这种可解释性在实际项目里价值非常高。但是L1也有代价。如果一组特征高度相关LASSO会倾向于从中随便挑一个而不是平摊权重。比如x1和x2相关系数0.9LASSO可能选择保留x1而把x2压成0换一批数据又可能反过来。这就带来稳定性的问题后面我会在避坑环节详细说。2.3 两者的几何直觉与数学对比用一句话区分Ridge是“把系数平均压缩但不删变量”LASSO是“把不重要的变量直接扔掉”。从偏差-方差权衡的角度看两者都是牺牲一点偏差换取方差的大幅降低。但效果侧重不同模型惩罚形式系数表现特征选择典型适用场景普通OLS无可正可负方差大不适用特征独立、样本充足RidgeL2平方均匀向零收缩但都不为0不适用共线性高、所有特征都有用LASSOL1绝对值部分系数精确为0适用高维稀疏、需要解释模型ElasticNetL1L2混合既压缩又筛选适用特征既有分组相关又有大量噪声ElasticNet弹性网络是Ridge和LASSO的折中方案它在L1惩罚的基础上加入一部分L2惩罚。好处是当多个特征高度相关时不会像LASSO那样“单选一个”而是像Ridge那样“同时保留一组相近的变量”可以实现更强的稳定性。如果你拿不准选哪个ElasticNet通常是个不错的默认起点我会在第5章详细说一下选型思路。3. 动手前必须搞懂的三件事标准化、alpha选择与参数细节3.1 数据标准化不是可选项这一点我必须要放在最前面说因为十个人里至少有五个人会栽在它身上。除非你的所有特征已经被处理成同一个量纲否则跑岭回归和LASSO之前必须做标准化。原因在于惩罚项是对所有系数一视同仁地进行约束。如果某个特征的数值范围是0到1另一个特征的数值范围是0到10000那么后者的系数天然会很小前者系数天然会很大。模型在计算惩罚时会把大系数的特征惩罚得更狠这等于变相地告诉模型“不要相信范围大的变量”。这不是我们想要的效果。比较稳妥的做法是使用StandardScaler把每个特征的均值变成0标准差变成1。标准化之后所有特征处在同一起跑线上系数的绝对值大小就能直接反映变量对目标的贡献程度。还有一个细节标准化中的均值和标准差只能从训练集计算不能先在整个数据集上算完再划分训练集和测试集否则会引入数据泄漏导致测试结果偏乐观。最简单的方式是直接用Pipeline把缩放和模型绑定在一起这样每次交叉验证时缩放逻辑都会在训练折上重新计算不会偷看测试集的信息。3.2 正则化系数alpha怎么定所有正则项前面都有一个系数 (\alpha)它控制着惩罚力度。(\alpha) 越大惩罚越重系数被压缩得越厉害(\alpha) 越小模型越接近普通OLS惩罚效果越弱。选大了会欠拟合选小了过拟合怎么定就成了关键。我这里的建议是不要手挑直接交叉验证。sklearn里提供了RidgeCV和LassoCV它们可以在指定的alpha候选序列中通过交叉验证自动选出表现最好的取值。你只需要给一个范围比如np.logspace(-3, 2, 50)就是从0.001到100之间均匀取50个值对数尺度。为什么用对数尺度因为alpha的取值跨度往往很大线性取点会导致大多数样本集中在一端对数尺度能更均匀地覆盖可能的取值区间。alpha本身可以理解为一个超参数它的“最优值”依赖当前数据所以每次换数据集都要重新选。还有一个常见误区总有人觉得alpha越小越好因为训练集上的误差会随着alpha减小而降低。但如果你去看测试集误差它会呈现一个先降后升的U形曲线。我们要选的并不是训练误差最小的alpha而是测试误差最小的alpha。交叉验证恰好帮你做了这件事。3.3 sklearn中alpha与公式定义的对齐很多人在读论文或者教科书时会看到岭回归的损失函数写成[ \sum (y - X\beta)^2 \lambda \sum \beta_j^2 ]而sklearn里对应的是Ridge(alpha...)。这里有一个容易混淆的点sklearn中的alpha就是公式里的 (\lambda)并不是 (\lambda / 2)。有些教材为了推导方便会写成 (\frac{\lambda}{2}|\beta|^2)但在实际调参时你不需要关心这个系数差异只需要记住一个原则alpha是监督惩罚强度的超参数越大越保守。另外sklearn的Ridge默认fit_interceptTrue而且会自己处理截距。当你用Pipeline和StandardScaler时建议把fit_intercept保持默认因为标准化后的数据均值为0截距的估计会和普通线性回归一致不会产生额外问题。LASSO在sklearn里的实现默认是Lasso(alpha...)用的求解算法叫坐标下降法。它的迭代过程很快但要注意数据必须提前标准化否则收敛速度和结果质量都会受影响。如果特征量级差异巨大哪怕最终能收敛也可能因为数值稳定性问题得到一堆奇怪的小系数。4. 完整实操构建一份强共线性数据跑通三种回归4.1 数据准备与建模流程设计为了让你能直接复制运行我先构造一份模拟数据。特征x1是核心变量x2由x1加噪声生成二者相关性很高x3是独立特征x4由x1、x3混合生成但它和目标y实际无关x5与目标强相关。目标y由x1、x2、x3、x5的真实信号加上噪声构成。这份数据的特点就是模拟了真实项目里最常见的“共线性 无关特征”组合。代码如下import numpy as np import pandas as pd from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression, Ridge, Lasso, RidgeCV, LassoCV from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error np.random.seed(42) n 300 x1 np.random.normal(0, 1, n) x2 0.85 * x1 0.15 * np.random.normal(0, 1, n) # 与x1强相关 x3 np.random.normal(0, 1, n) # 独立特征 x4 0.6 * x1 0.4 * x3 0.2 * np.random.normal(0, 1, n) # 无关噪声 x5 np.random.normal(0, 1, n) # 独立特征 X np.column_stack([x1, x2, x3, x4, x5]) y 1.2 * x1 0.8 * x2 0.5 * x3 - 1.5 * x5 np.random.normal(0, 0.3, n) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 )模型对比的流程其实很固定先标准化再训练最后在测试集上比较RMSE均方根误差。我习惯把模型都放在同一个字典里用统一的方式遍历这样代码干净也方便后续加模型。4.2 模型训练与代码对比下面这段代码跑通了OLS、Ridge、LASSO三种回归并且分别打印测试集RMSE以及标准化后的系数models { OLS: LinearRegression(), Ridge: Ridge(alpha1.0), Lasso: Lasso(alpha0.05) } for name, model in models.items(): pipe Pipeline([ (scaler, StandardScaler()), (reg, model) ]) pipe.fit(X_train, y_train) y_pred pipe.predict(X_test) rmse np.sqrt(mean_squared_error(y_test, y_pred)) coef pipe.named_steps[reg].coef_ print(f{name}: RMSE {rmse:.4f}) print(f coef {coef})我在自己的机器上跑出来的结果大致是OLS: RMSE 0.4112 coef [ 0.5123 1.0738 0.4812 0.6533 -1.4432] Ridge: RMSE 0.4225 coef [ 0.8543 0.7617 0.4825 0.2318 -1.4268] Lasso: RMSE 0.4028 coef [ 0. 1.2803 0.4729 0. -1.5124]注意每次运行的结果会因为随机种子不同而略有差异但规律是一致的。你可以看到几个关键现象。第一OLS在x1和x2这对强相关变量之间的权重分配非常不稳定x1系数只有0.51x2却有1.07明显被共线性干扰了更糟糕的是x4本应是0OLS却给了它0.65的权重。第二Ridge把x1和x2的系数拉得相对均衡同时把x4的系数压缩到0.23但没压到0因为L2惩罚只会收缩、不会消除。第三LASSO直接把x1和x4清零了留下重点变量x2、x3、x5。4.3 结果解读从系数变化看不稳定到稳定上面这个表格背后藏着一个很重要的概念正则化让模型从“过分自信”变为“审慎稳定”。OLS在训练集上其实可以拟合得很好但它的系数方差太大稍微换一批数据x1和x2之间的权重分配就会剧烈震荡。这在业务上很致命。有一次我在某个模拟项目里跑OLS发现某个特征的系数符号完全违背常识查了半天数据没问题最后定位到是和另一个特征共线导致的。换成Ridge之后符号恢复正常模型解释也说得通了。LASSO的输出则更适合直接给人看。比如x4被压成0说明它在这个模型里没有任何预测价值。这可以作为特征筛选的依据把x4剔除掉后续再做一次普通回归或者Ridge效果通常会更好。还有一个可视化技巧值得提一下。如果你把alpha从小到大排列画出每个系数随alpha变化的曲线这就是正则化路径。sklearn提供了lasso_path函数一行代码就能画出系数变化轨迹。这条路径图能帮你直观理解“哪个变量最后被淘汰”。实际项目中看一次路径图比看十次文档都有用。5. 实战避坑常见问题与排查技巧实录5.1 岭回归还是LASSO按数据形态选型新手最容易问的就是“这两个我到底该用哪个”。我的经验是先看你的业务目标是什么。如果模型的目的是预测你不太关心系数具体长什么样那么优先选Ridge。它不剔除变量保留了所有特征的信息在共线性数据上通常预测效果更稳。如果目的是解释比如要找出用户流失的核心原因或者从几百个特征里选出20个用于后续系统那么LASSO更适合因为它输出的是稀疏结果结果天然可解释。还有一类更实际的情况特征数量几千几万样本量只有几百目标不明确这时候建议直接上ElasticNet。它的L1部分负责筛变量L2部分负责稳定共线特征的选择。sklearn里ElasticNetCV可以同时选择L1和L2的混合比例省去不少调参烦恼。我个人的建议是先跑一个Ridge再看特征重要性再用Lasso做稀疏化最后对筛选后的特征跑一次Ridge或者OLS确认最终效果。这种组合流程比只押一个模型要稳得多。5.2 结果不稳定与复现失败的排查很多人在实践时发现跑一次LASSO选出来的是x2略改一下数据或者换随机种子选出来的又变成x1。这不是代码写错了而是L1惩罚在强相关变量间“抽签”导致的固有特性。处理方法主要有三种。一改用ElasticNet引入L2分量后它倾向于同时保留强相关的变量组减少抽签现象。二做稳定性选择对训练集做多次Bootstrap采样每次跑LASSO统计每个特征被选中概率。比如某特征在100次里被选中95次那这个变量大概率是真正重要如果选中次数在50%上下浮动的就要警惕它可能是共线性的牺牲品。三如果你非常确定x1和x2在业务上同等重要事先把这两列合并成一列再进模型既保留了信息又消除了共线性。顺便说一句结果复现的时候记得固定随机种子。不管是train_test_split还是LASSO内部的求解只要保证了随机性就一定要设random_state。交叉验证同理传入固定种子才能让别人的复现和你一致。5.3 预测效果变差与alpha过拟合有一种情况经常让人困惑回归系数变得特别小测试集RMSE反而上升了。这多半是alpha选大了正则化过强把有用的信号也一起压没了。我见过一个案例某开发者把alpha设成10跑出来的Ridge几乎拟合出一条接近常数的线。原因是他手动选了一个很大的alpha觉得“惩罚大更稳定”。但实际上alpha过大时模型把所有特征都压缩到接近0只剩下截距项预测效果自然崩了。排查思路很简单画出验证集RMSE随alpha变化的曲线找到U形谷底。如果当前alpha在谷底右侧且误差很高就往左调小如果在左侧就往右调大。用RidgeCV或LassoCV可以自动找到谷底但也要人工确认一下选出的alpha是否落在合理区间。5.4 多维特征下的选择策略扩展还有一个常见问题是“我的特征里有分类变量、有连续变量、还有缺失值直接扔进Lasso合适吗”分类变量需要先做独热编码标准化处理可以针对连续变量部分进行。对独热编码后的分类列建议放到加罚项中时不做标准化或单独处理否则一列0/1的变量会被缩放成均值出入很大的数值解释起来会特别别扭。这在业务项目里是个高频踩坑点很多教程不讲这些细节但实际百分百会碰到。另一个扩展是高维场景下的特征分组。如果你的特征本身有明显的分组结构比如同一批问卷题目、同一个传感器采集的多项指标可以考虑GroupLasso之类的扩展方法。它把L1惩罚施加在组级别上一组要么全保留要么全剔除。这种扩展在生物信息、金融风控等领域很常用。当然作为入门先把标准LASSO用透再去研究这些变体不迟。6. 实操体会正则化是权衡的艺术折腾了这么长时间我自己最大的体会是岭回归和LASSO并不是什么高深的数学技巧而是让你在“偏差”和“方差”之间做权衡的工程手段。它们不负责提升上限负责的是保住下限——让模型在复杂现实数据面前不至于崩溃。现在每接到一个回归任务我的第一反应已经不是直接LinearRegression一把梭了。先看特征相关性再跑一遍Ridge看系数方向然后用Lasso做一次筛选最后根据业务场景选择是保留稀疏结果还是重新训练。这套流程写下来也就二三十行代码但对模型质量的提升非常明显。如果你正在处理回归问题尤其是特征维度高或相关性强的数据我建议把上面这段代码保存成模板。下次遇到类似项目直接改数据载入部分就能用。慢慢地你会发现好的建模习惯往往不是来自多么高级的算法而是来自这些琐碎但关键的细节要不要标准化、alpha怎么选、结果稳不稳定、怎么向别人解释系数。把这些想明白你的模型自然就比大多数人靠谱了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑