ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python基本算法实现正则化多项式拟合:从最小二乘到岭回归

Python基本算法实现正则化多项式拟合:从最小二乘到岭回归 简介面向毕业设计学生的机器学习实践资料聚焦正则化的多项式拟合并涵盖主成分分析、EM算法、逻辑斯蒂回归等模型适合数据分析、模式识别类课程设计。压缩包共31个文件以10个py脚本、4份Word文档、若干m脚本为主大小仅943KBpy脚本对应算法实现Word文档整理理论推导m脚本用于数值对照。已有37人学习对搭建算法对比或写实验章节的本科生很有帮助。通过源码与文档可学习L1/L2正则化抑制过拟合掌握最小二乘、优化算法、数据预处理、模型评估与可视化流程同时各Python脚本展示了主成分降维、高斯混合模型与逻辑回归的实现细节贯通从数据准备到结果分析的完整步骤。此外包内按功能划分模块便于定位适合直接运行验证并参照文档补充对比实验提升毕设说服力。1. 基于python基本算法实现正则化的多项式拟合为什么一行调库反而害了你搜到“基于python基本算法实现正则化的多项式拟合.zip”这类资源的人多半是在找课程设计源码或面试复习代码。先说一个反直觉的结论直接调np.polyfit或sklearn.linear_model.Ridge一行代码就能跑完拟合但这样你永远学不会正则化。真正值得反复看的是用 python 基本算法把最小二乘、Vandermonde 矩阵、正则化系数一层层拆开又拼起来的过程。这套方案能解决的具体问题是在没有现成库函数可用时如何从原理出发写出一套可复现的拟合流程以及正则化系数设多大、多项式阶数选多高才不至于让模型在端点处疯狂震荡。适合三类人刚学完 python 基本语法、想做综合练习的新手准备数据岗面试、需要徒手推导岭回归的求职者还有被课程设计逼着读源码的本科同学。后面所有内容都按“先搭基座再加正则化最后调参数”的顺序展开。2. 先搭“无正则化”的基座用最小二乘与正规方程做多项式拟合做正则化多项式拟合最忌讳一上来就写带惩罚项的代码。因为正则化是对最小二乘解的修正你必须先有一个“最小二乘解长什么样、在什么情况下会翻车”的基准版本。这一章先把不带动任何惩罚的普通多项式拟合完整写出来后面加正则化时你才能感受到每个参数改动的实际效果。2.1 构造多项式特征矩阵用 np.vander 把单列 x 变成幂次特征多项式拟合的第一步不是解方程是把一维的x变成多维的幂次特征矩阵。数学形式是X[i][j] x[i]^jj 从 0 取到 degree这一整个矩阵通常叫 Vandermonde 矩阵。python 里不需要用循环去乘幂np.vander一行就能生成关键是参数要设对。import numpy as np np.random.seed(42) # 生成带噪声的训练数据真值函数是 sin(pi * x)再加一点高斯噪声 x np.linspace(-1, 1, 40) y np.sin(np.pi * x) 0.1 * np.random.randn(40) def poly_features(x, degree): # increasingTrue 表示列从左到右对应 x^0, x^1, ..., x^degree # 不设这个参数默认是递减排列容易搞混系数顺序 return np.vander(x, degree 1, increasingTrue) # 用 10 次多项式做拟合 degree 10 X poly_features(x, degree) print(特征矩阵形状, X.shape) # 40 个样本11 个特征np.vander是 numpy 内置的幂次特征构造器第一列全是 1对应常数项。increasingTrue这个参数必须养成习惯带上否则默认返回的列是降幂排列后面取系数、画图对照时特别容易看反。degree决定特征个数degree10时特征数是 11其中包括x^0这一列。这个特征矩阵就是整个拟合算法的输入核心。后续所有公式里出现的X.T X、X.T y都是在和这个矩阵打交道。很多初学者在这一步直接对原始x做多项式展开导致后续代码混乱这里建议统一封装成poly_features函数后面的正规方程和正则化都能直接复用。实际项目里我还会顺手检查一下X里有没有重复列比如x^2和x^4在值域接近时相关性会很高这在后面会直接体现在条件数上。2.2 正规方程求最小二乘解用 solve 而不是 inv先看条件数无正则化多项式拟合的闭式解是w (X^T X)^{-1} X^T y它在数学上叫正规方程。实现时有一个资深工程师都会强调的细节不要用np.linalg.inv(X.T X) X.T y去算应该用np.linalg.solve。solve走的是 LU 分解计算速度和数值稳定性都远好于显式求逆。求逆本身是 O(n^3)而且在矩阵接近奇异时误差会被放大solve至少能给你一个相对可靠的解。# 正常方程求解w (X^T X)^{-1} X^T y # 用 linalg.solve 而不是 linalg.inv后者数值上更脆弱、速度更慢 XtX X.T X Xty X.T y w np.linalg.solve(XtX, Xty) # 计算训练集均方误差看拟合效果 pred X w mse np.mean((pred - y) ** 2) print(训练集 MSE, mse) # 条件数衡量 XtX 数值稳定性值越大越接近奇异 cond np.linalg.cond(XtX) print(XtX 条件数, cond)逻辑是最小化||Xw - y||^2后对w求导并令导数为零就得到X^T X w X^T y这个线性方程组np.linalg.solve直接解这个方程组。参数上最值得关心的是条件数cond。当degree10、样本数 40 时cond很容易到 10 的 8 次方以上这已经是一个危险信号意味着XtX接近奇异w的微小数值扰动会被放大到不可接受。实际画一下拟合曲线就能看到10 次多项式在中间区域贴合得很好但在x接近 ±1 的两端会出现剧烈的上下震荡。这就是过拟合在多项式拟合里的典型形态训练误差很小模型形状却在端点处失控。这种震荡不是噪声造成的而是高次项系数为了迎合个别端点样本被解算得极大正负抵消产生抖动。记住 MES 很小但曲线边缘震荡这个画面它就是后面正则化要处理的第一个目标。3. 给拟合加正则化L2、L1 与弹性网三条路的代码差异和适用场景正则化的本质是在损失函数后面追加一个惩罚项限制系数向量的范数。最常见的三条路是 L2Ridge、L1Lasso和弹性网ElasticNet。这一章给出三条路径的 python 基本算法实现重点不是调用库而是让你看清每个惩罚项到底加在代码的哪个位置、改了哪一行。3.1 L2 正则化岭回归闭式解在正规方程对角线加正则化系数L2 正则化把目标函数改成||Xw - y||^2 lambda * ||w||^2求导后的正规方程变成(X^T X lambda * I) w X^T y。实现时有一个关键修正常数项对应的系数不应该被惩罚。因为在多项式拟合里x^0这一列全是 1它代表的是截距惩罚它会让拟合曲线整体被拉向 0这不是我们想要的。def ridge_fit(X, y, lambd): # lambd 就是正则化系数常写作 lambda但不能和关键字撞名 n_features X.shape[1] # 惩罚矩阵对角线为 1常数项位置置 0保证截距不受惩罚 penalty np.eye(n_features) penalty[0, 0] 0 # Ridge 的闭式解在 XtX 对角线上加 lambd * penalty w np.linalg.solve(X.T X lambd * penalty, X.T y) return w # 对比三组正则化系数 for lambd in [1e-6, 1e-2, 1.0]: w_ridge ridge_fit(X, y, lambd) train_mse np.mean((X w_ridge - y) ** 2) print(flambd{lambd:8.5f}, train_mse{train_mse:.5f}, fcoef_norm{np.linalg.norm(w_ridge[1:]):.3f})逻辑说明对比第 1 章普通最小二乘唯一的区别是linalg.solve的第一个参数从X.T X变成了X.T X lambd * penalty。对角线上加了一个正值等价于给每个特征分量加了一个小的扰动使得XtX从病态矩阵变成良态矩阵条件数显著下降。参数上最重要的就是lambd的取值范围1e-6几乎不影响拟合1.0会把系数范数压缩得很小曲线趋向于一条平滑的低阶曲线。三行输出对比后你会看到训练误差在变大但这是换取泛化能力的必要成本。很多网上代码图省事直接写lambd * np.eye(n_features)没有把penalty[0,0]置为 0。如果你做的是一次普通x值范围在 0 附近的拟合这个差异对结果的直观影响不大但一旦数据均值远离 0截距被惩罚后整个曲线会被明显拉偏这个坑后面专门展开。3.2 梯度下降实现 Ridge把 L2 变成 weight decay走大样本路线闭式解在特征维度几百以内还能跑一旦数据量大到XtX装不进内存或者你正在学习 python 基本算法里的梯度下降就应该切换到迭代求解。L2 正则化的梯度形式是原始梯度加2 * lambd * w这在实际工程里有个更形象的名字weight decay权重衰减。意思是每次更新时系数除了沿负梯度方向移动还会被主动缩小一点。def ridge_gd(X, y, lambd, lr0.01, epochs1000): n_samples, n_features X.shape w np.zeros(n_features) # 构造惩罚矩阵保持与闭式解一致 penalty np.eye(n_features) penalty[0, 0] 0 for t in range(epochs): # 最小二乘梯度2/n * X^T (Xw - y) grad 2.0 / n_samples * X.T (X w - y) # L2 惩罚梯度2 * lambd * (penalty w) grad grad 2.0 * lambd * (penalty w) w w - lr * grad if t % 200 0: loss np.mean((X w - y) ** 2) lambd * np.sum(w[1:] ** 2) print(fepoch {t}, loss{loss:.5f}) return w w_gd ridge_gd(X, y, lambd1e-2, lr0.01, epochs1000)逻辑说明每次迭代都对w做两件事——沿着减小||Xw-y||^2的方向走一步同时按1 - 2*lr*lambd的比例收缩一次。当lambd0时它就是普通的最小二乘梯度下降当lambd增大时大系数会被更快压小。参数说明里最重要的两个是lr和lambdlr太大容易发散lr太小收敛慢epochs设为 1000 只是为了展示实际应该用梯度范数np.linalg.norm(grad)判断收敛小于某个阈值如1e-6就提前退出。初学时容易把梯度下降和正规方程实现混着抄导致代码里同时出现两种路径。建议以闭式解为基准梯度下降只做交叉验证的复核。另外注意这里我用了2.0 / n_samples做归一化有些教程不除n_samples那lr的合理取值会差一个数量级。你一旦遇到“同样的 lambd 换组数据就发散”的问题先检查是不是这个缩放不一致导致的。3.3 L1Lasso与弹性网L1 为什么没有闭式解稀疏性才是它的卖点L1 正则化对应惩罚项lambd * |w|它在 0 点不可导所以没有顺滑的闭式解这也是为什么 Lasso 不用正规方程、而用坐标下降的原因。如果你只是想快速理解可以用次梯度来做迭代更新np.sign(w)就是绝对值函数在非 0 处的导数在 0 点取[-1, 1]之间的任意值numpy里np.sign(0)返回 0恰好落在次梯度集合内能用但不严格工程上更推荐坐标下降。这里给一个次梯度的演示版本方便理解原理def lasso_subgrad(X, y, lambd, lr0.005, epochs800): n_samples, n_features X.shape w np.zeros(n_features) for t in range(epochs): grad_mse 2.0 / n_samples * X.T (X w - y) # 次梯度近似对每个系数加 lambd * sign(w) grad grad_mse lambd * np.sign(w) # 常数项不参与 L1 惩罚把刚才加上的部分减回去 grad[0] - lambd * np.sign(w[0]) w w - lr * grad # 统计被压成 0 的系数个数 n_zero np.sum(np.abs(w[1:]) 1e-6) if t % 200 0: print(fepoch {t}, zero_count{n_zero}) return w w_lasso lasso_subgrad(X, y, lambd1e-2)逻辑说明L1 每次更新的梯度惩罚不是2*lambd*w而是lambd*sign(w)。当系数w[i]绝对值较小、梯度的其他部分又不足以把它拉回来时它会恰好在 0 处停下这是 L2 做不到的L2 只能把系数压到接近 0L1 能让系数变成精确的 0这叫作稀疏性。这个特性在特征选择场景很值钱但在多项式拟合场景里要警惕把某个幂次特征直接删掉会让拟合曲面出现结构性突变。弹性网ElasticNet就是 L1 和 L2 的组合损失函数变成MSE lambda1 * |w| lambda2 * |w|^2。实现上把两个梯度加起来就行具体比例用另一个超参数l1_ratio控制l1_ratio1是纯 Lassol1_ratio0是纯 Ridge。实际项目里如果特征维度不高、只是做平滑拟合Ridge 通常够用如果你想做特征选择再考虑带 L1 的版本。从这套代码出发加弹性网只需要把grad换成grad_mse lambda1*np.sign(w) 2*lambda2*(penaltyw)剩下流程完全一样。4. 正则化系数是核心参数交叉验证与岭迹图帮你选 lambda正则化系数lambd在这个项目里几乎决定成败。设小了过拟合的震荡压不住设大了拟合曲线被拉成一条直线完全丢失真实形状。选lambd不能靠肉眼从一两张图里拍脑袋常见做法是画岭迹图并配合 K 折交叉验证做数值选择。但在动手之前先要理解lambd与其他因素的联动关系否则就算是交叉验证也会选出错误的量级。4.1 数据量、阶数与 lambda 的联动样本少于特征时正规方程会翻车多项式阶数degree决定特征数量x的样本数n决定方程组的约束个数。当n degree 1时XtX满秩普通最小二乘有唯一解当n接近甚至小于degree 1时XtX奇异solve直接报LinAlgError或者算出一个条件数爆炸的解此时必须有正则化。下表是我在 python 里反复测过的典型表现样本数 n阶数 degreeXtX 条件数现象405约 1e4可正常拟合端点轻微震荡4010约 1e8训练误差小端点剧烈震荡40201e12 以上系数极大曲线完全失控2020矩阵奇异正则化系数必须非零条件数超过 1e10 时np.linalg.solve依然不报错但解出来的系数幅度会非常夸张经常看到某个w[i]到了几千。lambda 的加入能直接改善这个数值问题lambd1e-3就能把条件数从 1e8 压到 1e4 量级。这就是为什么说正则化不只是统计上的平滑手段更是数值求解上的“后悔药”——它在方程无解或接近无解时保证你总能算出一个结果。实际工程经验阶数不要直接拉到 15 以上去配合正则化硬扛。让正则化做它该做的让系数收缩让阶数做它该做的决定模型表达力。先从一个拟合形状合适的中等阶数开始比如 8 到 12再用交叉验证把 lambda 选出来。4.2 K 折交叉验证选 lambda一个不偷看测试集的完整流程选正则化系数的标准做法是 K 折交叉验证。把训练数据切成 K 份轮流拿其中 K-1 份训练、1 份验证最后把 K 次验证误差求平均。下面用一个不依赖 sklearn、纯 numpy 的手写版贴合“基本算法”的项目气质def k_fold_cv(X, y, lambd, k5, random_seed0): n len(y) rng np.random.default_rng(random_seed) idx rng.permutation(n) val_errors [] for i in range(k): # 用步长为 k 的切片做验证集简单且保证互不重叠 val_idx idx[i::k] tr_idx np.setdiff1d(idx, val_idx) w ridge_fit(X[tr_idx], y[tr_idx], lambd) err np.mean((X[val_idx] w - y[val_idx]) ** 2) val_errors.append(err) return np.mean(val_errors) # 候选 lambda 从 1e-4 到 1e2对数间隔采样 candidate_lambdas np.logspace(-4, 2, 13) cv_scores [k_fold_cv(X, y, l, k5) for l in candidate_lambdas] best_lambda candidate_lambdas[np.argmin(cv_scores)] print(候选 lambda, candidate_lambdas) print(交叉验证误差, np.round(cv_scores, 4)) print(最优 lambda, best_lambda)逻辑说明idx[i::k]是步长为k的切片效果是把数据分成 K 份每份轮流当验证集。循环里调用上一章的ridge_fit因此这个交叉验证天然支持换成 Lasso 或弹性网。candidate_lambdas用np.logspace在[1e-4, 1e2]区间内取 13 个值比等间距采样更合理lambda 对误差的影响在数量级尺度上变化等间距在小值区会严重漏选。注意不要用训练误差来选 lambda当lambd0时训练误差最小交叉验证有可能选出一个很小的 lambda但它对应的模型在验证集上仍在震荡。真正的检验是 K 折平均验证误差它估计的是“没见过的数据上会表现如何”。我一般会多跑几个random_seed看最优 lambda 是否稳定在同一数量级如果一次是1e-2一次是1说明数据量太少交叉验证结果本身也不可信。4.3 岭迹图观察每个系数随 lambda 收缩的路径除交叉验证外岭迹图是判断 lambda 取值范围最直观的方式。横轴是 lambda纵轴是各幂次特征的系数值每条线是一个特征。lambda 很小时系数起伏大lambda 增大时所有系数向 0 收缩。理想的 lambda 是让曲线刚进入平稳区域的位置此时系数还在合理大小又不再随 lambda 发生剧烈跳变。import matplotlib.pyplot as plt lambdas np.logspace(-4, 2, 25) coef_list np.array([ridge_fit(X, y, l) for l in lambdas]) plt.figure(figsize(8, 5)) # 去掉常数项那一列它的尺度和其他特征差异太大 plt.plot(lambdas, coef_list[:, 1:], linewidth1.2) plt.xscale(log) plt.xlabel(lambda (log scale)) plt.ylabel(coef value) plt.axvline(xbest_lambda, colorgray, linestyle--, labelbest by CV) plt.legend() plt.show()这张图的解读方式很简单看每条线如何从左侧的高波动区域进入右侧的平稳收缩区。如果某条线的系数在lambda1e-4时是 800在lambda1e-2时变成 200在lambda1时变成 5那它进入平稳区的点大概在lambda1e-2到1e-1之间。交叉验证选的best_lambda应该落在这一区间附近如果两者差了三四倍以上通常说明阶数degree选得太高了应该回头降低阶数而不是硬调 lambda。coef_list[:, 1:]去掉常数项是因为x^0的系数截距在数据均值不为 0 时比 1 大很多画在一张图里会把其他系数的变化压到看不见。这也呼应了前面说“常数项不参与惩罚”的细节岭迹图里每条线的收缩路径本质上是在验证哪些特征被压得快、哪些压得慢被压得越慢的特征对拟合贡献越稳定。5. 正则化多项式拟合避坑五类经典翻车现场与排查思路这一章内容是我在实际调试中反复踩过、也在他人代码里见过的坑全部围绕“基于python基本算法实现正则化的多项式拟合”这个主题。每条都按现象、原因、解决的顺序写可以直接对应你的报错信息或拟合图异常。5.1 特征没做缩放梯度下降收敛慢得不正常现象用 3.2 节的ridge_gd拟合epochs跑到 5000 次损失依然在缓慢下降打印出的系数与闭式解相差甚远用lr0.1则直接发散损失变成nan。原因多项式特征矩阵X的各列量级差异太大。x在[-1, 1]区间时x^1量级是 1x^10量级也只有 1看起来没问题但换到x在[0, 100]区间时x^10的量级是 1e20梯度方向被极少数高次项主导普通梯度下降很容易出现震荡或者收敛极慢。正则化系数lambd也是按整个特征矩阵统一施加的量级大的列会被罚得更狠缩放不统一时正则化本身的含义也被扭曲了。解决先做特征缩放常见做法是把每一列标准化到均值 0、方差 1即X_scaled (X - X.mean(axis0)) / X.std(axis0)。注意此时求出的系数是对缩放后特征的预测时要先对新样本做同样的缩放变换。另一个路径是换用正规方程闭式解它对量级不敏感所以在做小规模拟合时我通常直接用闭式解绕开这个问题。5.2 正则化加错了位置常数项也被惩罚了现象代码写np.linalg.solve(X.T X lambd * np.eye(X.shape[1]), X.T y)拟合结果在x接近 0 的地方偏离严重截距被明显拉向 0。原因np.eye(X.shape[1])的对角线全是 1包括第 0 个对角线元素也就是常数项w[0]也被放进了惩罚项。常数项代表的是拟合曲线的整体高度对它做 L2 惩罚等于强制曲线向 0 收敛而不是向均值收敛。在数据均值明显偏离 0 时这种偏差就会直接显现。解决用一个惩罚矩阵把常数项位置置 0和 3.1 节的代码保持一致penalty np.eye(m); penalty[0, 0] 0。如果你用 sklearn 的Ridge它的fit_interceptTrue参数默认不罚截距但自己实现时这个细节非常容易漏。调试时判断方法打印ridge_fit返回的w[0]对比普通最小二乘的w[0]如果前者比后者小得多大概率就是截距被惩罚了。5.3 阶数设太高正则化根本压不住震荡现象degree20、lambda0.1时训练误差依然很小但拟合曲线在端点处还是抖成了高次波浪线无论怎么调大 lambda要么压不住震荡要么曲线彻底扁平。原因多项式阶数越高高次项基函数的形状越陡峭模型在端点处的“表达欲”越强。正则化能限制单个系数的大小但多项式的取值是所有系数加权求和的结果当阶数高到一定程度即使单个系数已经很小多个系数在端点处同号叠加依然能制造出大幅度震荡。lambda 再大整体模型形状也会被拉平无法在“保形状”和“压震荡”之间找到折中。解决先降阶数再调 lambda。把degree从 20 降到 10 或 12你会发现最优 lambda 变了而且曲线稳定得多。正则化不是用来容错超高阶模型的它只能在你选对模型复杂度之后做细微修正。这也是为什么前面要强调项目里的核心超参数是degree和lambd两个而不是只调一个。5.4 只用训练误差选 lambda正则化系数偏小现象按 4.2 节的候选 lambda 列表计算训练集 MSE发现lambd1e-4时误差最小于是选了这个小值结果在验证集上一测误差反而比lambd1e-2时大一倍。原因训练误差永远随 lambda 减小而减小因为模型更贴近训练数据。你在根据过拟合的判断标准选参数时这是典型的“幸存者偏差”选中的不是泛化能力强的模型而是记忆训练集最强的模型。多项式拟合的过拟合尤其隐蔽中间部分看起来贴合端点却在剧烈震荡而训练误差指标不一定能反映这一点。解决选 lambda 只看交叉验证误差或独立的验证集误差。习惯做法是先划分出一个完全没参与拟合的测试集交叉验证在训练集内部选 lambda最后才用测试集做一次最终评估。如果数据集太小没法再分测试集那就用 4.3 节的岭迹图看系数是否已进入平稳区不要只盯 MSE 数字。5.5 L1 把系数压成 0 后直接删特征破坏了模型结构现象用 Lasso 拟合后发现w[3]和w[5]都是 0于是把对应的x^3、x^5两列从特征矩阵中删除重新做了二次拟合。结果拟合曲线变得非常奇怪在某个区间突然出现一个大的弯曲误差显著上升。原因L1 正则化让部分系数变为 0这并不代表该幂次项对拟合毫无贡献。多项式特征之间存在高度相关性x^3和x^5的系数为 0是因为它们在和别的项互相抵消后对拟合净贡献为 0。直接删列等于破坏了多项式的完整基函数体系后续预测的数值稳定性会变差。解决在多项式拟合场景下不要把 L1 稀疏性当作特征选择的依据。L1 在正交特征下做特征选择才有意义而多项式各幂次之间天然不正交。如果你确实想做特征选择应该换成正交多项式基或者先对特征做 PCA 再进入 Lasso。否则老老实实用 Ridge保留完整的多项式结构让系数整体收缩就好。6. 一个值得养成的验证习惯先画岭迹图再定正则化系数最后分享一个我现在每做一次多项式拟合都会走的验证流程它帮我在大量项目里避免了“选了个 lambda 但不知道为什么选它”的黑匣子状态。拿到一组数据后我的固定顺序是先快速画一遍岭迹图看系数在哪个 lambda 区间进入稳定收缩再跑一轮 K 折交叉验证把数值结果往图上叠。如果交叉验证挑出的最优 lambda 与岭迹图观察到的稳定区间明显矛盾我不会急着改 lambda而是回头检查阶数、特征缩放和惩罚矩阵。def quick_ridge_trace(X, y, lams): coefs np.array([ridge_fit(X, y, l) for l in lams]) plt.figure(figsize(8, 5)) # 不画常数项只看各幂次系数收缩路径 plt.plot(lams, coefs[:, 1:], linewidth1.2) plt.xscale(log) plt.xlabel(lambda (log scale)) plt.ylabel(coef value) plt.title(Ridge Trace) plt.show() lams np.logspace(-4, 2, 20) quick_ridge_trace(X, y, lams)这个流程真正解决的问题是交叉验证给你的是一个孤立的最优点它不告诉你这个点周围是否陡峭。lambda 从 1e-3 变成 1e-2验证误差变化不到 1%但系数却从 50 变成了 8这种情况在岭迹图上一目了然。我在一个销量预测项目里就遇到过交叉验证把最优 lambda 选在了1e-1但岭迹图显示这个区间所有系数都已经被压到接近 0模型等价于一条直线只是验证误差恰好相近。我按岭迹图把 lambda 调回1e-3线上预测稳定性好了很多。另外建议把岭迹图和数据集划分结合起来做随机换两次random_seed看曲线簇是否稳定。如果两次生成的岭迹图在某个 lambda 之后完全重合说明模型在这个区间是稳定的可以放心部署如果曲线一直发散说明数据量不足或阶数太高任何选出来的 lambda 都只是一次性的“幸存者”。我自己的习惯是只要画出这张图至少能排除一半以上的拟合翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表