
最小二乘法这个名字几乎每个学过数据分析、信号处理或者机器学习的人都听过。但我在带新人和做技术评审的时候发现一个很普遍的现象很多人能背出公式能调用现成的库函数可一旦被问到这个公式到底怎么来的为什么偏偏是平方而不是绝对值矩阵形式是怎么从标量形式推出来的就开始含糊其辞了。这个内容就是写给这类朋友的——你可能已经在用最小二乘法做线性回归、做曲线拟合、做传感器标定但对它背后的数学构造还停留在会用的层面。我会从最朴素的误差直觉出发一步步把公式推导出来包括标量形式和矩阵形式再补充几个实际使用中容易踩的坑。不管你是刚接触线性回归的学生还是工作几年想回头补数学底子的工程师应该都能从中拿到一些实在的东西。1. 从一个让人不舒服的问题说起为什么是平方1.1 误差度量的直觉选择假设你手头有一组实验数据横坐标是输入变量纵坐标是观测结果你隐约觉得它们之间是线性关系想找一条直线去拟合。问题来了什么样的直线才算最好最直接的想法是让每个数据点到直线的偏差加起来最小。但这里有个麻烦——偏差有正有负直接求和的话正偏差和负偏差会互相抵消一条烂到家的直线也可能因为正负抵消而得到总偏差为零的漂亮结果。这显然不行。那退一步把偏差取绝对值再求和呢这个思路其实很合理数学上叫最小绝对偏差法。它确实能避免正负抵消的问题而且在某些场景下比最小二乘法更稳健对异常值不那么敏感。但它有个致命的缺点绝对值函数在零点不可导这给求极值带来了很大麻烦。你没法像处理光滑函数那样直接求导令其为零得用线性规划之类的数值方法去解计算上不划算。于是就有了另一个选择把偏差平方之后再求和。平方运算有两个好处一是它天然消除了符号的影响任何实数的平方都是非负的二是它是一个光滑函数处处可导求极值的时候可以直接用微积分那套成熟工具。这就是最小二乘法的核心思想——让所有数据点的残差平方和达到最小。注意平方和最小这个准则并不是唯一正确的准则它是一种在数学便利性和统计合理性之间取得平衡的选择。理解这一点比死记公式重要得多。1.2 高斯-马尔可夫定理给出的理论支撑光说平方好算还不够有说服力毕竟好算不等于合理。真正让最小二乘法在统计学中站稳脚跟的是高斯-马尔可夫定理。这个定理说的是在一组特定的假设下误差均值为零、误差方差相等且不相关、自变量与误差不相关最小二乘估计量是所有线性无偏估计量中方差最小的那个。换句话说它是最优的线性无偏估计。这个结论给了最小二乘法一个非常硬的理论背书——它不只是一个方便的选择在满足假设的前提下它确实是最好的选择之一。我在实际项目里经常跟团队强调这一点当你用最小二乘法做线性回归的时候你其实是在隐含地接受一组假设。如果数据严重违反这些假设比如误差方差随自变量变化而变化异方差性或者误差之间存在强相关那么最小二乘估计虽然仍然是无偏的但不再是方差最小的了这时候就需要考虑加权最小二乘或者广义最小二乘。1.3 从残差到损失函数概念的衔接在机器学习的语境里最小二乘法的目标函数通常被称为均方误差或者平方损失函数。这跟统计学里的残差平方和是同一个东西只是表达习惯不同。统计学更关注估计量的性质机器学习更关注优化过程但数学内核完全一致。理解了这个衔接你再看线性回归的损失函数就不会觉得突兀了。它不是什么凭空冒出来的设计而是从最小二乘法这个古老的数学方法一路传承下来的。很多现代损失函数比如Huber损失本质上就是在最小二乘的基础上做改进——在误差较小的时候用平方误差较大的时候切换到绝对值兼顾了光滑性和稳健性。2. 标量形式下的完整推导过程2.1 问题设定与符号约定先把问题定义清楚。假设我们有 n 个观测数据点每个点用 (x_i, y_i) 表示其中 i 从 1 到 n。我们想找一条直线 y ax b 来拟合这些点其中 a 是斜率b 是截距这两个就是待求的未知参数。对于每个数据点模型给出的预测值是 ax_i b而实际观测值是 y_i。两者之间的差就是残差记作 e_i y_i - (ax_i b)。最小二乘法的目标就是让所有残差的平方和最小即最小化下面这个函数S(a, b) Σ(y_i - ax_i - b)²求和从 i1 到 n。这里 S 是 a 和 b 的二元函数。我们的任务就是找到让 S 取最小值的 a 和 b。2.2 对两个参数分别求偏导因为 S 是 a 和 b 的二次函数它是一个开口向上的抛物面最小值一定在偏导数同时为零的地方取到。所以分别对 a 和 b 求偏导。先对 b 求偏导。把求和展开对 b 求导的时候每一项 (y_i - ax_i - b)² 的导数是 2(y_i - ax_i - b) 乘以 (-1)所以∂S/∂b -2Σ(y_i - ax_i - b)令它等于零得到Σ(y_i - ax_i - b) 0这个式子展开后就是 Σy_i aΣx_i nb整理一下b (Σy_i - aΣx_i) / n也就是说b 等于 y 的均值减去 a 乘以 x 的均值。记 x̄ Σx_i/nȳ Σy_i/n那么 b ȳ - ax̄。这个结果非常直观拟合直线一定经过样本均值点 (x̄, ȳ)。再对 a 求偏导∂S/∂a -2Σx_i(y_i - ax_i - b)令它等于零得到Σx_i(y_i - ax_i - b) 0展开Σx_i y_i - aΣx_i² - bΣx_i 02.3 消元求解与最终公式现在把 b ȳ - ax̄ 代入上面这个方程。为了书写方便我用 Σx 表示 Σx_iΣy 表示 Σy_iΣx² 表示 Σx_i²Σxy 表示 Σx_i y_i。代入后Σxy - aΣx² - (ȳ - ax̄)Σx 0展开Σxy - aΣx² - ȳΣx ax̄Σx 0注意 ȳΣx (Σy/n)Σx ΣxΣy/n而 x̄Σx (Σx/n)Σx (Σx)²/n。把这些代进去整理得到a (nΣxy - ΣxΣy) / (nΣx² - (Σx)²)这就是斜率的最终公式。分母 nΣx² - (Σx)² 可以写成 nΣ(x_i - x̄)²分子 nΣxy - ΣxΣy 可以写成 nΣ(x_i - x̄)(y_i - ȳ)。所以斜率也可以表达为a Σ(x_i - x̄)(y_i - ȳ) / Σ(x_i - x̄)²这个形式更对称也更容易记忆。分子是 x 和 y 的协方差未归一化分母是 x 的方差未归一化。斜率本质上就是协方差除以方差。求出 a 之后代回 b ȳ - ax̄ 就得到截距。提示实际编程实现的时候用中心化形式 a Σ(x_i - x̄)(y_i - ȳ) / Σ(x_i - x̄)² 数值稳定性更好因为避免了直接计算大数相减带来的精度损失。这一点在处理数值范围较大的数据时尤其重要。2.4 一个手算例子验证推导光看公式容易晕拿一组简单数据走一遍。假设有四个点(1, 2)、(2, 4)、(3, 5)、(4, 8)。先算均值x̄ (1234)/4 2.5ȳ (2458)/4 4.75。然后算中心化后的量x 的偏差-1.5, -0.5, 0.5, 1.5y 的偏差-2.75, -0.75, 0.25, 3.25分子 Σ(x_i - x̄)(y_i - ȳ) (-1.5)(-2.75) (-0.5)(-0.75) (0.5)(0.25) (1.5)(3.25) 4.125 0.375 0.125 4.875 9.5。分母 Σ(x_i - x̄)² 2.25 0.25 0.25 2.25 5。所以 a 9.5 / 5 1.9。b ȳ - ax̄ 4.75 - 1.9 × 2.5 4.75 - 4.75 0。拟合直线是 y 1.9x。你可以把这四个点画出来看看这条线确实穿过了数据中心而且整体偏差看起来是最小的。手算一遍的好处是你对公式里每一项的含义会有更具体的感受。3. 矩阵形式从标量到向量的升维3.1 为什么要用矩阵表达标量形式处理一元线性回归够用了但现实中我们面对的问题往往有多个自变量。比如预测房价影响因素可能有面积、楼层、房龄、距离地铁站的距离等等。这时候模型变成 y w₁x₁ w₂x₂ ... w_dx_d b用标量形式一个个求偏导会非常繁琐。矩阵形式的好处是把所有参数打包成一个向量把所有数据打包成矩阵整个问题可以用一个紧凑的表达式写出来推导过程也大大简化。而且矩阵形式直接对应代码实现numpy 里几行就能搞定。3.2 设计矩阵与参数向量的构造把每个样本的特征写成一个行向量再加上一个恒为 1 的分量来吸收截距项。具体来说对于第 i 个样本构造增广特征向量 x_i [1, x_i1, x_i2, ..., x_id]其中第一个 1 对应截距。把所有 n 个样本的增广特征向量堆叠起来就得到 n×(d1) 的设计矩阵XX [[1, x_11, ..., x_1d], [1, x_21, ..., x_2d], ..., [1, x_n1, ..., x_nd]]参数向量记作 w [b, w₁, w₂, ..., w_d]^T是一个 (d1)×1 的列向量。观测值向量记作 y [y₁, y₂, ..., y_n]^T。模型对所有样本的预测就是 Xw残差向量就是 y - Xw。3.3 损失函数的矩阵表达与求导残差平方和可以写成残差向量的内积也就是 (y - Xw)^T(y - Xw)。展开S(w) (y - Xw)^T(y - Xw) y^T y - y^T Xw - w^T X^T y w^T X^T Xw注意 y^T Xw 和 w^T X^T y 都是标量而且它们互为转置所以相等。因此S(w) y^T y - 2w^T X^T y w^T X^T Xw对 w 求梯度。这里用到两个矩阵求导公式∂(w^T a)/∂w a∂(w^T A w)/∂w 2Aw当 A 对称时。X^T X 是对称矩阵所以∂S/∂w -2X^T y 2X^T Xw令梯度为零X^T Xw X^T y这就是著名的正规方程。如果 X^T X 可逆那么w (X^T X)^{-1} X^T y这个公式就是最小二乘的矩阵解。它把标量形式下那一堆求和符号全部浓缩成了一个简洁的矩阵表达式。3.4 正规方程的几何解释正规方程 X^T Xw X^T y 有一个很漂亮的几何含义。Xw 是设计矩阵列空间的某个向量而 y 是观测向量。残差 y - Xw 垂直于 X 的列空间也就是说最小二乘解对应的预测值 Xw 是 y 在 X 列空间上的正交投影。X^T(y - Xw) 0 这个条件说的就是残差与 X 的每一列都正交。从几何上看你是在 X 的列空间里找一个离 y 最近的向量最近的那个点就是正交投影点。这个视角比纯代数推导直观得多也是理解最小二乘法本质的一个关键角度。注意当 X 的列之间存在线性相关多重共线性时X^T X 会接近奇异甚至完全奇异这时候直接求逆会出问题。实际中通常用伪逆或者加正则项岭回归来处理。4. 数值计算中的坑与工程实践4.1 直接求逆为什么不可取理论上 w (X^T X)^{-1} X^T y 很完美但在计算机上直接算逆矩阵是个糟糕的主意。原因有几个一是求逆的计算复杂度是 O(d³)当特征维度很高时非常慢二是数值稳定性差X^T X 的条件数是 X 的条件数的平方这意味着如果 X 本身条件数就不小X^T X 的条件数会大到让浮点运算失去有效精度。我在实际项目里见过太多次因为直接求逆导致结果完全跑偏的案例。有一次团队做传感器标定特征之间相关性很强直接用求逆算出来的系数跟物理预期差了十万八千里换成 QR 分解之后立刻就正常了。4.2 QR分解与SVD更稳的解法实践中推荐用QR 分解或者奇异值分解来解最小二乘问题。QR 分解的思路是把设计矩阵 X 分解为正交矩阵 Q 和上三角矩阵 R 的乘积即 X QR。代入正规方程R^T Q^T Q R w R^T Q^T y因为 Q 是正交矩阵Q^T Q I所以R^T R w R^T Q^T y进一步化简得到 Rw Q^T y。这是一个上三角方程组用回代法几行代码就能解出来而且数值稳定性远好于直接求逆。SVD 的思路类似把 X 分解为 UΣV^T最小二乘解可以写成 w VΣ⁺U^T y其中 Σ⁺ 是伪逆。SVD 的好处是即使 X 秩亏也能给出一个合理的解而且能直接看出哪些方向是病态的。在 numpy 里np.linalg.lstsq内部用的就是 SVD所以日常使用直接调这个函数就行不用自己手写分解。4.3 特征缩放对数值稳定性的影响还有一个容易被忽略的点是特征缩放。如果不同特征的量纲差异很大比如一个特征范围是 0 到 1另一个是 0 到 1000000那么设计矩阵的条件数会非常大数值求解的精度会严重下降。解决办法是在求解之前对特征做标准化让每个特征均值为零、方差为一。这样处理之后X^T X 的条件数会显著改善。需要注意的是标准化之后的系数是在标准化空间里的系数如果要还原到原始空间需要做相应的逆变换。4.4 常见问题排查对照表现象可能原因排查方向系数结果与预期严重不符特征量纲差异大或共线性强检查特征相关性矩阵做标准化程序报奇异矩阵错误X^T X 不可逆改用伪逆或加正则项拟合效果差但系数看起来正常模型假设不成立检查残差图考虑非线性项新数据预测偏差大过拟合或外推检查训练数据覆盖范围加正则化计算速度慢特征维度高且用直接求逆改用 QR 或 SVD或梯度下降这张表是我自己在调试线性回归模型时总结的基本上覆盖了八成以上的常见问题。遇到问题的时候按这个顺序排查效率会高很多。5. 最小二乘与相关概念的边界5.1 和梯度下降的关系有人会问既然有正规方程的解析解为什么还要用梯度下降答案在于规模。正规方程需要计算 X^T X 并求解复杂度跟特征维度的立方成正比。当特征维度达到几十万甚至上百万的时候比如推荐系统里的稀疏特征正规方程根本算不动。这时候梯度下降特别是随机梯度下降就成了唯一可行的选择。两者的目标函数完全一样只是求解路径不同。正规方程一步到位梯度下降迭代逼近。理解这一点你就不会把两者对立起来看了。5.2 和岭回归、Lasso的区别最小二乘的目标函数只有残差平方和这一项。岭回归在它基础上加了一个 L2 正则项 λ||w||²Lasso 加的是 L1 正则项 λ||w||₁。加正则项的目的有两个一是防止过拟合二是处理共线性问题。岭回归的正则项让 X^T X λI 始终可逆从根本上解决了奇异问题。Lasso 的 L1 正则还有一个额外好处它会把不重要的特征系数压缩到零起到特征选择的作用。从最小二乘到岭回归的推导也很直接目标函数变成 ||y - Xw||² λ||w||²求导令零得到 (X^T X λI)w X^T y解出来就是 w (X^T X λI)^{-1} X^T y。你看只是在对角线上加了一个 λ就解决了大问题。5.3 加权最小二乘的适用场景普通最小二乘隐含假设所有样本的误差方差相同。但现实中经常不是这样比如某些测量仪器的精度随量程变化高量程处误差更大。这时候应该给精度高的样本更大的权重这就是加权最小二乘。加权最小二乘的目标函数是 Σw_i(y_i - ŷ_i)²其中 w_i 是第 i 个样本的权重。矩阵形式下目标函数变成 (y - Xw)^T W (y - Xw)其中 W 是对角权重矩阵。求导得到 X^T W X w X^T W y解为 w (X^T W X)^{-1} X^T W y。形式跟普通最小二乘几乎一样只是多了一个权重矩阵。权重的选取通常基于对误差方差的估计。如果已知每个样本的测量方差 σ_i²那么最优权重是 w_i 1/σ_i²。这个结论同样来自高斯-马尔可夫定理的推广。6. 从公式到代码一次完整的实现6.1 用numpy实现三种解法下面用一段代码把前面讲的三种解法都实现一遍方便你对比效果。import numpy as np # 生成模拟数据 np.random.seed(42) n, d 100, 3 X_raw np.random.randn(n, d) true_w np.array([2.0, -1.5, 0.8]) y X_raw true_w 1.0 np.random.randn(n) * 0.1 # 构造增广设计矩阵 X np.hstack([np.ones((n, 1)), X_raw]) # 方法一直接求逆不推荐仅作对比 w_inv np.linalg.inv(X.T X) X.T y # 方法二QR分解 Q, R np.linalg.qr(X) w_qr np.linalg.solve(R, Q.T y) # 方法三SVDnumpy内置lstsq w_svd, residuals, rank, sv np.linalg.lstsq(X, y, rcondNone) print(直接求逆:, w_inv) print(QR分解:, w_qr) print(SVD:, w_svd)跑一遍你会发现三种方法在这组数据上结果几乎一样因为数据条件数很好。但如果你把特征之间的相关性调高或者把量纲差异拉大直接求逆的结果就会开始飘而 QR 和 SVD 依然稳定。6.2 手写梯度下降版本为了加深理解再手写一个梯度下降版本。虽然实际项目里不会这么用但写一遍能帮你把损失函数的梯度跟参数更新联系起来。def gradient_descent(X, y, lr0.01, epochs1000): n, d X.shape w np.zeros(d) for _ in range(epochs): grad 2 / n * X.T (X w - y) w - lr * grad return w w_gd gradient_descent(X, y) print(梯度下降:, w_gd)注意这里的学习率需要根据数据尺度调整。如果特征没有标准化学习率要设得很小才能收敛这也是为什么前面强调特征缩放的重要性。6.3 验证与可视化建议实现完之后建议做两件事来验证结果。第一是画残差图横轴是预测值纵轴是残差理想情况下残差应该随机分布在零附近没有明显的模式。如果残差呈现漏斗形或者曲线形说明模型假设有问题。第二是计算 R² 决定系数它衡量模型解释了因变量多少比例的方差是一个直观的拟合优度指标。R² 1 - SS_res / SS_tot其中 SS_res 是残差平方和SS_tot 是总平方和。R² 越接近 1 说明拟合越好但要注意 R² 会随着特征数量增加而虚高所以特征多的时候要看调整后的 R²。我在实际工作中还习惯把拟合直线和原始数据画在同一张图上肉眼过一遍。有时候数值指标看起来不错但图上一看就发现某些区域拟合得很差这种直觉判断是纯看数字替代不了的。7. 几个容易被问到的细节问题7.1 为什么最小二乘解一定存在从代数角度看正规方程 X^T Xw X^T y 一定有解因为 X^T y 始终在 X^T X 的列空间里。这个结论不依赖于 X^T X 是否可逆。当 X^T X 不可逆时解不唯一但所有解给出的预测值 Xw 是相同的也就是说模型在训练数据上的拟合效果是一样的只是参数取值不同。从几何角度看y 在 X 列空间上的正交投影总是存在的而且唯一。这个投影对应的就是最小二乘的预测值。参数不唯一只是因为 X 的列之间存在冗余不同的参数组合可以表示同一个投影。7.2 残差和误差不是一回事这两个概念经常被混用但严格来说有区别。误差是观测值与真实值之间的差真实值我们通常不知道。残差是观测值与模型预测值之间的差是可以计算的。最小二乘法最小化的是残差平方和不是误差平方和。理解这个区别很重要因为残差的性质依赖于模型和数据的共同作用而误差的性质只依赖于数据生成过程。在做统计推断的时候我们通常对误差的分布做假设然后通过残差来检验这些假设是否合理。7.3 最小二乘对异常值为什么敏感因为平方运算会放大大的偏差。一个偏离很远的异常点它的残差平方可能是正常点的几百倍在最小化总平方和的时候拟合直线会被这个异常点拽过去很多。这就是为什么数据里如果有明显的异常值直接上最小二乘往往效果不好。处理办法有几种一是先做异常值检测和剔除二是改用 Huber 损失或者最小绝对偏差这类稳健方法三是用 RANSAC 之类的随机采样一致性算法。具体选哪种要看业务场景和对异常值的容忍度。7.4 多项式拟合算不算线性回归算。虽然模型关于自变量 x 是非线性的比如 y w₀ w₁x w₂x²但它关于参数 w 是线性的。你完全可以把 x 和 x² 看成两个不同的特征然后套用线性回归的整套框架。最小二乘法要求的是模型关于参数线性而不是关于输入线性。这个区分在理解广义线性模型的时候很关键。8. 写在最后的一点个人体会最小二乘法这个工具我从学生时代用到现在越用越觉得它的设计精妙。它把一个看起来模糊的拟合得好不好的问题转化成了一个有明确数学定义的优化问题而且这个优化问题还有闭式解。这种从直觉到形式化的过程本身就是应用数学最迷人的地方。如果你正在学线性回归我的建议是不要跳过推导直接调库。花一个下午把标量形式和矩阵形式的推导亲手写一遍再用代码验证一遍你对这个方法的理解会完全不一样。后面再学岭回归、Lasso、广义最小二乘的时候你会发现它们都是在最小二乘这个地基上添砖加瓦理解起来会顺畅很多。另外实际项目中遇到拟合效果不好的时候先别急着换更复杂的模型。回头检查一下数据有没有异常值、特征之间有没有严重共线性、误差方差是不是齐性的这些问题解决了最小二乘往往就能给出足够好的结果。简单的方法用到位比复杂的方法用半吊子强得多。