最小二乘法这个名字,很多人在大学课堂上第一次听到时觉得它不过就是个拟合工具,工作几年之后才发现,它几乎是所有数据类项目的底层地基。我做数据建模和算法落地这些年,从传感器标定、广告归因,到推荐系统的特征拟合,绕来绕去总会回到这条“让误差平方和最小”的主线上。它解决的问题很朴素:手上有一堆带噪声的观测点,想找一条(或一个)最靠谱的规律去描述它们,同时还要能说清楚这条规律到底靠不靠谱。这篇文章我打算把最小二乘法从直觉、原理、公式推导到代码实现完整走一遍,适合刚接触拟合的初学者,也适合想重新把数学底子捡回来的工程师。看完你至少能做到三件事:自己推导出正规方程、手写一个可用的最小二乘拟合函数、以及在遇到病态矩阵时知道该往哪个方向排查。
1. 最小二乘法到底在解决什么问题
1.1 从一个生活场景说起
假设你开了个奶茶店,想研究“气温”和“冰饮销量”之间的关系。你连续记录了十天,每天的最高气温和当天冰饮卖出的杯数。把这些点画在坐标纸上,你会发现它们大致沿着一条斜向上的直线分布,但又不完全在一条线上——因为销量还受周末、促销、隔壁新店开业等因素影响。这时候你想画一条“最能代表整体趋势”的直线,问题就来了:能穿过所有点的直线几乎不存在,那到底哪条线才算“最好”?
最小二乘法给出的答案非常直接:对每一条候选直线,计算每个真实点到这条直线的垂直偏差(残差),把这些偏差平方之后全部加起来,谁的平方和最小,谁就是最优的那条线。为什么是平方而不是绝对值?这个问题我后面会专门讲,因为它牵扯到最小二乘法最核心的数学动机。
用一句话概括:最小二乘法是一种通过最小化残差平方和来寻找最优拟合参数的数学方法。它既能做直线拟合,也能做多项式拟合、多元回归,甚至是复杂的非线性问题的线性化求解。
1.2 为什么是“二乘”而不是“一次”或“三次”
“二乘”在中文里就是平方的意思。选择平方而不是绝对值,背后有三个非常实际的考量,这也是我在实际项目中反复验证过的:
- 可导性:平方函数处处可导,而绝对值函数在零点不可导。可导意味着我们可以用求导等于零的方式直接解出解析解,这是最小二乘法能推出漂亮闭式公式的根本原因。
- 对大误差更敏感:平方会放大较大的偏差,抑制较小的偏差。换句话说,它更“讨厌”离谱的离群点,倾向于让整体拟合不要被个别极端值带偏太多。
- 统计意义:当观测误差服从正态分布时,最小二乘估计等价于极大似然估计。这一点非常关键,它让最小二乘法不只是一个几何技巧,而是有严格统计理论支撑的估计方法。
注意:平方对离群点敏感是双刃剑。如果数据里混入了明显的错误点,普通最小二乘会被严重拉偏,这时候要考虑鲁棒回归或者先做异常值剔除。
1.3 最小二乘法的适用边界
不是所有拟合问题都适合直接上最小二乘。我总结了几条判断标准:
| 场景特征 | 是否适合普通最小二乘 | 说明 |
|---|---|---|
| 误差主要在因变量方向 | 适合 | 经典假设,自变量测量精确 |
| 自变量和因变量都有明显误差 | 需谨慎 | 应考虑整体最小二乘或正交回归 |
| 存在强离群点 | 不适合 | 改用Huber、RANSAC等鲁棒方法 |
| 特征数远大于样本数 | 不适合直接求解 | 需引入正则化,如岭回归 |
| 关系明显非线性 | 需变换 | 可通过对数、多项式扩展转为线性 |
这张表是我踩过坑之后整理的。早期做传感器标定时,我一度忽略了自变量本身也有噪声,结果拟合出来的系数系统性偏小,后来换成整体最小二乘才把问题解决。
2. 原理详解:从几何直觉到统计基础
2.1 几何视角:投影与正交
理解最小二乘法最漂亮的方式是几何。把观测值向量 y 想象成空间里的一个点,把模型能表示的所有可能结果构成一个子空间(列空间)。最小二乘要做的,就是在这个子空间里找一个离 y 最近的点。而空间中点到子空间的最短距离,就是垂线——也就是投影。
这个结论直接推出一个重要性质:残差向量与拟合值向量正交。用公式写就是 X 转置乘以残差等于零,这正是正规方程的来源。我第一次真正理解这个正交性的时候,感觉整个最小二乘的推导都变得顺理成章了,因为它不再是一堆代数变形,而是一个清晰的几何事实。
2.2 代数视角:目标函数与求导
设模型为 y = Xβ + ε,其中 X 是设计矩阵,β 是待求参数,ε 是误差。残差平方和定义为:
S(β) = (y - Xβ)ᵀ(y - Xβ)
展开后得到:
S(β) = yᵀy - 2βᵀXᵀy + βᵀXᵀXβ
对 β 求梯度并令其为零:
∂S/∂β = -2Xᵀy + 2XᵀXβ = 0
整理得到正规方程:
XᵀXβ = Xᵀy
如果 XᵀX 可逆,解就是:
β = (XᵀX)⁻¹Xᵀy
这就是最小二乘法最核心的公式。整个推导过程只用了矩阵求导和二次函数极值两个工具,非常干净。
2.3 统计视角:为什么它是最优的
从统计角度看,如果误差 ε 满足均值为零、方差为 σ²、且相互独立,那么最小二乘估计 β 具有几个优良性质:
- 无偏性:E(β) = 真实参数,估计不会系统性偏高或偏低。
- 最小方差:在所有线性无偏估计中,最小二乘估计的方差最小,这就是著名的高斯-马尔可夫定理。
- 与极大似然等价:当误差服从正态分布时,最小化平方和等价于最大化似然函数。
这三个性质解释了为什么最小二乘法能成为工程和统计领域的默认选择。它不是随便挑的一个损失函数,而是在一组合理假设下被数学证明为最优的方案。
2.4 与极大似然估计的推导联系
很多人好奇最小二乘和正态分布到底怎么扯上关系。推导其实很直接。假设每个观测误差独立且服从 N(0, σ²),那么似然函数为:
L(β) = ∏ (1/(√(2π)σ)) exp(-(yᵢ - xᵢβ)²/(2σ²))
取对数后:
ln L = -n/2 ln(2πσ²) - (1/(2σ²)) Σ(yᵢ - xᵢβ)²
要让似然最大,就要让最后那个求和项最小,而它正好就是残差平方和。所以在正态误差假设下,最小二乘就是极大似然。这也解释了为什么最小二乘对离群点敏感——正态分布的尾部很薄,它天然不认为会出现极端偏差,所以一旦出现就会被过度惩罚。
3. 公式推导:一步步手推正规方程
3.1 一元线性回归的完整推导
先从最简单的一元情况入手,模型是 y = a + bx。残差平方和:
S(a, b) = Σ(yᵢ - a - bxᵢ)²
分别对 a 和 b 求偏导并令其为零:
∂S/∂a = -2Σ(yᵢ - a - bxᵢ) = 0
∂S/∂b = -2Σxᵢ(yᵢ - a - bxᵢ) = 0
由第一个式子得到:
a = ȳ - b x̄
把它代入第二个式子,经过整理可以得到斜率:
b = Σ(xᵢ - x̄)(yᵢ - ȳ) / Σ(xᵢ - x̄)²
这个公式非常实用,分子是协方差的未归一化形式,分母是 x 的方差未归一化形式。我在做快速估算时经常直接用它,不需要构建矩阵。
3.2 多元线性回归的矩阵推导
推广到多元情况,模型是 y = Xβ。前面已经推过,核心结果是:
β = (XᵀX)⁻¹Xᵀy
这里要特别注意 X 的构造。如果模型带截距项,X 的第一列必须全是 1。我见过不少初学者忘记加这一列,结果拟合出来的线强行过原点,误差大得离谱。
设计矩阵 X 的结构如下:
| 样本 | 截距列 | 特征1 | 特征2 | ... | 特征p |
|---|---|---|---|---|---|
| 1 | 1 | x₁₁ | x₁₂ | ... | x₁ₚ |
| 2 | 1 | x₂₁ | x₂₂ | ... | x₂ₚ |
| ... | ... | ... | ... | ... | ... |
3.3 正规方程的数值陷阱
理论上 β = (XᵀX)⁻¹Xᵀy 完美,但直接求逆在数值上非常危险。原因有两个:
- 条件数放大:XᵀX 的条件数是 X 条件数的平方,本来就不良的问题会变得更糟。
- 显式求逆不稳定:矩阵求逆的浮点误差在病态情况下会被急剧放大。
实际工程中我从来不直接求逆,而是用以下方法之一:
- QR分解:把 X 分解为正交矩阵 Q 和上三角矩阵 R,然后解 Rβ = Qᵀy。数值稳定性最好。
- SVD分解:奇异值分解,对病态问题最鲁棒,还能顺便给出伪逆解。
- Cholesky分解:当 XᵀX 正定时可用,速度比QR快,但稳定性略差。
提示:如果你用 numpy,直接调用 np.linalg.lstsq 就行,它内部用的是SVD,稳定性和精度都有保障,不要自己写求逆。
3.4 从平方和到卡方分布的延伸
在统计推断里,残差平方和除以真实方差后服从卡方分布,自由度是 n - p(样本数减参数个数)。这个结论是构造置信区间和做假设检验的基础。推导思路是:残差是观测值在正交补空间上的投影,而正态向量在正交变换下仍然是正态向量,其平方和自然服从卡方分布。理解了这一层,你就能明白为什么回归分析里总要看自由度,以及为什么参数越多残差平方和的期望越小。
4. 实操过程:从零实现最小二乘拟合
4.1 环境准备与数据构造
我用 Python 演示,依赖只有 numpy 和 matplotlib。先构造一组带噪声的数据:
import numpy as np import matplotlib.pyplot as plt np.random.seed(42) n = 50 x = np.linspace(0, 10, n) true_a, true_b = 2.5, 1.8 noise = np.random.normal(0, 1.5, n) y = true_a + true_b * x + noise这里真实截距是2.5,真实斜率是1.8,噪声标准差1.5。构造数据时固定随机种子,方便复现结果,这是做实验的基本习惯。
4.2 手写正规方程求解
先按公式手写一遍,理解每一步:
X = np.column_stack([np.ones(n), x]) XtX = X.T @ X Xty = X.T @ y beta = np.linalg.solve(XtX, Xty) print("截距:", beta[0], "斜率:", beta[1])注意我用的是 np.linalg.solve 而不是求逆,这样数值上更稳。运行后你会看到截距接近2.5,斜率接近1.8,说明拟合成功。
4.3 用lstsq做对比验证
再用官方稳定实现跑一遍:
beta_lstsq, residuals, rank, sv = np.linalg.lstsq(X, y, rcond=None) print("lstsq截距:", beta_lstsq[0], "斜率:", beta_lstsq[1])两次结果应该几乎一致。如果差异明显,说明你的矩阵条件数可能有问题,需要检查特征是否共线。
4.4 拟合效果可视化与残差分析
y_pred = X @ beta plt.scatter(x, y, label="观测点") plt.plot(x, y_pred, color="red", label="拟合线") plt.legend() plt.show() residuals = y - y_pred print("残差均值:", residuals.mean()) print("残差标准差:", residuals.std())残差均值应该接近零,残差图不应该呈现明显的喇叭形或弯曲趋势。如果残差随 x 增大而扩散,说明存在异方差,普通最小二乘的标准误估计会失真。
4.5 多项式拟合的扩展
最小二乘不只做直线。把 X 扩展成多项式特征即可:
degree = 3 X_poly = np.column_stack([x**i for i in range(degree + 1)]) beta_poly = np.linalg.lstsq(X_poly, y, rcond=None)[0]但要注意,高阶多项式会导致特征之间高度相关,XᵀX 条件数急剧上升。我一般会先对 x 做标准化,或者改用正交多项式基,否则数值结果会很难看。
5. 常见问题与排查技巧实录
5.1 拟合结果完全不对怎么排查
这是新手最常遇到的问题。我整理了一个排查顺序表:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 拟合线过原点 | 忘记加截距列 | 检查X第一列是否全为1 |
| 系数巨大且不稳定 | 特征共线或未标准化 | 计算条件数,做标准化 |
| 残差有明显规律 | 模型形式不对 | 画残差图,考虑非线性项 |
| 结果每次运行都变 | 数据未固定随机种子 | 设置seed |
| 预测值偏离极远 | 外推超出训练范围 | 限制预测区间 |
5.2 多重共线性的识别与处理
多重共线性是最小二乘最隐蔽的敌人。识别方法是计算方差膨胀因子(VIF),如果某个特征的VIF超过10,说明它和其他特征高度相关。处理方法有:
- 删除冗余特征
- 使用主成分回归
- 引入岭回归,在正规方程里加一个 λI 项
岭回归的解是 β = (XᵀX + λI)⁻¹Xᵀy,这个小小的 λI 能让矩阵从接近奇异变得良态,代价是引入一点偏差。我在特征多、样本少的项目里几乎默认用岭回归。
5.3 数值精度问题的实战经验
有一次我处理一组量纲差异极大的数据,一个特征范围是0到1,另一个是0到一百万,结果拟合系数完全乱套。原因是 XᵀX 的条件数被量纲差异撑到了天文数字。解决办法很简单:标准化。把每个特征减去均值再除以标准差,拟合完再把系数变换回原始尺度。这个操作我后来做成了标准流程,几乎每个项目都会先跑一遍。
5.4 加权最小二乘的使用时机
当不同观测点的可信度不同时,普通最小二乘一视同仁就不合理了。加权最小二乘给每个点一个权重 wᵢ,目标函数变成 Σwᵢ(yᵢ - xᵢβ)²。解的形式是:
β = (XᵀWX)⁻¹XᵀWy
其中 W 是对角权重矩阵。权重通常取观测方差的倒数,方差越大的点权重越小。这个技巧在处理传感器融合数据时特别有用,因为不同传感器的精度本来就不一样。
5.5 最小二乘与RANSAC的配合
数据里混入少量严重离群点时,最小二乘会被带偏。我的做法是先用RANSAC随机采样出一批内点,再用最小二乘在这批内点上做精细拟合。RANSAC负责抗噪,最小二乘负责精度,两者配合效果很好。这个组合在计算机视觉的直线检测、平面拟合里是标准套路。
6. 影响范围与延伸应用
6.1 在机器学习中的位置
线性回归就是最小二乘的直接应用。逻辑回归虽然用的是交叉熵损失,但在某些近似下也和最小二乘有联系。神经网络训练用的梯度下降,本质上是在做非线性最小二乘的迭代求解。理解了最小二乘,你就理解了损失函数、优化、正则化这一整条线的起点。
6.2 在信号处理与控制系统中的应用
系统辨识里,最小二乘用来估计传递函数参数。卡尔曼滤波的更新步骤,在特定条件下也等价于递推最小二乘。我在做传感器标定时,用最小二乘拟合温度补偿曲线,把测量误差从百分之几降到了千分之几,效果立竿见影。
6.3 在计算机视觉中的延伸
相机标定、单应矩阵估计、光流计算,背后都有最小二乘的影子。比如拟合椭圆、直线检测,都是先建立残差方程,再用最小二乘求解。当问题变成非线性时,就用高斯-牛顿或列文伯格-马夸尔特方法迭代求解,而每一步迭代内部仍然是一个线性最小二乘问题。
6.4 从最小二乘到正则化的演进
普通最小二乘在特征多的时候会过拟合。岭回归加了L2正则,Lasso加了L1正则,弹性网把两者结合。这些方法的共同点是在残差平方和后面加一个惩罚项,本质上是给最小二乘加了一个先验约束。理解了最小二乘,这些正则化方法就都是自然的延伸,而不是孤立的技巧。
6.5 暴力枚举与解析解的对比思考
有人会问,既然参数空间不大,为什么不直接暴力枚举所有可能的参数组合,选平方和最小的那个?理论上可行,但维度一高就彻底不可行。假设有10个参数,每个参数枚举100个值,总组合是100的10次方,天文数字。而解析解一步到位,这就是数学推导的价值。暴力枚举可以作为验证手段,用来检查解析解是否正确,但绝不能作为主算法。
7. 实操心得与避坑清单
7.1 我踩过的三个典型坑
第一个坑是忘记加截距列,导致拟合线强行过原点,误差大得离谱,排查了半天才发现是设计矩阵的问题。第二个坑是特征未标准化,量纲差异导致系数完全不可解释,后来养成习惯,拟合前先做标准化。第三个坑是直接用求逆解正规方程,遇到病态矩阵时结果完全不可信,换成lstsq之后问题消失。这三个坑现在都写进了我的检查清单。
7.2 参数选择的经验法则
- 样本数至少是参数个数的10倍,否则过拟合风险很高。
- 多项式阶数不要超过5,再高就该考虑其他模型形式了。
- 条件数超过1000就要警惕,超过10000基本可以判定病态。
- 残差标准差应该和观测噪声量级相当,差太多说明模型有问题。
7.3 验证拟合质量的实用方法
除了看R²,我更看重残差图。R²高不代表模型对,残差图有规律就说明模型漏掉了某些结构。另外做交叉验证,把数据分成训练集和测试集,看测试集上的预测误差,这比单看训练集R²可靠得多。我一般还会留一部分数据做外推测试,看看模型在训练范围之外的稳定性。
7.4 代码复用的封装建议
把最小二乘拟合封装成一个函数,输入X和y,输出系数、残差、R²、条件数。这样每次用的时候直接调用,不用重复写。我自己的工具函数里还加了自动标准化和自动加截距列的选项,减少人为失误。封装的时候记得把中间量也返回出来,方便排查问题。
7.5 什么时候不该用最小二乘
如果误差不满足独立同分布假设,或者数据里离群点比例超过10%,或者特征严重共线且无法通过正则化解决,那就该考虑其他方法了。最小二乘是强大的默认选项,但不是万能钥匙。判断标准很简单:先跑一遍最小二乘,看残差图,如果残差有结构或者被离群点主导,就换方法。
最后分享一个我常用的快速验证技巧:拿到任何一组数据,先画散点图,再跑最小二乘,再画残差图,这三步走完,数据的基本结构和你模型的问题基本就暴露得差不多了。这个流程我用了很多年,几乎没失手过。