
在头歌平台上和线性回归死磕了整整一个周末之后我才后知后觉地发现这门课最磨人的不是推导公式而是把公式转化为能跑通的代码时那些“理所当然”的细节。如果你也在国科大的《模式识别与机器学习》L2线性回归上被卡住或者正在为“为什么我的代码在本地跑得好好的一上头歌就出问题”而头疼那么这篇笔记就是为你准备的。我会把L2的核心推导、上机实操的坑、以及从线性回归到正则化的完整脉络一次说清楚尽量让你少走我走过的弯路。顺便提醒一句搜索“L2线性回归”的时候搜索引擎很容易把你带到PTA天梯赛的L2题解那边去那是团体程序设计天梯赛的难度等级跟咱们这门课的L2Lecture 2完全是两码事。别搜错了方向。1. L2线性回归到底在“回归”什么1.1 它不只是一条“拟合直线”很多同学包括当年的我对线性回归的第一印象就是中学数学里“用一条直线穿过一堆散点”。这个印象不算错但放到模式识别的体系里就太窄了。L2里的线性回归实际上在做一件更通用的事给定输入特征 (x \in \mathbb{R}^d)用参数的线性组合来逼近目标输出 (y)。写成数学形式就是[ y w^T x b ]这里 (w) 是权重向量(b) 是偏置。如果输入是一维的那确实就是一条直线但如果输入是二维、三维甚至上百维这个式子描述的就是一个超平面。比如房价预测里(x) 可以是面积、房龄、楼层数、周边配套评分等多个特征模型要学的是这些特征对房价的线性影响权重。关键点在于它叫“线性回归”指的是对参数 (w) 线性而不是对特征 (x) 线性。这意味着我可以把 (x) 换成 (x^2)、(\sin(x))、甚至 (x_1 x_2) 这样的多项式项只要模型关于 (w) 仍然是线性的它依然属于线性回归的范畴。这一点是理解后面多项式回归的钥匙。1.2 为什么这门课把线性回归放在第二讲翻开课程大纲你会发现L1通常讲贝叶斯决策理论L2就跳到线性回归中间跨度很大。我当时不太理解后来学完L3的线性判别分析、L7的支撑向量机才回过味来线性回归是整门课里“从数据估计参数”的第一次正式亮相。贝叶斯决策告诉你理想分类器长什么样但它依赖类条件概率密度这在实战中往往是未知的。而线性回归给了你一套完整的思路假设一个参数化模型定义损失函数用最小化损失来求解参数。这套“模型-损失-优化”的三步走后面几乎每一章都在用。L3的Fisher判别是它L7的支撑向量机本质上是带间隔约束的线性模型甚至L8集成方法里的基学习器也大量是线性模型。所以在L2上花功夫把最小二乘推导吃透并不是为了应付一次作业而是给整门课搭骨架。国科大这门课在头歌平台上的线性回归系列习题目的也不是让你调sklearn完事而是让你用numpy把最小二乘、梯度下降这些核心步骤手写出来和课件的公式一一对应上。2. 最小二乘的三副面孔几何、概率、优化既然要手写代码就得先把数学搞明白。最小二乘这个名字处处可见但它的推导有三种完全不同的视角分别对应“直观理解”“统计原理”和“计算实现”。我建议三个都过一遍你在看代码的时候会有一种“原来这里对应的是那一步”的通透感。2.1 几何视角残差与列空间的正交性把所有样本堆叠成矩阵形式(X) 是 (n \times d) 的特征矩阵(y) 是 (n \times 1) 的目标向量(w) 是 (d \times 1) 的权重。线性回归要最小化的是[ J(w) |Xw - y|^2 ]几何上(Xw) 是 (X) 的列空间 (\text{col}(X)) 里的一个向量。当 (w) 遍历所有取值时(Xw) 只能在列空间里移动。而 (y) 通常在列空间之外样本数量大于特征维度时几乎必然如此。所以要找一个列空间里的向量让它到 (y) 的欧氏距离最近——这就是线性代数里的正交投影。最近的那个点必须满足残差向量 (r y - Xw) 垂直于整个列空间。换句话说(r) 必须和 (X) 的每一列都正交[ X^T (y - Xw) 0 ]展开就得到正规方程[ X^T X w X^T y ]当 (X^T X) 可逆时[ w^* (X^T X)^{-1} X^T y ]这个视角让我彻底记住了正规方程长什么样——它不过就是“残差和特征列正交”这个几何条件的代数表达。你可以类比一个场景在积木搭成的斜面上放一个小球它滑到最低点的位置就是重力沿斜面的分量被完全抵消的地方最优的 (w) 就是让误差的“平行分量”归零的那个值。2.2 概率视角高斯噪声假设下的最大似然几何视角告诉我们“怎么求”概率视角告诉我们“为什么这个损失函数是合理的”。假设真实的生成过程是[ y w^T x \varepsilon ]其中噪声 (\varepsilon) 服从均值为0、方差为 (\sigma^2) 的高斯分布。那么给定 (x) 时(y) 的条件概率也是高斯的均值为 (w^T x)方差为 (\sigma^2)。对 (n) 个独立样本写出对数似然[ \ln L(w) -\frac{n}{2}\ln(2\pi\sigma^2) - \frac{1}{2\sigma^2}\sum_{i1}^{n}(y_i - w^T x_i)^2 ]最大化对数似然等价于最小化最后那一项平方和因为前面的常数项和 (\sigma^2) 都与 (w) 无关。所以最小二乘 高斯噪声假设下的最大似然估计。这个结论的意义在于如果你把噪声假设换成拉普拉斯分布最大似然准则推出来的就是最小化绝对值误差L1损失。这说明损失函数不是凭空发明的它对应着我们对数据生成过程的假设。这个思维对后面理解正则化、鲁棒回归都很有用。2.3 优化视角梯度下降是另一个版本的答案正规方程一次性求出闭式解但并不是所有模型都有闭式解。线性回归恰好有但它依然是理解梯度下降的最佳练习场。对 (J(w)) 求梯度。先把平方项展开[ J(w) w^T X^T X w - 2 w^T X^T y y^T y ]对 (w) 求导得到[ \nabla J(w) 2X^T X w - 2X^T y 2X^T(Xw - y) ]梯度下降的更新规则就是[ w \leftarrow w - \eta \nabla J(w) ]走到这一步你会发现令梯度为零得到的正是正规方程 (X^T X w X^T y)。所以梯度下降的本质是用迭代方式逼近正规方程的解而不是一个完全不同的方法。实际代码里批量梯度下降每轮要用全量数据算梯度样本量大时很慢随机梯度下降每次只用一个样本算梯度快但噪声大小批量梯度下降取中间值是深度学习和传统机器学习里最常用的折中方案。但L2作业的规模一般用正规方程就够了深入理解梯度下降更多是为了后面课程做铺垫。3. 正规方程、条件数与梯度下降的实战取舍3.1 正规方程不是万能的第一次手写线性回归代码时我的第一反应就是照抄公式 (w (X^T X)^{-1} X^T y)然后调np.linalg.inv求逆。直到有一次特征维度稍高训练直接跑出天文数字的权重我才意识到这个“标准解法”在数值上有多脆弱。问题出在两步。第一(X^T X) 的条件数可能是 (10^{12}) 这种量级直接求逆会放大舍入误差第二当特征之间存在高度相关性时比如两个特征几乎成比例(X^T X) 接近奇异行列式极小逆矩阵的元素会剧烈震荡任何一个微小扰动都会让权重 (w) 彻底失控。正确的做法是用np.linalg.solve(X.T X, X.T y)来求解线性方程组或者更稳妥地用 SVD 伪逆np.linalg.pinv(X) y。背后的道理是(X^T X) 的平方会放大条件数(\kappa(X^T X) \kappa(X)^2)所以直接在 (X) 上做 SVD 分解比在 (X^T X) 上求逆数值上更稳。一个直观的对比实验是构造一组两列几乎相同的特征矩阵 (X)分别用inv和pinv求权重你会发现前者的结果可能随数据微小扰动剧烈变化而后者稳定得多。3.2 条件数为什么归一化这么重要既然说到数值稳定性就绕不开条件数这个概念。通俗地说条件数衡量的是“输入扰动后输出变化的放大倍数”。如果条件数大训练数据里一点点噪声都会让求解出的 (w) 面目全非。特征尺度的不一致是条件数变大的最常见原因。假设 (x_1) 取值范围是 ([0, 1])(x_2) 取值范围是 ([0, 10^4])那么 (X^T X) 的对角元素量级相差 (10^8)这个矩阵的“扁”和“长”会让求解过程极其敏感。所以对特征做标准化减均值、除标准差不只是锦上添花在数值上是必需的。这也能解释为什么头歌平台上那些“需要梯度下降”的题如果你不归一化loss 曲线会像心电图一样乱跳甚至直接变 NaN——因为梯度的各分量尺度不一致更新步长没法同时适配所有维度。3.3 什么规模用什么方法我给自己定了一个粗粒度选型参考分享出来供你参考场景推荐方案原因特征维度 (d \le 1000)样本量 (n \le 10^5)正规方程 /lstsq闭式解一步到位无需调学习率特征维度 (d 10^4)或样本量巨大小批量梯度下降计算 (X^T X) 的内存和时间不可接受特征高度相关 / 疑似过拟合岭回归带正则项(X^T X \lambda I) 改善条件数限制权重幅度需要稀疏解 / 特征选择LASSOL1 惩罚让部分权重精确为零L2作业的数据规模用正规方程完全够用但如果后续课程要求你实现梯度下降版本的回归上面那张表可以帮你选对方向。4. 头歌线性回归习题比课件多出来的那几步4.1 四种典型的卡壳点头歌平台上的线性回归题目我前后调了五版才全部跑通遇到的坑基本可以归为四类。第一类数据读取与预处理的细节。有些测试数据文件带表头、有多余空格、有空行直接用np.loadtxt或np.genfromtxt容易读崩。建议统一先读取原始文本观察几行再做strip()、去空值、分隔符处理。读进来之后别忘了检查维度——y是列向量还是行向量X的形状是(n, d)还是(d, n)不确认就打印shape看一眼。第二类忘记增广一列全1。模型 (y w^T x b) 里那个偏置 (b)在矩阵形式中可以吸进 (w)代价是在 (X) 左边拼一列 1。很多人推导公式时记得 (b)写代码时却把这一列漏了结果所有预测都整体偏移一个常数。这个错误在本地测试时很容易被忽略因为误差平方和虽然大了但曲线形态还是对的。第三类特征缩放。前面已经详细讲过梯度下降版本不做标准化几乎是必炸的。即便是正规方程版本标准化虽然不影响最终正规方程的解前提是没加正则项但能显著改善数值稳定性。第四类输出格式。头歌的测试输出通常要求保留固定小数位比如保留4位小数。用print(f{value:.4f})可以格式化但要注意round()在 Python 中的“银行家舍入”行为和格式化输出的 “四舍五入” 并不完全一致尽量用格式化字符串而不是round()。4.2 一套可以复用的numpy模板下面这套代码框架是通用解法可以在不依赖 sklearn 的前提下完成线性回归的拟合与预测适合应付头歌上的线性回归类题目。它不算“标准答案”但体现了教学要求里的核心步骤。import numpy as np def standardize(X): mu np.mean(X, axis0) sigma np.std(X, axis0) # 避免除零特征全为常数时标准差为0 sigma[sigma 0] 1.0 return (X - mu) / sigma, mu, sigma def add_bias(X): # 在特征矩阵左侧拼一列全1对应偏置项 return np.hstack([np.ones((X.shape[0], 1)), X]) def fit_normal_equation(X, y): # X: (n, d) 的特征矩阵已含偏置列 # 用 lstsq 替代显式求逆数值上更稳定 w, *_ np.linalg.lstsq(X, y, rcondNone) return w # 使用时 # X_train, y_train, X_test 是你加载的数据 # Xs, mu, sigma standardize(X_train) # Xs_b add_bias(Xs) # w fit_normal_equation(Xs_b, y_train) # X_test_s (X_test - mu) / sigma # X_test_b add_bias(X_test_s) # pred X_test_b w这段代码的逻辑是先标准化再加偏置列最后用lstsq求解。lstsq底层走的是 SVD 分解比手写正规方程并求逆稳健得多这是我在踩过数值坑之后的一个长期习惯。4.3 多项式回归从线性到非线性的第一步头歌平台上有不少“线性回归多项式回归”相关的习题乍看好像超纲了其实原理上仍然是线性回归。多项式回归做的事情是把原始特征 (x) 扩展成 (x, x^2, x^3, \ldots, x^p)然后仍然用线性模型拟合。因为模型对权重 (w) 是线性的所以正规方程照样适用。你只需要自己把特征矩阵拼出来def polynomial_features(x, degree): # x: (n, ) 或 (n, 1) 的原始特征 x np.asarray(x).reshape(-1, 1) return np.hstack([x ** i for i in range(1, degree 1)])注意我这里没有包含 1 这一列等会儿在外面统一用add_bias。根据我自己的实验degree 从 1 提高到 9训练集上的误差会一直下降但测试集上的误差会先降后升——这就是过拟合的直观体现。多项式扩展给了线性模型“弯曲”的能力但能力越大越需要正则化来约束正好引出下一章内容。5. 正则化与模型选择避免考完试才发现的过拟合5.1 岭回归为什么能救奇异矩阵前面提到特征高度相关时 (X^T X) 接近奇异正规方程解出来的权重会异常大。解决思路很直接在损失函数里给权重的平方和加一个惩罚项[ J_{\text{ridge}}(w) |Xw - y|^2 \lambda |w|^2 ]求梯度并令其为零得到的闭式解是[ w_{\text{ridge}} (X^T X \lambda I)^{-1} X^T y ]多出来的 (\lambda I) 把矩阵对角线整体抬升条件数随之下降即使 (X^T X) 奇异也能保证可逆。从概率视角看这等价于给权重 (w) 施加一个均值为0的高斯先验然后做最大后验估计。L2正则化因此也被称为“权重衰减”。实际操作中最直观的判断方式是看岭迹图把 (\lambda) 从 (10^{-4}) 到 (10^6) 按对数刻度扫一遍画出每个权重的取值变化。你会看到 (\lambda) 很小时权重在剧烈波动(\lambda) 很大时权重趋于0真正的“甜点区”往往在曲线刚开始平滑、权重幅度还没被过度压缩的区域。5.2 LASSO为什么 L1 能给出稀疏解岭回归的 L2 正则化能让权重变小但不会让它们精确变成0。LASSO 用的是 L1 惩罚[ J_{\text{lasso}}(w) |Xw - y|^2 \lambda |w|_1 ]几何上L2 的约束区域是圆等值线是光滑的球L1 的约束区域是顶点落在坐标轴上的菱形。最优解在菱形顶点相切时某些坐标方向的权重就恰好为0。于是 LASSO 天然能做特征选择把无关特征的权重清零。不过 L1 惩罚的优化不能像岭回归一样直接套正规方程通常用坐标下降法或近端梯度法。如果题目场景允许调库sklearn 的Lasso类封装得很好如果需要自己实现坐标下降的核心是反复把每个坐标上的偏置项剥离后求闭式解。这个细节在L2阶段了解即可后面课程里还有更多优化算法出场。5.3 用交叉验证选 (\lambda) 和模型阶数最后一个问题(\lambda) 取多少合适多项式阶数取多少合适靠猜是不行的要用数据说话。最常用的方法是 K 折交叉验证把训练集分成 K 份每次用 K-1 份训练、1 份验证轮流 K 次记录每一组超参数下的平均验证误差常用 RMSE。选验证误差最小的超参数再用全量训练集重新训练一次模型。以多项式回归为例把 degree 设为 [1, 2, 3, 5, 8, 12] 分别做 5 折交叉验证你会得到一条先降后升的误差曲线。拐点附近的 degree 就是你在当前数据量下的合适复杂度。如果 12 阶的误差还在持续下降说明数据量或者特征表达不足以支撑“过拟合拐点”的出现这时就要怀疑是不是数据量太小或者特征设计有问题。评估指标方面除了 RMSER²决定系数在课程里也经常出现。R² 越大说明模型解释的方差比例越高但它在单变量线性回归里容易给人“拟合得很好”的错觉实际还得看残差图。如果预测值对应残差呈现明显的 V 形或周期性模式说明模型结构本身可能就不对——这时候不是调参而是要加特征或换模型。说回我自己踩坑的经历。第一次做头歌线性回归作业的时候我连打印中间结果的习惯都没有推导公式时记得清清楚楚代码一跑就 NaN然后我就开始怀疑自己是不是天生不适合写代码。后来发现只要在每一步之间加一句print(shape)或者把标准化前后的均值方差打出来看一眼很多问题几秒钟就能定位。希望这篇笔记能帮你把推导和代码之间的那道坎跨过去。线性回归作为模式识别课程的正式开场值得你多花点时间把细节都抠明白——毕竟后面 L3、L4 的那些公式可都在这一章的基础上长出来呢。