训练模型第四章精读:线性回归、梯度下降与正则化实战
这本书看到第四章很多读者会明显感觉到一个坎前面几章还在照着眼熟的API调包、训练、预测到了“训练模型”这一章突然开始讲偏导数、梯度、正规方程、学习曲线了。我在带项目的时候也见过不少同事跑通了第二三章的代码到第四章就翻得飞快觉得“数学太多、跟实践没关系”。这个判断其实是把整本书最值钱的部分跳过了。第四章看似在讲理论实际上是帮你把“模型到底是怎么学出来的”这件事彻底搞清楚。后面你用TensorFlow搭神经网络调学习率、加正则、看收敛曲线本质上都是这一章的思路在更高维度上的重演。这一章适合谁来读算法工程师、数据工程师甚至主要是调API的初学者都值得慢读。你不需要顶尖的数学底子会求导、看得懂矩阵乘法就够了。我尽量用偏实操的方式把这一章拆开结合我用Scikit-Learn做过的几个模拟项目讲讲哪些内容必须吃透哪些坑是书上没明说但实际天天踩的。1. 这一章到底在讲什么1.1 核心脉络从“调包”走到“调模型”第四章的标题叫“训练模型”但它的核心不是教你敲fit函数而是拆解fit背后发生了什么。全章主要围绕三块展开线性模型的解析求解、梯度下降的迭代求解、以及防止模型学歪的正则化手段。这三块是环环相扣的。我自己的理解是可以把这一章压缩成一个问题给定一个损失函数怎么找到让损失最小的一组参数教科书给的答案是求导置零但实际数据里特征一多、样本量一大解析解根本算不动所以必须用迭代的方式一步步逼近最优解。梯度下降就是这套迭代方法的代表而后面所有深度学习框架的优化器都是它的变体。1.2 别只盯着公式要盯住三个实验现象我看过不少人复习这一章把公式抄了一遍又一遍但真到调参时还是瞎试。原因在于这一章最关键的其实是几个实验现象第一学习率太大损失曲线会震荡甚至发散第二特征不缩放梯度下降会走得很歪第三多项式阶数一高训练误差降了验证误差反而涨。这三个现象比公式本身更重要。它们对应的是你在真实项目里最常遇到的问题不收敛、收敛慢、过拟合。所以我在带新人时会让他们先把书里的代码跑一遍再故意改坏几个参数看看曲线怎么变。这个“故意改坏”的过程比抄十遍公式都有用。2. 线性回归最容易被低估的模型2.1 正规方程与伪逆背后的现实差距第四章开篇讲的线性回归很多读者会觉得太简单不值得花时间。但实际上线性回归是整个监督学习的基石。书里给出了正规方程θ (X^T X)^(-1) X^T y这个公式看起来优雅但我在实际数据上几乎不会直接这样算。原因有两个一是当特征数量上万甚至上百万时(X^T X) 这个矩阵的求逆计算量是灾难性的内存和时间都撑不住二是当特征之间存在高度相关性时X^T X 可能是奇异矩阵或接近奇异求逆结果极不稳定。书里紧接着提到了伪逆也就是 SVD 分解的解法。这个我有一次踩过坑之后才真正重视起来。当时我做一个特征数量远大于样本数量的模拟数据集用正规方程直接报错换上 pinv 或者直接调 sklearn 的 LinearRegression它底层走的是 SVD才顺利解出来。所以大家第一次看这段可能觉得“怎么又讲矩阵”但到了真实项目里这正是帮你避开数值崩溃的关键。2.2 用Scikit-Learn实现时要留意的细节书里的代码很简单几行就能跑通from sklearn.linear_model import LinearRegression import numpy as np X np.random.rand(200, 3) y 2 * X[:, 0] - 1.5 * X[:, 1] 0.5 * X[:, 2] np.random.randn(200) * 0.1 model LinearRegression() model.fit(X, y) print(model.coef_) print(model.intercept_)这个代码本身没难度但我想提醒三个细节。第一LinearRegression 默认会计算 intercept_如果你已经手动给数据加了偏置列必须把 fit_intercept 设为 False否则参数会重复。第二sklearn 的 LinearRegression 在特征数量很大时实际使用的是最小二乘的 SVD 解法速度并不快所以不要迷信它。第三它没有内置的正则化项如果特征共线性严重结果会方差很大这时就该转到下一节要讲的岭回归。2.3 从线性回归延伸到神经网络为什么第四章明明在讲传统机器学习却放在一本同时覆盖TensorFlow的书里因为线性回归的损失函数、梯度、学习率这些概念和神经网络的训练机制完全一致。你可以把一个最简单的神经网络想象成多个线性回归堆叠起来再在中间插入非线性激活函数。正向传播计算损失反向传播计算梯度更新权重还是用梯度下降。所以我在给团队做分享时经常说如果你把第四章的线性回归和梯度下降弄透了后面看到反向传播、学习率调度、优化器都不会觉得是全新的东西只是换了个更复杂的壳。3. 梯度下降三种形态和调参3.1 批量、随机、小批量的差异不能只靠背第四章把梯度下降分成了批量梯度下降、随机梯度下降、小批量梯度下降三种。书上讲得比较清楚但我见过很多读者背了定义却不会选型。批量梯度下降每一步都拿全部样本算梯度方向最准但数据一大就慢得离谱。随机梯度下降每步只拿一个样本算快是快但梯度方向噪声极大损失曲线会抖得厉害。小批量梯度下降取两者折中一次拿一小批是深度学习里最常用的方案。我在模拟项目里测试过这样一个场景一个中等规模的数据集五万样本、几十个特征。用批量梯度下降每一轮迭代要算五万次预测时间长到可以起身泡杯茶用随机梯度下降收敛倒是快但最终精度会差一些改成小批量每次取256条速度和精度都能兼顾。这个对比做完你就不会再纠结为什么真实项目里没人用纯批量了。3.2 学习率不是越准越好要的是能收敛学习率是第四章里最重要的超参数没有之一。书上给出了学习率过小、合适、过大三种情况下的损失曲线。但我实际看到的现象更复杂学习率稍微偏大时损失曲线不是立刻发散而是先下降再突然弹上去很多人以为是自己数据的问题其实只是学习率超过了临界值。我的经验是先用对数尺度试一轮学习率比如从 0.1 往下尝试0.1、0.01、0.001、0.0001。找到一个能稳定下降的量级再在这个量级附近细调。Sklearn 的 SGDRegressor 可以直接配学习率用起来很方便from sklearn.linear_model import SGDRegressor from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline model make_pipeline( StandardScaler(), SGDRegressor(max_iter1000, tol1e-3, penaltyNone, eta00.01) ) model.fit(X_train, y_train)这里有个细节要强调SGDRegressor 对特征的尺度非常敏感我强烈建议先做标准化。书里其实也提了但很多人会觉得“好像提过不重要”。我可以负责任地说不做标准化直接跑 SGD小数据集上可能会完全无法收敛或者收敛速度慢十倍。3.3 迭代曲线才是真正的调试入口第四章让人收获最大的习惯是画学习曲线。不管是看训练损失还是验证损失都一定要把曲线画出来而不是只看最终的分数。我一开始也嫌麻烦后来被一个莫名其妙的模型坑了一次。当时做一个回归模型训练集分数很高验证集分数却不断波动看上去像过拟合。后来我把损失曲线画出来发现其实是学习率太大导致模型参数在最优解附近来回震荡并没有真正收敛。把学习率降下来之后验证分数立刻稳定了。这就是曲线比单一指标更有价值的原因。4. 多项式回归、学习曲线与偏差-方差4.1 多项式特征是把双刃剑第四章讲完线性回归紧接着就引入了多项式回归做法是先对原始特征做多项式扩展再套线性模型。Sklearn 提供了 PolynomialFeatures用法很简单from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.pipeline import make_pipeline poly_model make_pipeline( PolynomialFeatures(degree3), LinearRegression() ) poly_model.fit(X_train, y_train)书里用了一个带噪声的二次函数数据来演示阶数为1时欠拟合阶数为2时恰到好处阶数越高越扭曲。这个演示我建议亲手跑一遍因为“过拟合长什么样”这件事看文字描述远不如看图上那条疯狂弯曲的曲线来得震撼。4.2 学习曲线帮我判断模型状态学习曲线是第四章里另一个极其实用的工具它画的是训练集和验证集的误差随训练样本数量变化的曲线。我在实际项目里判断一个模型是欠拟合还是过拟合基本靠它。如果两条曲线最终都偏高而且靠得很近说明模型容量不够属于欠拟合这时加多项式特征、换更强的模型才有用。如果训练误差很低但验证误差高并且随着样本增加两者差距逐渐缩小说明过拟合这时加正则、加数据、做特征选择才有意义。很多人拿到一个模型上来就加正则结果本来欠拟合加了以后反而更差这就是没有先看学习曲线的后果。4.3 偏差与方差的权衡要在项目里反复体会第四章把偏差和方差讲得很透。我自己的理解是偏差是模型对规律本身的拟合能力不够方差是模型对数据中的随机噪声过度敏感。这两个东西是此消彼长的正则化本质就是拿一点偏差去换更大的方差下降。我做过一个电商用户复购预测的模拟项目一开始用线性模型偏差大得明显后来换了带多项式特征的模型方差又起来了最后用岭回归把高次项系数压下去才在验证集上拿到稳定提升。这个过程走一遍你对偏差方差的理解会比读十遍定义都深。5. 正则化给模型的自由套上缰绳5.1 岭回归、Lasso、ElasticNet 的选用逻辑第四章讲了三种经典正则化岭回归、Lasso、ElasticNet外加早停法。很多初学者会问这三个到底有什么区别平时用哪个岭回归加的是L2范数它会把系数整体往零的方向压缩但很少变成严格零。Lasso加的是L1范数它会把不重要的系数直接压成零天然能做特征选择。ElasticNet是两者混合既保留特征选择的稀疏性又保留L2的稳定性。书上建议当特征数量大于样本数量或者特征之间存在强相关时优先考虑ElasticNet我是认同的。用 sklearn 实现时三个模型的接口几乎一模一样from sklearn.linear_model import Ridge, Lasso, ElasticNet ridge Ridge(alpha1.0) lasso Lasso(alpha0.1) elastic ElasticNet(alpha0.1, l1_ratio0.7)5.2 alpha 的选择不能拍脑袋正则化强度 alpha 是一个必须调的超参数。书上介绍了使用交叉验证来选择 alphasklearn 提供了 RidgeCV、LassoCV、ElasticNetCV可以自动搜索。我用的时候会发现一个问题直接跑 CV 是比较慢的尤其样本量一大组合一多时间翻倍。所以我通常先在一个粗略的对数网格上试一遍锁定量级后再细搜。另外有个容易忽略的细节正则化对特征尺度同样敏感。因为惩罚项大小直接由系数决定而不同量纲的特征对应的系数尺度差异很大。所以用正则化模型时我会把特征标准化放进 pipeline这一步能显著提高 alpha 搜索的稳定性。5.3 早停深度学习里最常用的正则化第四章末尾提了早停法我用下来觉得它的实用程度被严重低估了。早停的思路很简单每训练一轮在验证集上测一次误差一旦验证误差开始持续上升就停止训练并回滚到验证误差最低时的参数。这个方法在深度学习里几乎是标配因为神经网络容量大训练集误差能压到非常低没有早停基本必然过拟合。而在 sklearn 的一些模型里比如 SGDRegressor 配合 validation 参数其实也能做类似的事。书里用代码演示了在迭代过程中提前停止这个思路我认为值得认真掌握它比你想办法调出一个完美 alpha 要省事得多。6. 逻辑回归与多分类6.1 逻辑回归其实也是回归第四章后半部分讲逻辑回归这里有个特别容易混淆的点逻辑回归虽然名字里有“回归”但它解决的是分类问题。它的思路是让线性模型的输出经过一个 Sigmoid 函数映射成0到1之间的概率然后基于概率做分类。用 sklearn 实现非常顺手from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline model make_pipeline( StandardScaler(), LogisticRegression(C1.0, max_iter1000) ) model.fit(X_train, y_train)注意这里的 C 是正则化强度的倒数C 越大正则越弱C 越小正则越强。很多人第一次用会把 C 当成 alpha 来理解白白踩坑。6.2 Softmax 回归实现多分类当分类目标超过两个类别时书里介绍了 Softmax 回归也就是多元逻辑回归。它把每个类别对应一个线性得分再用 Softmax 函数把得分转成各类别的概率分布最终取概率最大的类别作为预测结果。Sklearn 的 LogisticRegression 默认不支持多分类但你可以把 multi_class 设为 multinomial并使用 saga 或 lbfgs 求解器。不过实际经验是直接默认参数在多分类问题上通常也可以跑sklearn 内部会自动处理。书里也提醒使用 Softmax 回归时要保证特征缩放一致这一点我同样建议放在 pipeline 里做。我做过一个手写数字识别的模拟项目把像素拉平当作特征输入逻辑回归结果当然不如卷积神经网络但当时看到多个类别的概率输出时我意识到 Softmax 在解释模型预测置信度上非常有用。哪怕后来切到深度学习很多分类网络最后一层也还是 Softmax。6.3 分类模型的评价不能只看准确率第四章虽然没有花大篇幅讲评价指标但当你真的开始用逻辑回归做分类时必须知道只看准确率会误导。比如一个二分类任务里90%的样本是负类你把全部样本预测成负类准确率也有90%看起来很高但它什么也没学到。我一般在项目里同时看混淆矩阵、精确率、召回率、F1。对于类别不平衡的数据更关注召回率和F1而不是准确率。这本书前半部分对这些指标讲得相对简略但第四章既然把逻辑回归当分类主力在讲你就不能省略这一步否则模型上线后会在少数类上翻车。7. 常见问题与排查实录7.1 特征缩放被忽略导致梯度下降不收敛这是我在实际项目中遇到最多的一个问题。某个模拟数据集的特征一个范围在0到1另一个范围在几千到几万。直接丢给 SGDRegressor损失曲线要么不下降要么剧烈震荡。原因在于没有缩放的梯度在各方向上的尺度差异太大参数更新会走出类似“Z”字形路线。处理方式很简单用 StandardScaler 或 MinMaxScaler 先做缩放再送入模型。我会在 sklearn 里用 make_pipeline 把缩放器和模型绑在一起这样预测新数据时就会自动做同样的变换不会漏掉。7.2 把验证集信息泄漏进训练过程这一条严格说不算第四章的内容但我在用学习曲线调参时踩过坑所以必须提一下。一开始为了“看效果”我直接用完整数据集来画学习曲线然后选了看起来最好的多项式阶数。这相当于用验证集信息去选了模型超参数最终验证分数失去了参考价值。正确的做法是先划分出训练集和验证集只用训练集去交叉验证选超参数最后再在验证集上做一次最终评估。我在被这个坑折磨过一次之后给自己定了一个习惯任何超参数选择都写进一个独立流程绝不在完整数据集上做决策。7.3 把归一化和标准化搞混很多教程里把归一化和标准化混着说第四章里强调的是特征缩放但具体用哪种要看模型。树模型不敏感缩放不缩放影响不大线性模型和基于梯度的模型则强烈依赖。归一化把数据压到0到1之间标准化让数据均值为0、方差为1。如果特征分布近似正态标准化效果更好如果特征本身有界归一化更直观。我自己平时默认先用 StandardScaler因为它在大多数线性模型上表现稳定。只有在数据有明显边界时比如图像像素值才会考虑归一化到0到1。7.4 模型不收敛时先查学习率还是先查数据有人一看到损失不下降就疯狂调学习率结果越调越乱。我建议按照固定顺序排查第一步看数据有没有经过预处理比如缩放、缺失值、标准化第二步看损失曲线是发散还是停滞发散多半是学习率太大停滞可能是学习率太小或特征有问题第三步看训练集和验证集的分数差判断是不是过拟合。按这个顺序排查绝大多数训练问题都能定位。我见过一个案例某团队一直抱怨梯度下降不收敛最后发现是数据里有两个缺失值没有处理NaN 沿着计算图一路传播损失直接变成乱码。所以遇到一切“诡异不收敛”先检查数据里是不是有非有限数值。7.5 正则化系数不是越大越好这一点特别值得放到最后说。很多人觉得加了正则就安全于是一股脑把 alpha 调得很大。结果模型权重被压得过于平滑训练误差和验证误差同步上升反而欠拟合。我遇到过一位同事把岭回归的 alpha 调到上千然后对着下降的分数百思不解。正确的做法是画一条“验证误差随 alpha 变化”的曲线找到那个验证误差最低的区间。alpha 从小到大模型会从过拟合走向欠拟合中间常常有一段相对平稳的区间选这个区间里的值最稳。你也可以直接用 RidgeCV 这类带交叉验证的工具但手动画一遍曲线你对模型的理解会深很多。实操总结这一章真正教会我的事第四章的内容在整本书里位置特殊它不像后面章节那样直接教你怎么用 TensorFlow 搭网络但它给了你理解所有训练的底层视角。我后来在调神经网络时回想最多的就是这一章。学习率、小批量大小、正则化、早停这些概念我第一次认真掌握都是在这一章。按我个人的实操体会学习这一章最有效的方式不是把公式抄满笔记本而是把书上的代码改成自己的小实验换一个数据集、故意调坏一个参数、画一条损失曲线、观察误差变化。把这些实验做过一遍之后再回到公式上你会发现每个符号都有了实际意义。如果你正卡在这一章别急着跳过去。把它拆成线性回归、梯度下降、正则化、逻辑回归四块每块配合代码跑一遍花两到三天慢慢消化后面读神经网络章节时你会明显感觉轻松很多。