新闻详情

新闻详情

首页 / 资讯中心 / 详情

岭回归与Lasso实战:Python实现、参数调优与避坑指南

发布时间:2026/9/27 1:41:03来源:尧图网络
岭回归与Lasso实战:Python实现、参数调优与避坑指南
简介这是一份面向数据分析与机器学习初学者的PDF学习资料聚焦岭回归与Lasso等正则化回归模型。资料从线性回归的短板切入系统说明自变量个数大于样本量、存在多重共线性等场景下模型失效的原因并详细讲解如何通过L2平方项与L1绝对值惩罚项在保留重要特征的同时压缩系数从而提升模型的泛化能力。内容涵盖理论原理、公式解析与基于sklearn的Ridge、Lasso、RidgeCV、LassoCV实现代码以糖尿病数据集为案例演示交叉验证选择最优alpha参数、RMSE评估、F检验判断模型整体显著性、T检验检验单个系数显著性等完整建模流程还补充了statsmodels多元线性回归对比方便理解不同模型的适用场景。压缩包共1个PDF文件约907KB内容紧凑、便于离线阅读目前已有218人浏览学习。1. 当最小二乘开始“飘”的时候就该上岭回归与 Lasso 了做回归建模的人大概率都撞过这么一堵墙用 Python 调完sklearn.linear_model里的LinearRegression跑出来的 R² 还挺漂亮但一回看系数发现某几个特征的系数绝对值大得离谱正负号也跟业务直觉完全拧着来。这时候如果顺手算一下特征之间的相关系数基本就是一片红——存在多重共线性。OLS 在这个场景下会把系数方差撑得巨大模型成了一把“飘”的尺子。岭回归与 Lasso 这类带 L1/L2 惩罚的回归模型正是为压住这种飘而生的它们通过牺牲一点点偏差把方差和系数幅度降下来换取更稳的预测和能讲道理的特征解释。这篇就顺着“什么时候用、怎么调、坑在哪”的顺序把岭回归与 Lasso 在 Python 里的实现路径完整走一遍。先说明一个关键的认知岭回归是 L2 惩罚Lasso 是 L1 惩罚这俩不是竞争关系而是分工关系。如果你的目标是预测精度和系数稳定性岭回归是默认选择如果你的目标是特征筛选、想把无关变量直接压缩成 0Lasso 才真正上场。而所谓的“含 Python 语言实现”并不只是调一个Lasso()构造函数它涉及特征标准化、惩罚系数 λ 的搜索、哑变量处理、结果的可解释性检查这一整条链路。接下来从原理和选型讲起再落到能直接跑的代码。2. 回归模型加惩罚到底是什么代价函数与系数收缩机制2.1 从最小二乘的病态讲到 L2 惩罚先给出传统最小二乘的目标函数最小化残差平方和$$\hat{\beta}{OLS} \arg\min\beta \sum_{i1}^{n} (y_i - X_i\beta)^2$$这个目标函数本身没有任何问题问题出在 X 矩阵的性质上。当特征之间存在较强相关性时X^T X 会接近奇异矩阵其逆矩阵的对角元素变得极大导致 β 的估计方差急剧膨胀。此时最小二乘的解虽然仍然是无偏的但方差大到让系数失去稳定性——训练集稍微换几个样本系数就剧烈变动。岭回归在 OLS 基础上加了一个 L2 惩罚项$$\hat{\beta}{ridge} \arg\min\beta \sum_{i1}^{n} (y_i - X_i\beta)^2 \lambda \sum_{j1}^{p} \beta_j^2$$这个 λ 就是惩罚强度。当 λ 趋近于 0岭回归退化为 OLS当 λ 增大系数被往 0 方向均匀压缩但不会真正变成 0。这正是 L2 的核心特征它做的是“收缩”而不是“选择”。下面用一个非常小的数据来观察这个收缩过程直接展示系数随 λ 变化的轨迹。运行这段代码前先确保环境里有numpy、pandas、matplotlib、sklearn这四个库是后面所有实操的基础。import numpy as np import pandas as pd from sklearn.linear_model import Ridge from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt # 构造一个存在较强共线性的数据集 np.random.seed(42) n 200 x1 np.random.normal(0, 1, n) x2 0.8 * x1 np.random.normal(0, 0.3, n) # 与 x1 强相关 x3 np.random.normal(0, 1, n) y 3 * x1 0.5 * x2 - 2 * x3 np.random.normal(0, 0.5, n) # 真实系数3, 0.5, -2 X np.column_stack([x1, x2, x3]) scaler StandardScaler() X_scaled scaler.fit_transform(X) # 在不同 lambda 下训练岭回归 alphas np.logspace(-4, 3, 200) coefs [] for a in alphas: ridge Ridge(alphaa) ridge.fit(X_scaled, y) coefs.append(ridge.coef_) coefs np.array(coefs) # 绘制岭迹图 plt.figure(figsize(9, 5)) for i in range(3): plt.plot(alphas, coefs[:, i], labelfx{i1}) plt.xscale(log) plt.xlabel(lambda (log scale)) plt.ylabel(coefficient value) plt.title(Ridge trace plot) plt.legend() plt.grid(True, alpha0.3) plt.show()先说这段代码的意图构造了 x1 和 x2 两个高度相关的特征真实系数是 3 和 0.5但 OLS 在这种共线性下往往给出很不稳定的系数。通过岭迹图能看到每个系数随 λ 增大被匀速压缩的过程。L2 惩罚对系数是“按比例收缩”系数绝对值大或被特征方差影响大的变量惩罚越重但永远不会归零。这一点决定了岭回归不适合做特征选择。2.2 L1 惩罚如何把系数压成 0Lasso 的几何本质Lasso 的目标函数$$\hat{\beta}{lasso} \arg\min\beta \sum_{i1}^{n} (y_i - X_i\beta)^2 \lambda \sum_{j1}^{p} |\beta_j|$$L1 惩罚与 L2 的差别不是简单的绝对值与平方之差。从几何视角看L2 惩罚项的等值线是一个圆形区域L1 惩罚项则是一个菱形在二维情形下。圆形与残差平方和的椭圆等值线相切时切点往往落在坐标轴以外而菱形的顶点恰好位于坐标轴上所以 L1 惩罚的解有更高概率落在某个系数为 0 的位置。这就是 Lasso 可以把不重要的特征系数精确压缩为 0 的原因。提示实际实现中不必关心这个几何过程但理解它有助于明白为什么 Lasso 的效果是“特征选择”以及为什么在高维稀疏场景下 Lasso 表现突出。Lasso 在 Python 中的实现路径比岭回归更值得讲究一方面sklearn.linear_model.Lasso有lars、coordinate_descent两种路径可选另一方面当特征数量远大于样本数量时Lasso 最多只能选出 n-1 个特征这是一个硬性边界后面在避坑章节会单独展开。下面演示 Lasso 的变量选择过程from sklearn.linear_model import Lasso # 扩展特征数加 10 个纯噪声特征 rng np.random.default_rng(42) X_noise rng.normal(0, 1, (n, 10)) X_full np.column_stack([X, X_noise]) X_full_scaled scaler.fit_transform(X_full) # 用较大的 lambda 让 Lasso 做特征筛选 lasso Lasso(alpha0.2, max_iter10000) lasso.fit(X_full_scaled, y) coef_series pd.Series(lasso.coef_, index[fx{i1} for i in range(13)]) print(Lasso 系数0 表示被淘汰) print(coef_series)实际运行这段代码会看到前三个系数保持非零但数值被压缩后面的 10 个噪声特征系数基本全是 0。这就是 Lasso 跟岭回归最本质的使用差异岭回归给出一个更稳但系数不稀疏的模型Lasso 直接给你一张“哪些变量被留下”的清单。然后需要强调的是上面代码里的alpha0.2是一个靠经验拍的值实际项目中 α 的选择必须依赖交叉验证这部分在后面参数搜索章节详细展开。3. Python 语言实现回归模型标准化、交叉验证与系数解读3.1 为什么必须先做标准化再训练惩罚项对尺度敏感在跑任何带惩罚的线性回归之前先问自己一个问题你的特征是否在同一量纲上如果 x1 的取值范围是 [0, 1]x2 的取值范围是 [0, 100000]那么 L2 惩罚项会优先压缩 x2因为它的系数稍微一动β_j² 的变化就比 x1 大得多。这不是模型在识别重要性只是尺度在捣乱。所以在岭回归与 Lasso 的实现里标准化的标准做法是from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X) # 训练完模型后预测新数据时也要用同一个 scaler 做转换 X_new np.array([[0.5, 0.3, -1.2]]) X_new_scaled scaler.transform(X_new) pred ridge.predict(X_new_scaled)注意这里的关键点fit_transform用在训练集上transform用在测试集或新数据上。千万不能对测试集单独做fit_transform否则会把测试集的均值和方差偷偷带进模型这就是典型的数据泄漏。但还有一个常被忽略的问题标准化之后的系数不再代表“原始特征每变化一个单位对 y 的影响”而是“该特征每变化一个标准差对 y 的影响”。这也是为什么很多数据分析师觉得自己做完标准化之后“系数看不懂了”。实际落地时我的习惯是同时保留两套系数一套标准化后的系数用于比较变量重要性一套反标准化后的系数用于业务解释。反标准化的换算公式是# 将标准化后的系数还原为原始尺度 orig_coef ridge.coef_ / scaler.scale_ orig_intercept ridge.intercept_ - np.sum(scaler.mean_ / scaler.scale_ * ridge.coef_) print(原始尺度下的系数, orig_coef) print(原始尺度下的截距, orig_intercept)这段换算的原理很简单标准化公式是x_std (x - mean) / scale把它代回线性方程展开就能得到原始系数与截距。但需要注意的是只有在训练时对特征做了标准化、没有对 y 做标准化的情况下这个公式才适用。如果 y 也被标准化了换算会多一个步骤后面有时间再展开。3.2 交叉验证选 λLassoCV与RidgeCV的实际用法lambda 是岭回归与 Lasso 里最核心的超参数。选太小惩罚等于没有模型退化为 OLS选太大系数被压到接近 0模型变成预测均值。实践中最可靠的方法是 K 折交叉验证把训练数据切成 K 份轮流用 K-1 份训练、1 份验证记录不同 λ 下的验证误差选误差最小的那个 λ。sklearn已经把这个过程封装成了RidgeCV和LassoCV它们的内部实现除了交叉验证之外还内置了高效的路径算法——LassoCV走的是坐标下降的整个正则化路径这意味着它会一次性计算很多个 λ 的系数而不是每次都从头训练。from sklearn.linear_model import RidgeCV, LassoCV from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error # 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X_full_scaled, y, test_size0.2, random_state42 ) # RidgeCV内置交叉验证自动选 lambda ridge_cv RidgeCV(alphasnp.logspace(-3, 3, 100), cv5) ridge_cv.fit(X_train, y_train) print(fRidge 最优 lambda: {ridge_cv.alpha_:.4f}) print(fRidge 测试集 RMSE: {mean_squared_error(y_test, ridge_cv.predict(X_test), squaredFalse):.4f}) # LassoCV同样内置交叉验证 lasso_cv LassoCV(alphasnp.logspace(-3, 1, 100), cv5, max_iter100000) lasso_cv.fit(X_train, y_train) print(fLasso 最优 lambda: {lasso_cv.alpha_:.4f}) print(fLasso 非零系数个数: {np.sum(lasso_cv.coef_ ! 0)})这里需要解释几个细节alphas参数传入的是一个候选 λ 列表sklearn会在这个列表里做交叉验证搜索。alphas的范围设置很关键——如果范围太大计算耗时猛增如果范围太小可能错过最优区域。我的实践经验是先给一个宽范围比如np.logspace(-4, 2, 200)跑完后打印alpha_看它落在范围中间还是边缘。如果最优 λ 落在候选列表的边界上说明范围设置不合理需要扩大范围重新搜索。cv5是 5 折交叉验证对于小样本数据集几百条我建议改成cv10或使用留一法LeaveOneOut因为折数太少会导致验证集过小λ 的估计噪声很大。max_iter这个参数同样值得注意Lasso 的求解是迭代算法默认的迭代次数在大 λ 值或高维数据下可能不收敛。运行时如果出现ConvergenceWarning优先调大max_iter而不是忽略警告。3.3 一个完整的最小可复现代码框架把前面几步串起来给出一个完整的建模流程也方便对照自己的项目做改造import numpy as np import pandas as pd from sklearn.linear_model import LassoCV, RidgeCV from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score def build_regularized_model(df, target_col, model_typelasso): 通用带惩罚的线性回归建模流程 model_type: lasso 或 ridge # 1. 分离特征与目标 X df.drop(columns[target_col]) y df[target_col].values # 2. 划分训练集/测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 3. 标准化 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 4. 交叉验证选择模型 if model_type lasso: model_cv LassoCV(cv5, max_iter100000, random_state42) else: model_cv RidgeCV(alphasnp.logspace(-3, 3, 100), cv5) model_cv.fit(X_train_scaled, y_train) # 5. 评估 y_pred model_cv.predict(X_test_scaled) rmse mean_squared_error(y_test, y_pred, squaredFalse) r2 r2_score(y_test, y_pred) # 6. 输出结果 coef_df pd.DataFrame({ feature: X.columns, coef_scaled: model_cv.coef_, abs_coef: np.abs(model_cv.coef_) }).sort_values(abs_coef, ascendingFalse) print(f模型类型: {model_type}) print(f最优 lambda: {model_cv.alpha_:.4f}) print(f测试集 RMSE: {rmse:.4f}) print(f测试集 R²: {r2:.4f}) print(f非零系数数量: {np.sum(model_cv.coef_ ! 0)}) print(\n变量重要性排序) print(coef_df.head(10)) return model_cv, scaler, coef_df # 使用示例 # model, scaler, coef_df build_regularized_model(your_df, target_column, lasso)这个函数把标准化、交叉验证、模型训练、评估和变量重要性排序全部封装干净换了数据集之后只需要把df和target_col填进去就能跑。但需要注意两点第一这个函数默认数据里没有缺失值有缺失需要先做填充或删除第二如果特征是类别变量必须先做编码one-hot 或 ordinal直接丢进标准化会导致编码后的哑变量被同等缩放这在某些场景下是有问题的下一章细说。4. 岭回归与 Lasso 的进阶变体自适应 Lasso 与弹性网在 Python 里的实现思路4.1 自适应 Lasso把特征选择的偏差掰回来一点Lasso 有一个被很多人忽略的缺陷它对所有系数施加的是相同强度的 L1 惩罚。这导致大系数被过度压缩而小系数被压缩得不够最终的特征选择结果在理论上不是一致的。自适应 Lasso 的改进思路很直观先用岭回归得到一个初始系数估计然后用这个估计的倒数给每个特征加权重要特征得到更小的惩罚不重要特征得到更大的惩罚。在 Python 里没有现成的AdaptiveLasso类但实现它只需在Lasso的基础上做一步预处理——给每个特征乘上权重from sklearn.linear_model import Ridge, Lasso # 第一阶段用岭回归得到初始系数 ridge_init Ridge(alpha1.0) ridge_init.fit(X_train_scaled, y_train) # 计算权重系数绝对值的倒数加一个小常数防止除零 gamma 1.0 weights 1.0 / (np.abs(ridge_init.coef_) 1e-6) ** gamma # 对特征加权 X_train_weighted X_train_scaled / weights X_test_weighted X_test_scaled / weights # 第二阶段在加权特征上跑标准 Lasso adaptive_lasso Lasso(alpha0.05, max_iter100000) adaptive_lasso.fit(X_train_weighted, y_train) # 还原系数到原始特征空间 adaptive_coef adaptive_lasso.coef_ / weights print(自适应 Lasso 系数, adaptive_coef)此处关键代码逻辑在于weights的计算公式也就是1.0 / (abs(coef) 1e-6)的写法。这里的1e-6不是随便加的——如果某个特征在岭回归中系数刚好为 0weights会变成无穷大导致后续计算崩溃。所以必须加一个极小常数做平滑。gamma是一个可调参数通常设为 1它对最终特征选择的结果影响很大。注意自适应 Lasso 的alpha不能直接照搬普通 Lasso 的交叉验证结果因为特征已经加权变换了两者不在同一尺度下。严格的做法是对新生成的加权特征重新做交叉验证选 λ。理论上如此但实际项目中很多人会直接用普通 Lasso 的 α 作为起始值再手动微调。4.2 弹性网L1 L2当特征分组相关时 Lasso 会翻车前面已经提到Lasso 在特征数量超过样本数量时最多只能选出 n-1 个特征。除此之外还有一个更隐蔽的问题当一组特征彼此高度相关时Lasso 会像抓阄一样只随机选其中一个而丢掉其他相关的特征。这在基因表达数据、股票因子数据这类特征天生分群的场景里是极为糟糕的性质。弹性网把 L1 和 L2 惩罚按比例混合$$\hat{\beta}{enet} \arg\min\beta \sum (y_i - X_i\beta)^2 \lambda_1 \sum |\beta_j| \lambda_2 \sum \beta_j^2$$L1 部分负责稀疏性L2 部分负责让相关特征的系数趋近于彼此接近。这只“组效应”能力对做量化交易因子筛选的人来说是刚需——一组技术指标往往高度同源用 Lasso 可能只留下一两个用弹性网则能保留一个完整的因子族。sklearn里对应的是ElasticNetCV类和LassoCV相比多了一个l1_ratio参数它控制 L1 惩罚所占的权重from sklearn.linear_model import ElasticNetCV enet_cv ElasticNetCV( l1_ratio[0.1, 0.3, 0.5, 0.7, 0.9], alphasnp.logspace(-3, 1, 100), cv5, max_iter100000, random_state42 ) enet_cv.fit(X_train_scaled, y_train) print(f最优 alpha: {enet_cv.alpha_:.4f}) print(f最优 l1_ratio: {enet_cv.l1_ratio_:.2f}) print(f非零系数个数: {np.sum(enet_cv.coef_ ! 0)})l1_ratio的语义是l1_ratio1时为纯 Lassol1_ratio0时为纯岭回归。ElasticNetCV内部会同时对alpha和l1_ratio做网格搜索计算量比LassoCV大不少。在特征数上万的数据集上跑弹性网耗时可能是 Lasso 的几倍如果计算资源紧张建议先用LassoCV快速确定一个 α 量级再在附近用弹性网精细搜索。4.3 强基线对比xgboost 回归模型在什么情况下会取代线性模型提到岭回归与 Lasso 等回归模型有经验的从业者一定会想追问一个问题既然 xgboost、lightgbm 这些树模型在非线性关系上的拟合能力碾压线性模型那还有必要学这套东西吗我的回答是如果你只关心预测精度且样本量足够大、数据里非线性关系明显xgboost 回归模型确实是更好的选择。但有三类场景线性模型不可替代。第一小样本场景几百条样本的表格数据上xgboost 非常容易过拟合而岭回归的归纳偏置反而成了优势配合交叉验证甚至能取得与树模型相当的精度。第二特征解释性要求极高的场景比如金融风控里的评分卡监管要求模型的每个变量有明确权重和业务含义xgboost 的特征重要性只能反映分裂频率无法给出系数方向的解读。第三高维稀疏场景比如文本 TF-IDF 特征或反欺诈特征工程线性模型配合 L1 惩罚在十万一百万维度下依然可控可解释xgboost 在这个规模下训练耗时和内存都是灾难。上面的讨论给一个实用的建模建议在做复杂模型之前先把岭回归和 Lasso 的基线结果跑出来。这有两个作用一是给自己一个精度底线二是用线性模型的系数做一些粗筛剔除掉明显无关的特征缩小后续树模型的特征搜索空间。5. 岭回归与 Lasso 在 Python 实现中的五个典型避坑记录5.1 坑一忘掉标准化系数全在乱说现象特征x2的量纲是x1的 10000 倍训练完 Lasso 之后发现x2的系数几乎为 0于是判定x2不重要。但业务方说x2是核心指标。原因L1 惩罚对尺度大的特征惩罚更重压掉的不是“不重要”而是“数值大”。惩罚项对不同尺度的特征是不公平的。解决在任何带惩罚的回归之前无条件执行StandardScaler。如果你的业务场景不允许改变特征含义那就用前面 3.1 节末的反标准化公式把系数还原做解释。5.2 坑二哑变量被标准化之后失去对比基准现象对类别特征做了 one-hot 编码后直接进入StandardScaler然后训练 Lasso。每个哑变量的系数出现了正负交错业务逻辑无法解释。原因哑变量标准化后变成均值为 0、方差为 1 的连续变量原有的“与基准类别对比”含义被打破了。比如性别特征编码成is_male标准化之前系数 0.5 的含义是“男性比女性平均高 0.5 个单位”标准化之后这个值不再是直接可解释的概率差。解决常见做法是连续特征做标准化哑变量保持 0/1 原样传入模型。sklearn里可以借助ColumnTransformer实现这个混合处理from sklearn.compose import ColumnTransformer from sklearn.preprocessing import StandardScaler, OneHotEncoder # 假设 df 有 age(数值) 和 gender(类别) preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), [age]), (cat, OneHotEncoder(dropfirst), [gender]) ]) X_processed preprocessor.fit_transform(df[[age, gender]])这个方案既保留了哑变量的可解释性又让连续特征被标准化。5.3 坑三最优 λ 落在搜索范围边缘现象LassoCV跑完打印出来的alpha_恰好等于你传入alphas数组的最小值或最大值。此时你以为找到了最优其实搜索范围设定不合理。原因LassoCV只会在你给定的候选值里择优如果真正的 λ 比候选范围更小或更大它会“选”边缘值凑合但这不是最优。解决检查alpha_是否等于alphas[0]或alphas[-1]。若是扩大该方向的搜索范围后重跑。这是一个很容易被忽略的质量检查步骤。5.4 坑四Lasso 在特征多于样本时只能选出固定数量的特征现象数据集有 100 行但 500 个特征Lasso 跑完正好选了 99 个非零系数你还以为模型刚好找到 99 个重要特征。原因这是 L1 惩罚的数学性质决定的——在 p n 的场景下Lasso 最多选择 n-1 个非零系数。这个上限导致它不能同时保留所有真正重要的特征只能从中挑一部分。解决切换到弹性网L2 部分允许更多特征同时非零或是先用相关性或单变量筛选把特征降到 n/10 以下再跑 Lasso。5.5 坑五用标准化后的系数大小直接解释变量重要性现象变量 A 的标准化系数是 0.8变量 B 是 0.2于是直接得出结论“A 的重要性是 B 的 4 倍”。原因这个结论只有在所有特征线性独立且相互正交时才近似成立。当特征之间存在相关性时系数大小受到惩罚强度和共线性的双重影响不能直接做比例比较。解决将系数绝对值排序用来做粗排序没问题但要小心解读。更稳健的做法是看permutation_importance通过打乱某个特征后 RMSE 的恶化程度来判断真实重要性。这个指标线性模型同样适用且算法对共线性并不友好但对业务解释足够直观。from sklearn.inspection import permutation_importance result permutation_importance( lasso_cv, X_test_scaled, y_test, n_repeats10, random_state42 ) importance_df pd.DataFrame({ feature: X.columns, importance_mean: result.importances_mean, importance_std: result.importances_std }).sort_values(importance_mean, ascendingFalse) print(importance_df.head(10))6. 信用评分卡场景实操当变量选择遇上交叉验证最后给出一个更贴近真实业务的进阶技巧如何把岭回归与 Lasso 的训练过程嵌入到一个完整的金融风控建模任务里。在这个场景中开发者通常要面对的是包含几十个数值特征、数百万条样本的表格数据同时还要面对一个核心目标——在变量维度上做出稳定的、有业务解释性的选择。先跑一个基于模拟数据的完整流程涵盖数据清洗、特征筛选、模型选择、结果稳定性检验这四个环节。下面的代码是从实际项目中抽出来的结构数据字段做了脱敏import numpy as np import pandas as pd from sklearn.model_selection import cross_val_score, KFold from sklearn.linear_model import LassoCV, RidgeCV, ElasticNetCV from sklearn.preprocessing import StandardScaler # 模拟信贷数据特征名为 f1-f20目标为是否违约的连续化风险分 np.random.seed(7) n 5000 X_sim np.random.randn(n, 20) # 让部分特征相关形成分组 X_sim[:, 3] 0.7 * X_sim[:, 1] 0.3 * np.random.randn(n) X_sim[:, 7] 0.6 * X_sim[:, 2] 0.4 * np.random.randn(n) # 真实模型f1, f2, f3, f5 有效其余噪声 y_sim (2.5 * X_sim[:, 1] 1.8 * X_sim[:, 2] 0.9 * X_sim[:, 3] - 1.2 * X_sim[:, 4] np.random.randn(n) * 0.8) # 同样的公式这次跑在更大的样本量上对比三种模型 scaler StandardScaler() X_scaled scaler.fit_transform(X_sim) cv KFold(n_splits5, shuffleTrue, random_state42) lasso_cv_model LassoCV(cvcv, max_iter50000, random_state42) ridge_cv_model RidgeCV(alphasnp.logspace(-3, 2, 100), cvcv) lasso_cv_model.fit(X_scaled, y_sim) ridge_cv_model.fit(X_scaled, y_sim) # 用交叉验证分数比较模型稳定性 from sklearn.model_selection import cross_val_predict from sklearn.metrics import r2_score # 模型得分对比 models { Lasso: lasso_cv_model, Ridge: ridge_cv_model } for name, model in models.items(): scores cross_val_score(model, X_scaled, y_sim, cvcv, scoringr2) print(f{name}: R2 {scores.mean():.4f} ± {scores.std():.4f})这段代码演示了模型比较的规范流程。常见的错误做法是只报一个测试集上的分数而忽略了交叉验证分数之间的波动幅度。Lasso和Ridge在交叉验证中如果精度接近但std相差巨大倾向于选择std更小的那一个——因为它更稳定。在这个模拟数据里因为有真实的噪声特征Lasso的R²通常会略优于Ridge因为它把噪声特征直接清零减少了模型的无效复杂度。最后再补充一个稳定性检验的实用技巧——重复多次交叉验证。由于交叉验证的样本切分本身就带有随机性跑一次的结果不能代表模型真实水平。我一般至少重复 3 次以上from sklearn.model_selection import RepeatedKFold rkf RepeatedKFold(n_splits5, n_repeats3, random_state42) scores cross_val_score(lasso_cv_model, X_scaled, y_sim, cvrkf, scoringr2) print(f3 次 5 折交叉验证结果: R2 {scores.mean():.4f} ± {scores.std():.4f})当scores.mean()和scores.std()报告完毕有一个我观察了很久的经验如果模型在交叉验证中的均值与测试集分数之间的差距超过 0.05多半是数据分布不一致或过拟合了需要回到特征工程或数据划分上查找问题而不是继续调 λ。在小样本建模的场景下——比如只有 200 条样本这样的典型场景——上面提到的所有陷阱都会成倍放大。交叉验证折数要增多alphas的搜索范围要收窄标准化和哑变量处理更要做对。有些坑在小数据上是致命的但在大数据上只是多一个警告这也是为什么很多人在数据量大的地方跑通了一套代码换到小数据上就全线飘红。从岭回归到 Lasso再到弹性网这一整个家族的工具共同解决的问题不是“如何让模型更复杂”而是“如何在简单模型里学会克制”。对很多而言学会调alpha很容易难的是在模型精度、变量解释性和稳定性之间找到那个平衡点。只要把交叉验证当作选择 λ 的唯一裁判把系数稳定性当作模型的健康指标这套思路在结构化数据上基本能一直复用。希望这段从原理到实操的梳理能帮你少走一段弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

