LASSO这名字在机器学习圈里出现频率实在太高了,尤其你一旦开始处理高维数据、做特征筛选,或者模型解释性遇到瓶颈,它基本就是绕不开的那把手术刀。这篇东西不是我写给论文看的,是我把从原理到落地、再到调参踩坑整个过程重新梳理了一遍,用一次实际项目的方式完整走下来,把每一步的“为什么”和“怎么选”都讲透。你可以把它当成一份带注释的操作笔记,照着做就能快速把LASSO用起来,而不是停留在“听过这个名字”的层面。
1. LASSO是什么,它到底解决了什么麻烦
1.1 从普通线性回归聊起:过拟合是怎么来的
大多数人接触机器学习的第一课就是线性回归,公式简单,效果直观,解释起来也方便。但一旦特征数量多起来,问题就出现了。你手里有几百个特征,样本量却只有几十条,普通最小二乘回归会在训练集上拟合得异常漂亮,测试集上惨不忍睹,这就是典型的过拟合。更麻烦的是,几百个特征里真正有用的可能就十几个,剩下全是噪声,可模型不分辨这些,它会把每个特征的系数都估出来,哪怕是完全没有意义的列,也会得到一个非零的权重。
我刚开始做特征工程的时候也走过弯路,觉得特征越多信息越多,恨不得把交互项、平方项全部塞进去。结果模型复杂得像个八爪鱼,预测一塌糊涂,领导问起来哪个变量起了作用,我一句都答不上来。这时候我意识到,模型不仅要“会预测”,还得“能解释”。但是普通回归在这个需求面前几乎是束手无策的,除非你手动去筛特征,而手动筛意味着主观、耗时、容易出错,而且特征之间还有相关性,你单独看每个特征和标签的关系可能都不显著,组合起来却很重要,这种效应靠人工很难发现。
LASSO的出现正好卡在这个痛点上:它能在拟合模型的同时自动完成特征选择,把一堆特征里没用的那些系数直接压成零,留下的就是模型认可的“有用特征”。这意味着你跑完一次训练,不仅拿到预测模型,还顺手得到了一份精简后的特征清单,这在真实项目里的价值往往比模型本身的精度提升还要大。
1.2 LASSO和岭回归的纠葛:都是正则化,路数完全不同
很多人第一次接触LASSO的时候会同时听到另一个名字——岭回归(Ridge)。它们俩确实是近亲,都属于正则化线性模型,罚的都是系数的大小,但罚的方式不一样,效果也就截然不同。岭回归加的是L2范数惩罚,也就是系数平方和,它会把系数往小里压缩,但极少变成零。LASSO加的是L1范数惩罚,也就是系数绝对值之和,它会把系数精确地压成零。
这个差别从几何直观上很好理解。约束条件在二维平面上,岭回归是圆形,LASSO是菱形,目标函数的等高线最先接触到菱形的顶点时,就落在坐标轴上,对应的另一个系数就是零。放到高维空间里,LASSO造成的解边界就带有“尖角”,这些尖角刚好让解倾向于稀疏。简单说,岭回归削峰填谷但保留所有变量,LASSO是直接裁员,把不重要的变量踢出门。
所以在选择上,如果你的目标是预测精度、特征之间存在多重共线性时,岭回归往往更稳;但如果你的目标是特征筛选、模型解释、压缩存储,LASSO就是那个更合适的选择。真实项目里倒也不用在两者之间死磕,因为它们还有一个混合体叫弹性网(Elastic Net),介于两者之间,这个后面我会单独讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. LASSO的核心原理:那个压缩系数为零的机制是怎么运作的
2.1 损失函数:一个目标函数里藏着两个诉求
LASSO的完整目标函数长这样:
[
\min_{\beta} \left{ \frac{1}{2n} \sum_{i=1}^{n} (y_i - X_i \beta)^2 + \lambda |\beta|_1 \right}
]
前一项是残差平方和,衡量模型对训练数据的拟合程度;后一项是L1惩罚项,(|\beta|1 = \sum^{p} |\beta_j|),也就是所有系数绝对值之和。(\lambda)是正则化参数,控制惩罚的强度。整个求解过程就是在“拟合得更好”和“系数更小/更稀疏”之间找一个平衡点。
这里有个关键点:惩罚项不是加在所有系数上的,截距项 (\beta_0) 通常不参与惩罚。因为截距只是平移预测值,不影响特征的筛选,罚它没有任何意义。你在用sklearn的Lasso时,它会自动处理好截距,不需要手动去调,但如果哪天你自己实现了损失函数,记住别把截距也罚了,否则结果会偏。
另外,前面那个 (\frac{1}{2n}) 是归一化因子,本质上是为了让损失函数不随样本量变化而剧烈波动,也方便不同样本量之间的比较。很多教材里写的时候会省略,但在实现时候务必加上,不然你的 (\lambda) 区间和标准实现会对不上。
2.2 软阈值:一个让系数变为零的精巧操作
LASSO为什么能把系数精确变成零而不是趋近于零?答案藏在坐标下降法的迭代更新公式里。对于标准化的数据,LASSO的系数更新可以写成一个非常简洁的形式:
[
\beta_j = S_{\lambda}(\rho_j)
]
其中 (\rho_j = \sum_{i=1}^{n} x_{ij
