
简介正则化回归的Python算法源码包面向机器学习与数据科学初学者及需要处理高维数据的分析人员围绕L1Lasso与L2Ridge正则化系统展示防过拟合、特征选择及模型调参的完整代码实践。资源共168个文件包含110个py脚本、34个rst说明文档、8个ipynb交互式笔记本以及pyx扩展、配置文件等压缩包仅1.28MB轻量但结构完整。已有302人学习下载适合快速上手与对照练习。压缩包内代码示例覆盖LASSO demo、Fused LASSO、Group LASSO、逻辑回归、分布式LASSOADMM等场景并配有安装说明与目录文档可直接在Jupyter中运行观察效果。通过阅读这些代码可理解不同正则化项的数学作用、alpha参数对稀疏性的影响以及交叉验证搜索最佳超参数的实现思路为后续在实际项目中灵活选用正则化方法打下扎实基础。1. 正则化回归的 Python 算法打包从 Lasso 到分布式 ADMM 的一次性资源做回归模型的同学应该都有过这种经历训练集上 R² 飘到 0.95测试集直接崩到 0.6典型的过拟合。正则化回归就是干这个的——通过在损失函数里加惩罚项把模型参数按到合理范围顺带把不重要的特征权重压成零。这份名为 regreg 的 Python 源码包把 Lasso、Group Lasso、Fused Lasso 和分布式 ADMM 求解器全部打包进了 Jupyter Notebook不是那种贴一个 sklearn 调用的敷衍示例而是带完整推导和对比实验的工程实现。适合两类人正在做特征选择的数据分析师和需要在大规模数据上用分布式优化求解稀疏模型的算法工程师。下载后按 INSTALL 装依赖逐个 notebook 跑基本能把正则化回归的全流程摸透。2. 算法矩阵拆解六个 Notebook 分别解决什么2.1 目录拆解哪些文件是你真正要跑的拿到压缩包解压后第一眼看到 regreg.css、.gitignore、layout.html 这些文件有点懵。其实 css 和 layout.html 是文档页面的样式和模板对跑通实验没有直接影响.gitignore 是版本控制辅助文件真正有价值的是 INSTALL 和那六个 .ipynb 文件。INSTALL 文件写的是依赖安装方式常见内容是 pip install numpy scipy scikit-learn matplotlib以及通过源码安装 regreg 的方式。老版本 regreg 需要从 GitHub clone 下来之后执行 python setup.py install新版本直接 pip 就能装。装之前建议先建一个干净的虚拟环境避免和系统 Python 的包冲突。这一步值得花半小时后面跑分布式 notebook 时如果出现 OpenMP 相关的报错多半是环境里 numpy 和 scipy 的二进制不兼容导致。这六个 Notebook 按功能可以分成三组Notebook核心算法适用场景LASSO demo.ipynbL1 正则化入门、特征选择Fused LASSO.ipynb融合惩罚突变检测、分段常数信号Group LASSO.ipynb分组惩罚组特征、基因通路Newsgroup logistic regression.ipynbL1 逻辑回归高维文本分类Distributed LASSO: ADMM one value.ipynbADMM 单步演示理解迭代原理Distributed LASSO: ADMM.ipynbADMM 分布式优化大规模稀疏求解我的建议是从 LASSO demo.ipynb 开始跑确认环境没问题后再按分组深入。不要一上来就碰分布式 notebookADMM 的收敛判据和 rho 参数调起来相当费时间新手容易在这里卡住。如果你已经有 sklearn 的使用基础从 Fused LASSO 和 Group LASSO 开始也完全可以这两个 notebook 会让你看到 sklearn 没覆盖的惩罚项长什么样。2.2 Lasso 与 Group Lasso惩罚项的结构差异正则化回归的核心差异不在损失函数而在惩罚项的结构。Lasso 用的是 L1 范数惩罚项是模型参数绝对值之和求解结果是大部分系数精确等于零做的是特征级别筛选。Group Lasso 把特征预先分成若干组惩罚项变成各组系数的 L2 范数之和结果是整组系数一起被选中或被压零做的是组级别筛选。用公式表达更直观。设损失函数为 L(w)Lasso 和 Group Lasso 的目标函数分别是Lasso: min L(w) λ * Σ|w_j| Group Lasso: min L(w) λ * Σ_g ||w_g||_2下标 g 代表一个特征组||w_g||_2 是组内系数的欧氏范数。做基因表达分析时同一个通路的基因天然是一组用 Group Lasso 比用 Lasso 更贴合业务逻辑。这个结构差异直接影响求解器的选择——Lasso 的惩罚项可分离每个坐标方向可以独立求近端映射坐标下降法收敛快Group Lasso 的惩罚项在组内不可分离要用块坐标下降或近端梯度法。还有个经常被忽视的中间地带是 ElasticNet它在惩罚项里同时放 L1 和 L2公式是 λ1 * Σ|w_j| λ2 * Σ w_j²。它的行为介于 Lasso 和 Ridge 之间保留稀疏性的同时对已选中的特征组做收缩解决 Lasso 在特征高度相关时会随机丢弃其一的问题。这份源码包里没有单列 ElasticNet notebook但理解了 Lasso 和 Group Lasso 的结构再看 ElasticNet 就很容易。regreg 库的通用做法是用 simple_problem 把损失函数和惩罚项组合起来。损失函数继承自平滑函数类惩罚项继承自原子类atom求解器在后台自动匹配近端算子。你不需要手动推导每个惩罚项的近端映射但理解惩罚结构仍然必要它决定了你该选哪个 solver以及在数据规模变大时该换哪种优化策略。2.3 ADMM 求解器分布式 Lasso 的迭代逻辑ADMM交替方向乘子法是处理分布式正则化回归的常用手段。核心思路是引入辅助变量 z把原来只含 w 的优化问题拆成两个子问题一个更新 w一个更新 z再用拉格朗日乘子 u 把两者拉近。每轮迭代先在本地节点计算 w再汇总更新 z这样特征矩阵太大时可以把样本拆到不同机器上算。两个分布式 Lasso notebook 的区别在于one value 版本只做了一次 ADMM 迭代适合理解原理你能看到每个变量在单步内怎么被更新的完整版本包含迭代循环和收敛判断适合实际使用。实际工作中我一般把 ADMM 的 rho 设为 1.0 到 1.5然后用原始残差和对偶残差双重判据控制迭代次数。rho 太小时迭代慢rho 太大时容易震荡调这个参数基本靠看残差曲线多跑几个值对比才有感觉。ADMM 的收敛行为值得多看几遍 notebook。你会发现原始残差和对偶残差的下降速度不一样对偶残差往往慢一些。如果只看原始残差就提前终止解会偏向某个方向和目标函数最优值差不少。正确做法是两条残差曲线都低于阈值才算收敛。这份源码包的完整版 notebook 大概率画了两条曲线跑一遍认真看一下比你单独背 ADMM 公式有用得多。3. 跑通 Lasso 与 Ridge从数据预处理到 alpha 选参3.1 数据准备与标准化不做这一步结果没法看很多人拿过来就 fit结果 Lasso 的系数完全不可解释。原因是不做标准化时量纲大的特征在惩罚项里天然吃亏。比如一个特征单位是元数值在几千到几万另一个是比例值在 0 到 1 之间Lasso 的惩罚项对系数绝对值求和前者稍微有一点系数就产生巨大的惩罚贡献于是被优先压到零。这跟特征重要性完全无关纯粹是尺度问题。标准做法是先 StandardScaler 再进模型完整代码如下import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split data pd.read_csv(your_dataset.csv) y data[target_column].values X data.drop(columns[target_column]).values scaler StandardScaler() X_scaled scaler.fit_transform(X) X_train, X_test, y_train, y_test train_test_split( X_scaled, y, test_size0.2, random_state42, shuffleTrue )这里shuffleTrue是 train_test_split 的默认行为但我习惯显式写出来防止有人误以为切分不洗牌。random_state42固定随机种子保证每次跑结果一致这是所有可复现实验的前提。不用随机种子的话换台机器或换个时间跑结果就对不上排查问题时会很痛苦。标准化之后 Lasso 的系数大小可以直接比较系数绝对值越大说明特征对预测的贡献越强。注意 sklearn 的 Lasso 默认fit_interceptTrue标准化后可以让模型自己学截距项不需要手动把截距从特征里剔除。提示fit_transform只能用在训练集上测试集必须调用同一个 scaler 的transform不能重新 fit否则会造成信息泄漏评估结果虚高。3.2 Lasso 与 Ridge 的实现从默认参数到手动调参先看最基础的实现。sklearn 里 Lasso 和 Ridge 的接口几乎一样调用时只差一个类名但背后的代价函数完全不同一个是 L1一个是 L2。from sklearn.linear_model import Lasso, Ridge from sklearn.metrics import mean_squared_error lasso Lasso(alpha1.0, max_iter10000, tol1e-4) lasso.fit(X_train, y_train) ridge Ridge(alpha1.0) ridge.fit(X_train, y_train) for name, model in [(lasso, lasso), (ridge, ridge)]: pred model.predict(X_test) mse mean_squared_error(y_test, pred) print(f{name} MSE: {mse:.4f}) if name lasso: print(flasso 非零系数个数: {np.sum(model.coef_ ! 0)})两个细节容易踩坑。第一alpha1.0是库默认值它对你的数据未必合适。如果 Lasso 跑出来所有系数全是零不是模型写错了而是 alpha 太大、惩罚过度需要调小。第二Lasso 的默认求解器是坐标下降max_iter默认 1000特征维度高或共线性强时可能不收敛常见做法是设到 10000同时把tol设为 1e-4。如果迭代不够sklearn 会弹出 ConvergenceWarning系数结果也不稳定。Ridge 没有稀疏化效果所有系数都趋向小值但不会为零。所以做特征选择时 Ridge 不合适它的价值是处理多重共线性——两个特征强相关时 Lasso 可能随机只保留一个Ridge 会把权重分散到两个特征上模型更稳定。实际业务里我常用的组合是“Lasso 选特征 Ridge 精调权重”先用 Lasso 找到关键特征子集再用 Ridge 在这些特征上拟合最终系数。评估指标的选取也要多说一句。MSE 对量纲敏感和业务指标对不上时可用 R²。R² 越接近 1 越好但 R² 在测试集上为负也不罕见说明模型比直接用均值预测还差这时候去查一下是不是训练测试分布差异过大比继续调 alpha 更有意义。3.3 alpha 与正则化路径怎么判断当前值是否合理alpha 是正则化强度控制惩罚项在损失函数里的占比。alpha 越大系数压缩得越狠Lasso 的非零特征越少Ridge 系数普遍变小alpha 趋近零模型退化为普通最小二乘。理解这一点的最好方式是画正则化路径。import numpy as np from sklearn.linear_model import lasso_path import matplotlib.pyplot as plt alphas, coefs, _ lasso_path(X_train, y_train, eps1e-3, n_alphas100) for coef in coefs: plt.plot(np.log10(alphas), coef) plt.xlabel(log(alpha)) plt.ylabel(coefficients) plt.title(Lasso regularization path) plt.show()lasso_path会从大到小扫一串 alpha 值返回每个 alpha 下的完整系数矩阵。画出来之后你会看到曲线从左边大 alpha 的零位置开始随着 alpha 变小依次“长出来”。那条曲线离开零点的位置就是该特征开始进入模型的临界点对解释特征重要性非常有用——临界 alpha 越小说明这个特征越重要需要更大的惩罚才能把它压掉。看路径图还有一个用处确认 alpha 的取值范围。如果你的数据让最优 alpha 落在路径的末端或起始端说明你设的范围不合适需要扩展。regreg 的 LASSO demo notebook 里就有类似的正则化路径绘图比只报一个 MSE 数字信息量大得多。另外lasso_path里的eps1e-3控制最小 alpha 与最大 alpha 的比值如果曲线最右端还没收敛到零把这个值调大一点。4. Fused Lasso 与 Group Lasso结构惩罚项的进阶实操4.1 Fused Lasso让相邻系数差也进惩罚Fused Lasso 是 Lasso 的变体适合系数存在顺序结构的场景比如时间序列突变点检测、基因组拷贝数变异分析。它的惩罚项有两部分一部分是系数自身的 L1 范数另一部分是相邻系数之差的 L1 范数min L(w) λ1 * Σ|w_j| λ2 * Σ|w_j - w_{j-1}|第二项强制相邻系数尽量相等因此求解出来的系数具有明显的分段常数结构——一段一段的平台平台之间是突变。这对检测“哪个时间点发生了结构变化”天然合适。如果你拿普通 Lasso 来做同样的任务得到的系数是杂乱稀疏的没有分段平台这个概念解释起来也很费劲。在 regreg 库里组合惩罚的表达方式大致是这样的import regreg.api as rr loss rr.squared_error(X, y) sparsity rr.l1norm(X.shape[1], lagrange0.5) fused rr.fused_lasso(X.shape[1], lagrange0.5, spacing1) penalty sparsity fused problem rr.simple_problem(loss, penalty) solution problem.solve()fused_lasso的lagrange对应公式里的 λ2spacing1表示只对相邻系数作差。两个惩罚项相加在 regreg 里是允许的因为两者都属于可分结构的原子。求解器会根据惩罚项结构自动选择广义近端梯度法。需要提醒的是regreg 各版本 API 有少量变动如果导入报错先按 INSTALL 文件检查库版本再查对应版本的 API 文档。sklearn 里没有内置 Fused Lasso所以这个 notebook 就是这份源码包的核心价值之一。跑的时候把 λ1 和 λ2 的比例多试几个值λ2 远大于 λ1 时解趋近于一个全局常数λ2 趋近零时退化成普通 Lasso。理解这个比例变化的连续过渡比跑通一遍代码本身收获更大。4.2 Group Lasso分组惩罚与组的权重策略Group Lasso 的组团方式直接决定结果。regreg 里用 group_lasso 原子手工定义组索引import numpy as np import regreg.api as rr loss rr.squared_error(X, y) groups [[0, 1, 2], [3, 4], [5, 6, 7, 8]] penalty rr.group_lasso(groups, weightsNone, lagrange1.0) problem rr.simple_problem(loss, penalty) solution problem.solve()groups 是组索引列表每个子列表是一组特征的列索引。weights 可以按组设置不同的惩罚权重默认是每组权重取组大小的平方根这样惩罚强度不会因为组的大小而失真。如果业务上知道某些组的特征更可靠调小这些组的权重能让它们更容易被保留而不是被大组的惩罚淹没。跑完 Group Lasso 之后检查系数的方式和 Lasso 不一样。Lasso 看单个系数是否为零Group Lasso 必须看整组是否全零。我一般用np.linalg.norm(solution[group], ord2) 1e-6来判断该组是否被选中。阈值不要设太小近端梯度法得到的解不会严格为零数量级小于 1e-6 就被视为零。组的划分是这个模型最需要业务知识的环节。如果你只是为了演示手写几个组索引没问题真实项目里组的边界一定来自业务定义比如电商场景中商品类目的层级关系、风控场景中同一用户的多个行为特征。不要自动聚类定组那样得到的组没有解释性后续模型评审很难通过。4.3 Newsgroup 逻辑回归高维文本分类的 L1 惩罚Newsgroup logistic regression.ipynb 把场景从回归换到分类用 L1 惩罚的逻辑回归在 20 Newsgroups 数据集上做文本分类。文本分类的特征维度通常有几万维L1 正则化在这里的核心价值是特征选择——把无关词对应的权重压成零只留下有区分度的词。sklearn 里 L1 逻辑回归的实现是from sklearn.linear_model import LogisticRegression model LogisticRegression( penaltyl1, solverliblinear, C1.0, max_iter2000, random_state42 ) model.fit(X_train, y_train)注意这里的超参数不是 alpha 而是 CC 是正则化强度的倒数。C 越大正则化越弱越容易过拟合C 越小正则化越强系数越稀疏。solver 的选择有硬约束liblinear 和 saga 支持 L1lbfgs 不支持。数据量在万级以下用 liblinear更大规模用 sagasaga 还能配合 l1_ratio 做 ElasticNet 的弹性惩罚。在跑这个 notebook 之前文本数据一般要经过 TF-IDF 向量化把原始文本转成词频或词权重矩阵。你可以在 notebook 里验证一下稀疏率——L1 逻辑回归在两万维词袋特征上通常能把 80% 以上的系数压成零这个稀疏率直接影响了线上预测的推理开销是你在工程落地时最关心的指标。另一个观察点是类别数量对结果的影响20 Newsgroups 有 20 个类别决策边界相当复杂L1 逻辑回归在这种情况下往往要选足够大的 C 才能保证模型有足够的表达能力。5. 避坑正则化回归实战中的五个经典翻车现场5.1 不做标准化就上 Lasso系数全乱现象特征里既有几千量级的数值变量又有 0-1 的哑变量跑完 Lasso 后数值变量的系数几乎全为 0哑变量的系数大得离谱业务上完全解释不通。原因L1 惩罚对所有系数绝对值求和量纲大的特征哪怕系数只有 0.01惩罚贡献远比量纲小的特征大于是数值变量被优先压到零。这纯粹是尺度问题不是相关性或者重要性的反映。解决训练前先 StandardScaler 标准化把每个特征变为零均值、单位方差再进入 Lasso。标准化后的系数绝对值才有可比性。注意测试集要用训练集上拟合好的 scaler 做 transform不要重新 fit否则数据泄漏会让评估结果虚高。5.2 alpha 用默认值结果要么全零要么不稀疏现象Lasso(alpha1.0) 跑出来系数全为零换一个数据集又几乎全不为零怎么看都不对手动试几个 alpha 又毫无章法。原因alpha1.0 是 sklearn 默认的占位值不代表适合你的数据。正则化强度要跟特征维度、样本量、噪声水平匹配固定值必然翻车。手动试出来的 alpha 没有统计学依据也难说服同事和评审。解决不要手动试 alpha交给 LassoCV 沿正则化路径自动搜索。LassoCV(cv5, n_alphas100)会从大到小扫 100 个 alpha用交叉验证选出最优值。拿到结果之后再手动单独跑一次 Lasso逻辑更清楚也方便保存模型参数。cv 设 5 是经验值数据量小可以降到 3数据量大可以升到 10但运行时间会明显增加。5.3 ADMM 收敛判据太松分布式结果跟单机对不上现象Distributed LASSO 的 notebook 跑完目标函数数值和单机 Lasso 求解的结果对不上误差明显偏大甚至系数分布都不一样。原因ADMM 被提前截断了。很多实现只检查原始残差忽略对偶残差而原始残差下降快、对偶残差下降慢只看前者的提前终止会让解停留在目标函数梯度还没消失的位置。解决确认 notebook 里是否同时计算原始残差和对偶残差把收敛阈值从默认的 1e-3 收紧到 1e-5同时限制最大迭代次数防止死循环。更稳的验证方法是把 ADMM 的解当作初始值用坐标下降法再优化几十步如果目标函数还能下降说明 ADMM 没算到位。5.4 Group Lasso 的组权重没调重要小组被整组吞掉现象业务上明确某几个特征组成组很重要但 Group Lasso 跑完这一组系数全是零怎么调 lagrange 都出不来。原因默认组权重和组大小挂钩大组惩罚重容易被淘汰。业务重要的组如果内部冗余度高信息分散在多个系数上连续 L2 惩罚让整组在未被选中前就被压至零。解决把 weights 参数从默认值调低比如 0.5减轻这组特征的惩罚。调权的尺度要从 0.5 到 1.5 之间做小范围对比调太小会退化成普通 Lasso失去分组约束调太大又把这组特征完全压死。5.5 L1 逻辑回归选错 solver直接报错或告警现象LogisticRegression(penaltyl1) 刚跑就报错提示 lbfgs 不支持 L1换到 saga 后又出现迭代不收敛的告警。原因sklearn 里不是所有 solver 都支持 L1lbfgs 和 newton-cg 只支持 L2liblinear 支持 L1 但只适合小数据saga 支持 L1 且适合大数据。另外逻辑回归的 max_iter 默认只有 100高维数据上迭代数不够就会告警。解决小数据集用 solverliblinear大数据集用 solversaga同时把 max_iter 调到 1000 以上tol 设 1e-4。跑文本分类这种几万维特征的任务我一般 saga 加 max_iter2000 起步先看收敛曲线再决定要不要继续加。6. 用交叉验证锁定 alpha一套可复用的三步调参流程最后落到一个可以反复使用的干活流程。之前讲了那么多原理和坑核心就一句话alpha 不要拍脑袋让数据自己回答。下面这三步我每次做正则化回归基本都走一遍。第一步粗扫定数量级。用 LassoCV 在默认正则化路径上跑一次确定最优 alpha 大约在什么量级以及此时保留了多少个非零特征。from sklearn.linear_model import LassoCV lasso_cv LassoCV(cv5, n_alphas100, random_state42, max_iter10000) lasso_cv.fit(X_train, y_train) print(f粗扫最优 alpha: {lasso_cv.alpha_:.4f}) print(f非零系数个数: {np.sum(lasso_cv.coef_ ! 0)})第二步精扫锁定区间。以粗扫得到的 alpha 为中心在它 0.5 到 1.5 倍的区间里用 GridSearchCV 做 21 个点的细网搜索得到更精确的最优值。这一步缓解了 LassoCV 默认网格比较稀疏的问题能让最终选出的参数更贴合当前数据集。from sklearn.model_selection import GridSearchCV from sklearn.linear_model import Lasso best_alpha lasso_cv.alpha_ grid GridSearchCV( Lasso(max_iter10000, tol1e-4), {alpha: np.linspace(best_alpha * 0.5, best_alpha * 1.5, 21)}, cv5, scoringneg_mean_squared_error ) grid.fit(X_train, y_train) print(f精扫最优 alpha: {grid.best_params_[alpha]:.4f})第三步验证稳定性。把最优 alpha 附近的测试集 MSE 画出来观察曲线在最小值附近是否平缓。如果曲线收得很尖说明模型对 alpha 极其敏感上线后数据稍微漂移性能就大幅波动这比 MSE 本身高低更要命。遇到尖峰我一般会往较大 alpha 一侧偏移一点让模型更保守牺牲少量训练精度换取稳健性。从那以后我每次跑 Lasso 都强制走一遍这个流程先粗扫、再精扫、最后看稳定性不再凭感觉填 alpha 就提交结果。这套流程同样适用于 Ridge、ElasticNet 和 Group Lasso只是把模型类、参数名和评估指标换成对应的配置。正则化回归的坑大多出在超参数选择上这套流程能把最多的不确定性消除掉希望帮到你。本文还有配套的精品资源点击获取