
每年到期末季群里总有人问线性回归的推导题怎么证梯度下降的学习率到底调多少头歌平台那几道题为什么一直跑不过问这些问题的同学有的在西电、有的在山大也有自学吴恩达或李宏毅课程的——教材版本五花八门但翻到机器学习相关的课本第二章几乎都安排给线性回归。说实话这个章节看着人畜无害却是整个机器学习体系里最容易被低估的一块基石后面所有的回归模型、分类模型的半壁江山、神经网络的线性层底子都在这里。这篇文章我打算按自己复习和带新人的经验把线性回归从头到尾捋一遍它到底在解决什么问题、损失函数和两种求解方式正规方程、梯度下降背后的逻辑、Python 手写实现和 sklearn 调用、以及期末题和平台作业里那些防不胜防的坑。适合正在上机器学习课、被第二章折磨的学生也适合刚入门想搞清楚回归到底在干嘛的自学者。公式我会尽量讲成人话推导步骤写到能直接抄作业的程度。1. 先说清楚线性回归到底在解决什么问题1.1 从预测一个数说起机器学习入门第一个案例基本都长一个样给出一套房子的面积、卧室数量、地理位置等特征让你预测房价。注意这里的输出不是猫还是狗这种类别而是一个连续的具体数值。这种任务统称回归问题线性回归就是回归问题里最朴素、最直观的模型。很多新手容易把回归和画一条曲线混在一起其实线性回归做的事情一句话就能说清用一条直线多元情况下是一个超平面去逼近样本点让预测值和真实值之间的差距尽可能小。这里有个特别容易误解的点所谓线性指的是参数 w 之间的关系是线性的而不是说特征 x 必须是一次方。你完全可以构造 x1²、x1x2 这样的多项式特征只要模型形式还是 y w1x1 w2x2 ... b它就依然是线性回归。这一点期末概念题特别喜欢考我后面还会展开。1.2 模型形式、增广技巧与数学假设标准写法是h(x) w1x1 w2x2 ... wdxd b其中 w 是权重向量b 是偏置。为了推导方便几乎所有的教材都会用增广技巧把 b 吸收进 w同时在特征向量最前面补一个常数 1。于是模型可以简写成h(x) w^T x这里 x [1, x1, x2, ..., xd]对应 w [b, w1, w2, ..., wd]。写成整个训练集的矩阵形式就是h X wX 是 m 行 (d1) 列的设计矩阵每行是一个样本第一列全是 1。这个矩阵形式是所有后续推导的根基代码里也全是这套乘法建议第一遍就把它刻在脑子里。除了形式教材还会列出几条假设特征与输出之间存在线性关系、误差项独立同分布、误差的方差恒定同方差性、误差服从正态分布。前两条是建模前提后两条主要用于解释为什么最小二乘和极大似然能扯上关系。期末概念题最爱在这里设陷阱比如同方差性被破坏会对参数估计产生什么影响——答案是参数估计依然无偏但方差估计会不准置信区间失效。这种细节点要多留个心。1.3 为什么几乎所有教材都把它放在第二章周志华的《机器学习》、吴恩达的课程、李宏毅的课程第一章讲完基本概念第二章无一例外都是线性回归。原因很简单它是理解机器学习全流程的最小完整案例。一个完整的机器学习项目包含数据准备、模型选择、定义损失函数、优化求解、评估调参五个环节。线性回归把这条路完整走了一遍而且每一步都有干净的数学表达损失函数是凸函数优化既有闭式解也有迭代解评估指标清晰直观。学懂这一章后面再看逻辑回归、SVM、神经网络会发现全是老熟人。神经网络里一个全连接层本质上就是线性变换 非线性激活线性回归去掉激活函数剩下的正是那个线性变换。所以很多人说线性回归学不透后面全是空中楼阁这句话不过分。2. 核心推导损失函数、最小二乘与梯度下降2.1 为什么损失函数偏偏选 MSE单个样本的误差是预测值减真实值那整体损失用什么函数默认答案是均方误差MSEJ(w) (1/2m) * Σ (h(x_i) - y_i)²注意有的教材写成 1/m有的写成 1/2m多出来的 1/2 纯粹是为了求导时抵消掉系数 2让梯度表达式干净一点。考试时务必看清题目用的是哪个定义否则梯度公式会差一个倍数。选 MSE 不是因为它看着顺眼背后有四层理由。几何上看它是预测值与真实值欧氏距离的平方非常直观。数学性质上它关于 w 是凸函数意味着局部最优就是全局最优梯度下降不会陷入局部极小。统计层面如果假设误差 ε 服从均值为 0、方差为 σ² 的正态分布那么极大化似然函数完全等价于最小化 MSE——这个结论是期末推导题的高频考点。最后是计算层面MSE 处处可导梯度表达式简洁到可以直接写成矩阵乘法。作为对照如果选绝对值误差 MAE在零点不可导梯度下降需要引入次梯度选别的非凸损失优化难度直线上升。所以 MSE 看似大家都在用其实是多个因素权衡后的必然结果。2.2 正规方程解析解怎么来、什么时候失效对 J(w) 关于 w 求偏导并令它等于 0可以直接解出最优参数。先把 (Xw - y)^T(Xw - y) 展开成四项再用矩阵求导法则得到∂J/∂w (1/m) * X^T (Xw - y)令它等于零就得到正规方程w (X^T X)^(-1) X^T y这个公式强烈建议自己完整推一遍而不是背下来。一个特别好的几何理解方式是Xw 是设计矩阵 X 的列向量的线性组合它必然落在 X 的列空间里最小二乘解的本质是在列空间里找一个离 y 最近的向量而这个最近向量的残差 y - Xw 必须与 X 的每一列正交即 X^T(y - Xw) 0。把等式解出来正是正规方程。这个视角比死记硬背管用得多考试时还能当作证明题的口头答法。正规方程有两个要命的边界条件。一是 X^T X 必须可逆当样本量 m 小于特征数 d 时矩阵奇异解不唯一二是特征之间存在强多重共线性时X^T X 接近奇异求逆数值极不稳定w 的分量会变得巨大且互相抵消。另外复杂度上求逆是 O(d³)特征维度一上万正规方程就力不从心了——这才需要梯度下降出场。2.3 梯度下降与三种更新策略梯度下降的思想一句话概括沿着损失函数下降最快的方向负梯度方向迈步不断迭代直到收敛。更新公式是w : w - α * (1/m) * X^T (Xw - y)其中 α 是学习率。这里有个理解关键梯度向量指向的是损失函数增长最快的方向所以我们要往反方向走走的步子大小由学习率控制。把梯度的表达式代进去每一步只需要做两次矩阵乘法非常高效。按每次迭代用多少数据梯度下降分三种。批量梯度下降每次用全部 m 个样本计算一个平均梯度稳定但慢适合小数据集。随机梯度下降SGD每次只抽一个样本更新速度快但震荡剧烈往往需要配合学习率衰减才能收敛。小批量梯度下降mini-batch每次用一个 32 或 64 的小批兼顾了稳定的梯度和可接受的计算量这也是现在深度学习训练事实上的标准。期末问概念记住三者的区别和适用场景就够如果考改代码你只需要把批量版的矩阵运算改成一个 for 循环遍历样本就能得到 SGD。收敛的判据通常有两个迭代轮数达到上限或者梯度范数小于某个阈值 ε比如 1e-5。实际操作中更多人选择画损失曲线看它是否趋于平坦。2.4 学习率与特征缩放两个最容易翻车的点学习率是调参第一关。α 太大参数会在最优值附近反复横跳甚至发散损失曲线越跳越高α 太小收敛慢到让人怀疑人生。实践里的做法是先试 0.1、0.01、0.001 三个量级观察损失曲线每轮下降的幅度再按 3 倍左右为步长做微调。吴恩达课程里有个经典技巧画出损失随迭代次数的曲线如果曲线先降后反弹多半是学习率过大如果曲线一直缓慢下降但在同一水平徘徊可能是学习率过小。特征缩放的必要性可以这样理解假如 x1 在 0~1 之间x2 在 0~100000 之间那么最优的 w1 和 w2 尺度会差好几个数量级损失函数的等高线被拉成极扁的椭圆梯度方向几乎垂直于椭圆长轴走起来像在迷宫里绕路。标准化减均值除标准差或归一化缩放到 0~1之后等高线接近圆形梯度下降收敛速度会有数量级的提升。注意特征缩放用的均值和标准差必须从训练集上计算然后原样应用到测试集。用全量数据算会引入数据泄露。这个问题在平台作业里非常隐蔽但期末设计题里最容易被老师挑出来扣分。3. Python 手写实现与关键实操细节3.1 用 numpy 手写正规方程学习阶段强烈建议用 numpy 手写一遍正规方程代码二三十行但是能让你彻底理解矩阵形式的来龙去脉。以下我生成一份带噪声的模拟数据来演示。import numpy as np # 生成模拟数据y 2*x1 - 3*x2 5 噪声 np.random.seed(42) m 100 X np.random.randn(m, 2) true_w np.array([2.0, -3.0]) b 5.0 y X true_w b 0.2 * np.random.randn(m) # 增广第一列补 1对应偏置项 X_aug np.hstack([np.ones((m, 1)), X]) # 正规方程求解 w np.linalg.inv(X_aug.T X_aug) X_aug.T y print(b , w[0], w1 , w[1], w2 , w[2])输出应该非常接近 b5、w12、w2-3。注意我的数据是人为加了噪声的所以估计值和真实值之间存在微小偏差这是正常的——样本有限时估计本身就带有随机性不代表代码写错了。工程上其实没人会直接用 np.linalg.inv因为它数值稳定性差。更稳的做法是用 np.linalg.lstsq 或者 np.linalg.solve。前者底层走 SVD 分解对奇异矩阵也能给出最小二乘意义下的解后者用 LU 分解精度更高但要求矩阵可逆。这几个函数的选择可以作为面试时的加分细节考完试多了解一层不吃亏。3.2 手写梯度下降顺便学会验证收敛手写梯度下降的套路同样固定。先标准化特征再迭代更新参数顺便把每一轮的损失记录下来。def gradient_descent(X, y, lr0.1, epochs200): m, d X.shape w np.zeros(d) loss_history [] for _ in range(epochs): grad (1 / m) * X.T (X w - y) w - lr * grad loss (1 / (2 * m)) * np.sum((X w - y) ** 2) loss_history.append(loss) return w, loss_history # 特征标准化 mu X.mean(axis0) sigma X.std(axis0) X_s (X - mu) / sigma X_s_aug np.hstack([np.ones((m, 1)), X_s]) w_gd, losses gradient_descent(X_s_aug, y, lr0.1, epochs200)一个实用原则先跑一遍看损失曲线确认单调下降再谈调参。如果损失曲线前几轮就冲到天文数字先把学习率降低 10 倍再跑。另外标准化数据训练出来的 w 是标准化空间的权重要还原成原始特征尺度下的权重需要做变换w_orig w[1:] / sigmab_orig b - sum(w[1:] * mu / sigma)。这个变换考试一般不要求但做真实项目时迟早会遇到值得记一笔。还有一个我自己踩过坑后养成的习惯手写梯度下降时用数值梯度去验证解析梯度的正确性。对每个参数 w_j计算 (J(w εe_j) - J(w - εe_j)) / 2ε再和解析梯度对比误差在 1e-4 量级以内就说明推导没问题。这个习惯在之后写神经网络反向传播时能救你无数次。3.3 sklearn 三行代码以及数据划分的规矩工程实战直接上 sklearn 就好代码极简from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) scaler StandardScaler() X_train_s scaler.fit_transform(X_train) X_test_s scaler.transform(X_test) # 只 transform不重新 fit model LinearRegression() model.fit(X_train_s, y_train) print(model.intercept_, model.coef_)这里有三个细节必须提醒。第一sklearn 的 LinearRegression 默认就带偏置不需要像手写那样手动加一列 1。第二测试集上必须使用训练集 fit 好的 scaler 做 transform不能重新 fit否则就是数据泄露。第三LinearRegression 底层不是用正规方程而是基于 SVD 的最小二乘求解所以哪怕数据存在共线性它也能给出一个数值解只是参数的可解释性已经不可靠了。很多同学在作业里习惯先把整个 X 做标准化再切分训练测试集这个顺序是错的。正确的流程一定是先切分再在训练集上 fit 缩放器最后应用到测试集。多写一行代码的事但多少人在这里被扣分。3.4 评估指标怎么选、R² 的几个坑模型训练完必须回答效果到底好不好。最常用的指标是这四个MSE均方误差量纲是 y 的平方容易被大误差项主导。RMSE对 MSE 开根号量纲回到 y 本身汇报时更好解释。MAE平均绝对误差对异常值更稳健但零点不可导。R²决定系数1 - SS_res / SS_tot最大不超过 1越接近 1 越好但可以是负数。指标公式特点适用场景MSE(1/m)Σ(y_i - ŷ_i)²放大较大误差处处可导作为损失函数、理论推导RMSEsqrt(MSE)量纲与原变量一致向业务方汇报预测误差MAE(1/m)Σy_i - ŷ_iR²1 - SS_res/SS_tot无量纲便于跨模型比较回归拟合优度汇报R² 一个最容易踩的坑是R² 为负。很多人以为决定系数一定在 0 到 1 之间实际上当模型比直接预测均值还差时SS_res 大于 SS_totR² 就是负数。这在测试集与训练集分布差异大、或者模型严重过拟合的时候经常出现期末概念题几乎年年扣人分。另一个容易忽略的点评估指标要和业务目标对齐。预测房价差 10 万和差 1000MSE 会极度惩罚前者如果业务上对小误差的容忍度很高、但对离群值不敏感用 MAE 可能更合理。教科书不太强调这个但工作里天天遇到。4. 期末考与平台作业的常见题型和避坑指南4.1 推导题就这三种考法线性回归的推导题翻来覆去就那几类掌握了以后基本是送分题。第一种给损失函数求梯度下降更新公式。步骤很固定写出 J(w)对 w 求偏导再把偏导代进更新公式。重点在于矩阵求导的布局规则不同教材的约定不同分母布局还是分子布局结果差一个转置考试以课本为准。第二种证明最小二乘解。先把 J(w) 写成矩阵形式 J(w) (1/2m)(Xw - y)^T(Xw - y)展开成 w^T X^T Xw - 2w^T X^T y y^T y 三项然后对 w 求导令导数为零得到 X^T Xw X^T y即正规方程。不少同学卡在中间展开那一步建议考前自己完整写两遍速度会快很多。第三种证明 MSE 与极大似然估计等价。设 y_i w^T x_i ε_i其中 ε_i ~ N(0, σ²)写出似然函数取对数去掉与 w 无关的常数项最大化对数似然等价于最小化 Σ(y_i - w^T x_i)²。原理不复杂但作答要完整——正态分布密度函数、似然乘积、对数化、去常数项一步都不能少缺一步扣一分。4.2 概念题易错点清单根据我见过的历年期末题下面这些概念几乎每年都有人栽跟头回归与分类的区别看输出是连续值还是离散标签而不是看数据长什么样。线性的含义y w1x1² w2x2 依然是线性回归因为 w 是线性的y w1²x1 则不是。R² 为负说明模型劣于直接取均值常见于严重过拟合或测试集分布偏移。欠拟合与过拟合的判断训练集和测试集误差都大是欠拟合训练集误差很小、测试集误差大是过拟合。偏差与方差的权衡线性回归在复杂数据上欠拟合说明偏差大多项式次数太高引发过拟合说明方差大。还有一个容易被忽略的数据中存在异常值时MSE 作为损失函数会把模型朝异常值方向拉得很厉害因为平方运算放大了大误差的惩罚。这时可以考虑先做异常值处理或者换 MAE 类损失。这个概念虽然不直接出现在第二章公式里但面试和项目里经常被追问。4.3 头歌、吴恩达作业的典型翻车现场不少同学在头歌或者课程作业平台上卡壳我见过的原因按出现频率排个序。第一数据读取和列拼接出错。平台给的数据可能是 CSV 或 txt第一列可能是索引而不是特征。拿到数据先 df.head() 和 df.shape 确认别急着建模。吴恩达第二周的作业里有一份人口-利润数据特征只有一列很多人把偏置列漏了导致正规方程维度直接对不上。第二提交的内容是损失曲线而不是参数。有些题目要求输出每一轮迭代的损失你就要把 loss_history 完整保留下来按格式打印四舍五入的位数都要和题目一致。这类平台的判分是精确匹配差一位小数就判错不看趋势只看结果。第三特征归一化和数据划分的顺序反了。先切分再在训练集上拟合缩放参数再应用到测试集。这个我在 3.3 已经强调过但它确实是平台作业里最大的隐性扣分点。第四测试集上用了全局标准差。本质和上一条一样但有些人会写成 scaler StandardScaler().fit(X_all)把所有数据都参与计算测试时就泄露了。严格说这不只是线性回归的问题但期末设计题和大作业里特别容易被老师抓。平台判分通常看结果但真实项目评审看过程。数据泄露这种错误在作业平台上可能不报错到了实际业务里会让你的模型上线后表现崩盘。4.4 常见问题排查速查表最后整理一个速查表遇到问题先对照表格定位现象可能原因排查与解决方案损失不降反升学习率过大学习率除以 10或加学习率衰减损失下降极慢学习率过小 / 特征未缩放调大学习率做标准化或归一化训练损失低、测试损失高过拟合增加数据、加正则项、降低多项式次数正规方程报奇异特征数 ≥ 样本数 / 多重共线性改用梯度下降删除冗余特征加 L2 正则岭回归预测结果全是一个常数数据泄露或特征全为零检查缩放参数是否只用训练集计算检查数据预处理梯度与数值结果对不上推导有误用中心差分做梯度检查误差控制在 1e-4 内关于预测结果全是一个常数再多说一句这不是模型的问题而是数据预处理的问题。比如你把测试集单独标准化或者训练时某个特征的方差为 0模型能学到的就只剩偏置。排查这类问题先看训练数据的统计信息再看预处理流程最后才是怀疑模型。5. 一些课本之外的实操习惯讲完推导和代码我想说点课本之外的东西。我见过太多人把这一章看懂了——公式能默写、代码能跑通可一换数据集就懵。根源在于线性回归这章真正要建立的不是公式记忆而是三套思维框架。第一套是矩阵化思维。把手写的向量循环全部改成矩阵乘法是机器学习编程的分水岭。建议凡是遇到对每个样本做一遍的代码都试着改写成矩阵形式效率能提升几十倍而且代码长得跟数学表达式几乎一一对应排查 bug 轻松得多。第二套是可视化验证习惯。训练完模型先别急着看指标画三张图损失曲线看收敛情况预测值对真实值的散点图看整体拟合质量残差图看是否存在隐藏模式——比如残差随预测值增大而增大多半是漏掉了非线性关系。这三张图比任何指标都直观也是我调参时最先看的东西。第三套是梯度检验。手写梯度下降时用数值方法验证一下解析梯度成本极低但能防止公式抄错一个符号、整个模型白跑的悲剧。这个习惯我在写神经网络反向传播时无数次救命现在分享给你def numerical_gradient(J, w, eps1e-5): grad np.zeros_like(w) for j in range(len(w)): wp w.copy(); wp[j] eps wm w.copy(); wm[j] - eps grad[j] (J(wp) - J(wm)) / (2 * eps) return grad期末复习时间紧的话我个人的建议是把正规方程推导、梯度公式、R² 定义这三块先拿稳再把吴恩达第二周作业 ex1 独立写一遍最后花半小时过一遍概念易错点。这三板斧应付大多数学校的期末考试足够了。线性回归这章如果真正学透后面看逻辑回归、正则化、神经网络你会觉得每一处都是在今天的基础上加了一点点东西——那才是最赚的时候。