1. 从一条直线说起为什么线性模型依然是工业界的首选很多人刚接触机器学习时会觉得线性回归太简单了简单到有点“不好意思拿出来讲”。但如果你真正在工业场景里做过特征工程、模型部署和线上推理就会发现一个反直觉的事实大部分稳定赚钱的模型底层都是线性模型或者线性模型的变体。深度模型在图像、语音、自然语言领域确实一骑绝尘但在推荐系统、风控评分、销量预测、广告出价这些场景里线性回归、岭回归、套索回归依然是主力。原因不复杂。线性模型有三个硬优势推理速度快、可解释性强、训练成本低。一个训练好的线性模型线上推理就是一次向量点积微秒级别返回结果。而一个深度模型可能需要几十毫秒甚至上百毫秒。在QPS动辄几万的风控场景里这几十毫秒的差距就是服务器成本翻倍和用户体验下降的区别。但线性模型也有它的软肋。最典型的问题就是过拟合和多重共线性。普通最小二乘OLS在特征数量接近甚至超过样本数量时解会变得极不稳定系数方差爆炸。这时候就需要引入正则化于是就有了岭回归和套索回归。这三个模型放在一起讲不是随便凑的它们恰好构成了一条从“无约束”到“L2约束”再到“L1约束”的完整逻辑链。这篇文章我会从实际使用的角度出发把线性回归、岭回归、套索回归的核心原理、适用场景、参数调优、代码实现和踩坑经验全部串起来。不管你是刚入门的新手还是已经用过但没深究背后逻辑的从业者都能从中找到可以直接复用的东西。2. 普通最小二乘的数学直觉与它的三个致命伤2.1 最小二乘到底在最小化什么线性回归的核心思想可以用一句话概括找到一组权重让预测值和真实值之间的残差平方和最小。数学上写成Loss Σ(y_i - (w·x_i b))²这个损失函数对权重求导令导数为零就能得到解析解w (XᵀX)⁻¹Xᵀy这个公式看起来很干净但实际用的时候问题很多。首先矩阵求逆的计算复杂度是O(n³)特征维度上万时直接算不动。其次XᵀX这个矩阵必须是可逆的但现实中特征之间往往高度相关导致这个矩阵接近奇异求逆结果数值不稳定。我刚开始做项目时用OLS拟合一个包含几十个特征的数据集结果每次重新采样训练数据系数符号都会翻转。当时以为是代码写错了排查了半天才发现是多重共线性导致的。特征A和特征B的相关系数达到0.98模型根本分不清到底是A在起作用还是B在起作用于是系数就在正负之间来回跳。2.2 多重共线性为什么让系数变得不可信多重共线性的本质是信息冗余。假设你要预测房价特征里同时有“面积平方米”和“面积平方英尺”这两个特征携带的信息几乎完全一样。模型可以给前者分配一个很大的正系数给后者分配一个很大的负系数两者相互抵消最终预测值依然正确。但单独看任何一个系数都是没有意义的。更严重的是这种系数的不稳定性会随着数据扰动被放大。训练集稍微变一点系数就可能剧烈变化。这在需要解释模型权重的场景里是致命的。比如风控模型你需要告诉业务方“收入每增加一万违约概率下降多少”如果系数本身都不稳定这个解释就站不住脚。判断多重共线性有一个常用指标叫方差膨胀因子VIF。简单说就是对每个特征做一次回归用其他特征预测它看R²有多高。R²越高说明这个特征越能被其他特征替代VIF就越大。一般VIF超过10就认为存在严重共线性。2.3 过拟合当模型把噪声也学进去了OLS的另一个问题是它没有对模型复杂度做任何惩罚。只要特征够多它就能把训练集拟合得完美无缺但一到测试集就崩了。这就是过拟合。举个极端例子如果你用样本编号作为特征去预测房价OLS会给你一个完美的拟合结果因为每个样本编号唯一对应一个房价。但这个特征没有任何泛化能力。OLS不会自动识别这种“垃圾特征”它只会忠实地最小化训练误差。过拟合的根源在于模型容量超过了数据承载的信息量。当特征数量p接近或超过样本数量n时OLS几乎必然过拟合。这时候就需要正则化来约束模型让它不要那么“自信”。3. 岭回归给系数套上一根弹性绳3.1 L2正则化的几何意义岭回归Ridge Regression的做法很简单就是在原来的损失函数后面加一项Loss Σ(y_i - ŷ_i)² λΣw_j²这一项叫做L2正则化λ是超参数控制惩罚力度。加了这一项之后解析解变成w (XᵀX λI)⁻¹Xᵀy注意这里多了一个λI这个小小的改动带来了两个好处。第一XᵀX λI一定是可逆的因为λI让矩阵的对角线元素都增加了λ特征值全部大于零。第二系数被“压缩”了不会出现极端大的值。从几何上看L2正则化相当于把系数限制在一个圆形区域内。优化目标是在这个圆形区域内找到损失最小的点。因为约束区域是圆滑的最优解通常不会落在坐标轴上所以岭回归的系数不会变成零而是整体缩小。3.2 λ的选择偏差与方差的权衡λ的取值直接决定了模型的偏差-方差权衡。λ越大惩罚越强系数被压缩得越厉害模型方差减小但偏差增大。λ越小越接近OLS方差大但偏差小。实际调参时我一般用交叉验证来选λ。具体做法是把数据分成K折对每个λ值训练K次取平均验证误差选误差最小的那个λ。sklearn的RidgeCV可以直接做这件事内部用的是广义交叉验证GCV效率比手动K折高很多。有一个经验性的起点λ的范围可以从10⁻³到10³按对数均匀取100个点。如果最优λ落在边界上说明范围设窄了需要扩大。我见过有人直接把λ设成1结果模型欠拟合严重预测值几乎都挤在均值附近。3.3 岭回归的适用场景与实操细节岭回归最适合的场景是特征数量多且存在多重共线性。比如基因表达数据动辄上万个基因特征样本可能只有几百个。这种情况下OLS完全不可用岭回归是标准做法。实操中有几个细节值得注意。第一特征标准化是必须的。岭回归的惩罚项对系数大小敏感如果特征量纲不同比如一个特征是收入万元另一个是年龄岁惩罚力度就会不公平。标准化之后所有特征均值为0、方差为1惩罚才均衡。第二截距项通常不惩罚。sklearn的Ridge默认fit_interceptTrue截距不参与正则化。如果你自己实现记得把截距单独拿出来。第三岭回归的系数解释要谨慎。因为系数被压缩了绝对值比OLS小不能直接说“特征X每增加一个单位目标增加w个单位”。这个w是带惩罚的估计和真实效应有偏差。4. 套索回归让模型自动做特征选择4.1 L1正则化为什么能产生稀疏解套索回归Lasso Regression把惩罚项换成了L1范数Loss Σ(y_i - ŷ_i)² λΣ|w_j|这个改动看似只是把平方换成了绝对值但效果截然不同。L1正则化的约束区域是一个菱形在二维情况下菱形的顶点落在坐标轴上。优化过程中损失函数的等高线很容易和菱形的顶点相切导致某些系数精确地等于零。这就是套索回归最吸引人的地方它自动做了特征选择。如果你有1000个特征套索可能只保留其中50个非零系数其余950个直接归零。这不仅简化了模型还提高了可解释性。但L1也有代价。当特征之间存在高度相关时套索倾向于随机保留其中一个把其他的归零。这个选择是不稳定的数据稍微变一点保留的特征可能就换了。这在需要稳定特征选择的场景里是个问题。4.2 套索的求解坐标下降法套索没有像岭回归那样的解析解因为L1范数在零点不可导。实际求解通常用坐标下降法每次固定其他系数只优化一个系数轮流迭代直到收敛。对于单个系数w_j更新公式是w_j S(Σx_ij(y_i - ŷ_i^(-j)), λ) / Σx_ij²其中S是软阈值函数S(z, λ) sign(z) * max(|z| - λ, 0)这个软阈值操作就是产生稀疏性的关键。当|z|小于λ时直接归零大于λ时向零收缩。你可以直观理解为只有对预测贡献足够大的特征才能“挣脱”λ的束缚保留下来。4.3 套索的实战调参经验套索的λ选择同样用交叉验证。但和岭回归不同的是套索的λ对结果影响更剧烈。λ稍微大一点可能所有系数都归零了λ小一点又保留太多特征。我一般会画一条正则化路径图横轴是λ的对数纵轴是系数值每条曲线代表一个特征。随着λ增大系数逐渐收缩到零。通过观察这条路径可以直观判断哪些特征最先被淘汰哪些特征最稳定。还有一个实用技巧先标准化再跑套索。和岭回归一样套索对特征尺度敏感。如果不标准化量纲大的特征会被优先保留但这可能只是因为它数值大而不是因为它真的重要。另外如果特征之间相关性很高可以考虑弹性网络Elastic Net它结合了L1和L2惩罚既能做特征选择又能处理共线性。不过这是另一个话题了这里不展开。5. 三种回归的对比与选型决策树5.1 核心差异一览维度线性回归OLS岭回归Ridge套索回归Lasso惩罚项无L2系数平方和L1系数绝对值和系数是否稀疏否否是部分为零多重共线性处理差好一般随机保留特征选择无无有解析解有有无用坐标下降适用场景特征少、无共线性特征多、有共线性特征多、需要稀疏解5.2 选型决策逻辑实际项目中怎么选我通常按这个顺序判断第一步看特征数量和样本数量。如果特征数远小于样本数且特征之间相关性低直接用OLS就行没必要上正则化。如果特征数接近或超过样本数必须用正则化。第二步看是否需要特征选择。如果业务方要求模型可解释只保留少量关键特征选套索。如果只是要预测准不关心哪些特征重要岭回归更稳。第三步看特征相关性。如果特征之间高度相关岭回归会把相关特征的系数都保留但缩小套索会随机选一个。如果你希望保留一组相关特征岭回归更好。第四步考虑弹性网络。如果既想要稀疏性又担心套索在相关特征上的不稳定弹性网络是折中方案。5.3 一个容易忽略的点截距的处理三种模型对截距的处理方式不同。OLS的截距就是均值差岭回归和套索默认不惩罚截距。但如果你手动实现一定要把截距排除在正则化之外。否则当λ很大时截距也被压缩到零模型预测值会整体偏移。sklearn的Ridge和Lasso都有fit_intercept参数默认True会自动处理截距。但如果你用SGDRegressor配合penalty参数就要注意截距的惩罚设置。6. 从零实现到sklearn完整代码与调参实录6.1 数据准备与标准化先造一组模拟数据包含多重共线性和噪声import numpy as np from sklearn.linear_model import LinearRegression, Ridge, Lasso, RidgeCV, LassoCV from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score np.random.seed(42) n_samples, n_features 200, 50 X np.random.randn(n_samples, n_features) # 制造共线性后25个特征是前25个的线性组合加噪声 X[:, 25:] X[:, :25] * 0.8 np.random.randn(n_samples, 25) * 0.2 # 真实系数只有前10个非零 true_coef np.zeros(n_features) true_coef[:10] np.random.randn(10) * 3 y X true_coef np.random.randn(n_samples) * 0.5 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.3, random_state42) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)这里我故意让后25个特征和前25个高度相关模拟真实场景中的冗余特征。真实系数只有前10个非零其余40个是噪声特征。6.2 三种模型的训练与对比models { OLS: LinearRegression(), Ridge: RidgeCV(alphasnp.logspace(-3, 3, 100), cv5), Lasso: LassoCV(alphasnp.logspace(-3, 1, 100), cv5, max_iter10000) } for name, model in models.items(): model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) mse mean_squared_error(y_test, y_pred) r2 r2_score(y_test, y_pred) n_nonzero np.sum(np.abs(model.coef_) 1e-6) print(f{name}: MSE{mse:.4f}, R2{r2:.4f}, 非零系数{n_nonzero})跑出来的结果通常是这样OLS的MSE最大因为过拟合了Ridge的MSE明显下降Lasso的MSE和Ridge接近但非零系数少很多可能只有十几个。这里有个细节LassoCV的max_iter我设成了10000因为默认1000在特征多的时候可能不收敛。如果看到ConvergenceWarning就把max_iter调大。6.3 正则化路径可视化import matplotlib.pyplot as plt from sklearn.linear_model import lasso_path alphas, coefs, _ lasso_path(X_train_scaled, y_train, alphasnp.logspace(-3, 1, 100)) plt.figure(figsize(10, 6)) plt.plot(np.log10(alphas), coefs.T) plt.xlabel(log10(alpha)) plt.ylabel(系数值) plt.title(Lasso正则化路径) plt.axvline(np.log10(models[Lasso].alpha_), colorr, linestyle--, labelCV最优alpha) plt.legend() plt.show()这张图能直观看到每个特征系数随λ增大而收缩的过程。前10个真实特征的系数曲线比较粗说明它们对预测贡献大收缩得慢噪声特征的系数很快就被压到零了。6.4 调参中的三个坑第一个坑标准化用了全部数据。很多人习惯先标准化再划分训练测试集这是数据泄露。正确的做法是先划分再用训练集的均值和方差去标准化测试集。上面的代码里我是先划分再fit_transform这是对的。第二个坑RidgeCV的alphas范围太窄。如果最优alpha落在边界上说明范围不够。我一般先用一个宽范围粗调再在最优值附近细调。比如先跑10⁻³到10³发现最优在10²附近再跑10¹到10³。第三个坑Lasso不收敛。Lasso用坐标下降特征多的时候收敛慢。除了调大max_iter还可以调tol参数默认是10⁻⁴可以放宽到10⁻³加速收敛。另外如果特征之间相关性极高Lasso可能震荡这时候考虑弹性网络。7. 工业场景中的真实应用与踩坑记录7.1 风控评分卡可解释性压倒一切在风控领域评分卡模型几乎都用逻辑回归线性回归的广义形式加正则化。为什么不用XGBoost因为监管要求模型必须可解释必须能说清楚每个特征对评分的贡献。岭回归和套索在这里是标配。我做过一个项目特征有几百个包括征信查询次数、逾期记录、收入水平等。用套索之后模型只保留了三十多个特征业务方一看就懂。但这里有个坑套索在相关特征上的随机性会导致模型不稳定。比如“近三个月查询次数”和“近六个月查询次数”高度相关套索可能这个月保留前者下个月保留后者。后来我们改用弹性网络让相关特征一起进或一起出稳定性好了很多。7.2 销量预测岭回归处理季节性特征销量预测的特征里有很多季节性哑变量比如“是否春节”“是否双十一”。这些哑变量之间天然存在相关性节假日往往扎堆。用OLS时系数符号经常出现反直觉的情况比如“双十一”的系数是负的。换成岭回归后系数变得合理了因为L2惩罚把相关特征的系数拉向彼此避免了极端值。这里有个经验岭回归的λ不要设太大。销量预测里如果λ太大模型会过度平滑把真实的促销效应也抹掉了。我一般从0.1开始试配合时间序列交叉验证而不是随机K折。因为销量数据有时间依赖性随机K折会泄露未来信息。7.3 生物信息学套索做基因选择基因表达数据是典型的“大p小n”问题几万个基因几百个样本。套索在这里几乎是默认选择因为它能自动选出与疾病相关的基因子集。但要注意套索选出的基因集合可能不稳定。同一个疾病的不同数据集套索选出的基因可能只有部分重叠。解决办法是稳定性选择Stability Selection对数据做多次子采样每次跑套索统计每个基因被选中的频率。频率高的基因更可靠。这个方法计算量大但能显著提高结果的可重复性。7.4 一个反直觉的教训有时候OLS反而更好说了这么多正则化的好处但我要分享一个反直觉的经验当特征数量少且经过精心筛选时OLS可能比正则化模型更好。我做过一个实验特征只有8个样本有5000条且特征之间相关性很低。这种情况下OLS的测试误差和岭回归几乎一样但OLS的系数无偏解释起来更直接。正则化不是万能的。它的本质是用偏差换方差。当方差本来就不大时引入偏差反而有害。所以不要一上来就上正则化先看看OLS的表现如果过拟合不严重OLS就够了。8. 进阶话题贝叶斯视角与在线学习8.1 岭回归就是贝叶斯线性回归从贝叶斯角度看岭回归等价于给系数加上一个高斯先验。具体来说假设w服从均值为0、方差为σ²/λ的正态分布最大后验估计就是岭回归的解。λ越大先验方差越小系数被约束得越紧。套索则对应拉普拉斯先验这个分布在零点有一个尖峰所以后验众数容易落在零点产生稀疏解。理解了这一层就能明白为什么L1产生稀疏而L2不产生拉普拉斯分布在零点的概率密度远高于高斯分布。这个视角的实用价值在于你可以把领域知识编码进先验。比如你知道某个特征的系数应该是正的就可以用截断正态先验。这在数据稀缺时特别有用。8.2 在线学习与增量更新工业场景中数据是流式到来的不可能每次重新训练。这时候可以用**随机梯度下降SGD**来增量更新线性模型。sklearn的SGDRegressor支持partial_fit可以逐批更新系数。但SGD对特征尺度非常敏感必须严格标准化。而且学习率的选择很关键太大不收敛太小收敛慢。我一般用自适应学习率比如Adam或RMSprop虽然SGDRegressor默认不支持但可以手动实现。另一个方案是用递归最小二乘RLS它维护一个矩阵的逆每来一个新样本就更新一次。计算复杂度是O(p²)p是特征数。特征不多时很高效。9. 写在最后一些个人体会线性模型看起来简单但真正用好并不容易。我见过太多人直接调sklearn的默认参数然后抱怨效果不好。默认参数只是起点λ的选择、标准化的方式、截距的处理、求解器的选择每一个细节都会影响最终结果。如果让我给一条最重要的建议那就是永远先画正则化路径图。这张图能告诉你很多信息哪些特征重要、λ在什么范围合理、模型是否稳定。比盲目调参高效得多。另外不要迷信复杂模型。在特征工程做好的前提下线性模型往往能打败未经调优的复杂模型。我做过多次对比实验精心调优的岭回归和XGBoost的差距通常在1%以内但推理速度快了几十倍可解释性更是天壤之别。最后分享一个实用技巧用岭回归的系数作为特征重要性的参考。虽然岭回归不产生稀疏解但系数绝对值的大小能反映特征的重要性。如果某个特征的岭回归系数接近零说明它可能真的没用可以考虑删掉。这比套索更稳定因为套索在相关特征上的选择有随机性。
阅读完成 · 觉得有帮助?