25个真正可用的SVG图标网站推荐(开发者实测) 2026/9/27 2:35:01

25个真正可用的SVG图标网站推荐(开发者实测)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
做新媒体的小说网站实战案例:搞定备案不头疼 2026/9/27 2:35:01

做新媒体的小说网站实战案例:搞定备案不头疼

做新媒体的小说网站实战案例:搞定备案不头疼 备案号还没下来,服务器就被运营商断网,这种憋屈事你遇到过吗?做新媒体的小说网站,最让人头大的往往不是代码写不出来,而是备案流程一头雾水。我见过太多创业者,站点做得花里胡哨,结果卡在工信部ICP备案…

阅读更多 →
全志T113-S3 RGB屏移植避坑指南:从设备树到LVGL触摸校准 2026/9/27 2:34:55

全志T113-S3 RGB屏移植避坑指南:从设备树到LVGL触摸校准

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
树莓派5双2.5G网口扩展实战:PCIE Switch实现NVMe与网卡共存 2026/9/27 2:34:55

树莓派5双2.5G网口扩展实战:PCIE Switch实现NVMe与网卡共存

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
RK3588移植Ubuntu 24.04根文件系统实战指南 2026/9/27 2:34:48

RK3588移植Ubuntu 24.04根文件系统实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
内存测试核心:Shmoo图与RMT分析原理及工程实践 2026/9/27 2:34:48

内存测试核心:Shmoo图与RMT分析原理及工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