做工程师这些年,我有个感受越来越深:数据拿到手只是第一步,能从数据里把规律挖出来才是真本事。最小二乘法,就是那个被反复使用、却往往被忽视的“挖规律利器”。不管是传感器标定、实验曲线拟合,还是设备参数辨识、异常检测,最小二乘几乎无处不在——它用一套简洁的数学语言,告诉你怎么在杂乱的数据里找到一条“最优”的拟合线。这篇文章从工程师的实际需求出发,把最小二乘法的原理、矩阵公式、代码实现和工程经验一次讲透。适合刚转算法岗的工程师、做数据分析的实验人员,以及所有想系统掌握工程常用算法的朋友。
1. 最小二乘法的本质:先从“量绳子”讲起
1.1 最小二乘其实每天都在用
先想一个最接地气的问题:给你一根绳子,拿尺子量了10次,读数分别是99.8、100.1、99.9、100.3……怎么报出最可信的长度?大多数人第一反应是取平均。这个“平均值”看起来毫无技术含量,但它恰恰是最小二乘法在常数模型下的最优解——当拟合函数是 y = b(一条水平线)时,最小化 Σ(yᵢ - b)² 对 b 求导并令其为零,解出来的 b 恰好就是算术平均。
我最初接触最小二乘,是在做传感器标定的项目里:一堆散点看着有明显线性趋势,但手画太主观,两点连线又浪费了中间所有数据点。最小二乘要解决的就是这类问题:给定一组样本点 (xᵢ, yᵢ),找一个函数 y = f(x),让预测值和真实观测值之间的误差平方和最小,即:
min Σ (yᵢ - f(xᵢ))²
这里的 f(x) 具体形式取决于你对问题的物理认识——可能是直线、多项式,也可能是指数函数。最小二乘提供了“如何确定系数”的完整方法论:误差平方和对系数求偏导,令所有偏导数为零,解方程组得到最优系数。这个看似普通的操作,是后面所有线性回归、曲线拟合、参数辨识算法共同的地基。
1.2 为什么偏偏是“平方”而不是绝对值
刚开始学的时候我也疑惑:误差有大有小,为什么不用 Σ|yᵢ - f(xᵢ)|,也就是让绝对误差之和最小?绝对值更直观,而且对异常点不敏感。但深入想会发现平方有三个不可替代的优势。
第一是数学处理的便利性。平方函数光滑、处处可导,求最小值只需要算梯度并令其为零;绝对值函数在零点处不可导,每项都带分段符号,求解析解非常困难,工程上几乎没法直接用。
第二是误差的“惩罚权重”不同。平方相当于给大误差“加重罚”——一个偏离很远的点,误差平方会大幅上升,优化算法会优先把它拉回来。这听起来像缺点(对异常点敏感),但在噪声服从正态分布的前提下,这恰恰是统计意义上的最优行为。第三点更根本:如果测量噪声是独立同分布的高斯白噪声,那么最小二乘解正好等于最大似然估计解。换句话说,在绝大多数真实工程场景中,噪声的统计特性决定了平方是最合理的选择。
这里可以类比裁判打分:小偏差是轻微扣分,大偏差是严重犯规,平方恰好实现了这种“非线性打分”。只用绝对值的话,偏离5个单位和偏离50个单位的数据点,惩罚只是10倍差距;用平方就成了100倍差距,极端错误会被重点优化——这正是工程上想要的行为。
1.3 适用边界
最小二乘不是万能的。它假设误差主要出现在因变量 y 上,自变量 x 被认为是精确已知的;假设残差均值为零,且不随 x 系统变化。如果传感器在 x 方向也有明显误差,或者数据里存在明显的系统偏差、异方差(不同 x 区间的噪声幅度不同),直接用最小二乘就会得到有偏结果。这些边界条件在理论学习时容易被忽略,在工程使用中却直接决定拟合结果能不能信。
2. 线性最小二乘的数学原理:矩阵法五分钟上手
2.1 一元线性拟合的显式公式
先看最常用的一元线性拟合:y = kx + b。目标函数是:
J(k, b) = Σ (yᵢ - kxᵢ - b)²
分别对 k 和 b 求偏导,令其为零,得到两个方程:
∂J/∂b = -2Σ(yᵢ - kxᵢ - b) = 0
∂J/∂k = -2Σxᵢ(yᵢ - kxᵢ - b) = 0
整理之后就是教科书上常见的二元一次方程组。解出来的斜率和截距可以写成更直观的形式:
k = Σ(xᵢ - x̄)(yᵢ - ȳ) / Σ(xᵢ - x̄)²
b = ȳ - k·x̄
其中 x̄ 和 ȳ 分别是 x 和 y 的平均值。注意看这个 k 的表达式——分子是 x 和 y 的协方差,分母是 x 的方差,所以拟合直线的斜率本质上是 x 和 y 之间“协同变化强度”的归一化度量。这个理解比死记公式有用得多,后面做特征工程时也经常用这个含义来判断两个变量是否值得建模。
2.2 矩阵化的正规方程
当拟合函数是多个参数的线性组合时,比如 y = β₀ + β₁x₁ + β₂x₂,或者 y = β₀ + β₁x + β₂x²,再写偏导方程就不优雅了。把所有样本写成矩阵形式:
y = Xβ
其中 X 是 n×p 的设计矩阵(n 个样本,p 个参数对应的基函数列),β 是待求的 p 维系数向量。目标函数写成:
J(β) = ‖y - Xβ‖² = (y - Xβ)ᵀ(y - Xβ)
对 β 求梯度并令其为零,就得到著名的正规方程:
XᵀXβ = Xᵀy
β = (XᵀX)⁻¹Xᵀy
理解这个式子的直观角度:Xᵀy 可以理解为每个基函数与观测数据之间的“对齐程度”,XᵀX 描述的是各基函数之间的“重叠程度”,求逆就是把这个重叠解耦掉,再左乘回去就得到相互独立的系数。这个视角在多个特征高度相关时尤其重要——如果 XᵀX 接近奇异,说明某些基函数几乎可以互相表达,解出来的系数就不可靠了。
2.3 加权最小二乘的实际意义
标准最小二乘默认所有样本点的“话语权”相同。但工程测量里每个点的可信度常常不一样:靠近量程末端的传感器精度差,某些实验条件下的重复测量方差大,或者数据本身带已知的不确定度。这时候就要用加权最小二乘(WLS):
J(β) = Σ wᵢ(yᵢ - f(xᵢ))²
β = (XᵀWX)⁻¹XᵀWy
W 是对角矩阵,对角线上的 wᵢ 是每个点的权重。权重的取值可以直接用测量不确定度的倒数平方,也可以用重复测量的方差倒数。我在校准温度传感器时就用过这个方法——低温段数据分散,高温段数据集中,给低温段降权之后,拟合出的曲线在全量程上明显更稳,不会为了迁就那几个噪声大的低温点而牺牲主工作区间的精度。
3. 动手实现:用 NumPy 手写最小二乘拟合
3.1 生成一份带噪声的测量数据
理论说再多,不如跑一段代码。用 NumPy 模拟一个典型的工程场景:物理规律是线性的,但测量过程叠加了随机噪声。以下代码生成 50 个样本点,真实关系是 y = 2.5x + 1.3,噪声标准差设为 1.5:
import numpy as np import matplotlib.pyplot as plt np.random.seed(42) x = np.linspace(0, 10, 50) true_k, true_b = 2.5, 1.3 y_true = true_k * x + true_b y = y_true + np.random.normal(0, 1.5, size=x.shape) plt.scatter(x, y, s=20, label='观测数据') plt.plot(x, y_true, 'r--', label='真实规律') plt.xlabel('x') plt.ylabel('y') plt.legend() plt.show()噪声标准差 1.5 相对斜率 2.5 来说不算小,肉眼已经能看出散点有波动,但整体趋势仍然清晰。这种信噪比水平在真实工程采集里非常典型。
3.2 三种实现方式与对比
同一份数据,至少有三条路可以走到答案:np.polyfit 一行搞定;np.linalg.lstsq 用 SVD 求解,数值最稳;手写正规方程则让你彻底理解内部原理。
# 方法一:polyfit 快速拟合 k1, b1 = np.polyfit(x, y, 1) # 方法二:lstsq 求解(推荐) A = np.vstack([x, np.ones_like(x)]).T result = np.linalg.lstsq(A, y, rcond=None) k2, b2 = result[0] # 方法三:手写正规方程 X = np.vstack([x, np.ones_like(x)]).T beta = np.linalg.inv(X.T @ X) @ X.T @ y k3, b3 = beta print(k1, b1) # 约 2.53, 1.21 print(k2, b2) # 约 2.53, 1.21 print(k3, b3) # 约 2.53, 1.21三种方法的结果完全一致。polyfit 适合快速验证思路;lstsq 基于 SVD 分解,可以处理 XᵀX 奇异或接近奇异的病态问题,是实际项目里的首选;手写正规方程只适合教学和理解原理,我强烈不建议在生产代码里直接算 XᵀX 的逆矩阵。理由后面第 5 章会详细展开。
用拟合结果画图对比真实曲线,你会发现拟合线和真实规律非常接近,但又不完全重合——这就是“从带噪数据中估计规律”的典型结果:我们永远无法恢复真实规律,只能得到一个对它很好的近似。
3.3 拟合质量怎么评估:R² 与残差
拟合完不能只看“长得像不像”,要有量化指标。最常用的是决定系数 R²:
y_pred = k2 * x + b2 ss_res = np.sum((y - y_pred) ** 2) ss_tot = np.sum((y - np.mean(y)) ** 2) r2 = 1 - ss_res / ss_tot print(f"R² = {r2:.4f}") # 约 0.92R² 的含义是“模型解释了数据总方差的百分比”,0.92 表示这条直线解释了 92% 的数据波动,剩下 8% 是噪声。但单独看 R² 会骗人——高次多项式能把 R² 推到 0.99 以上,却可能严重过拟合。所以必须同时看残差图:把 (x, y - y_pred) 画出来,如果残差围绕 0 随机上下波动,说明模型形式合理;如果残差呈现 U 形或漏斗形,说明模型形式不对或噪声异方差。
我在实际项目里的习惯是:先看残差分布形态,再看 R²,最后才下结论。R² 只是总分,残差图才是体检报告。
4. 工程实战:最小二乘的典型应用场景
4.1 传感器标定:多点拟合取代两点校准
传感器标定是最小二乘最经典的工程应用,没有之一。一套电压采集系统里,AD 采样值 code 和真实物理量 V 之间大致是线性关系 V = a·code + b,但因为有温漂、运放偏置、非线性误差,两点校准经常不够准。我做过的一个项目里,某温度传感器的实测曲线在低端和高端各有不同程度偏移,用两点法校准后中间段误差达到了 0.8℃,而用 11 个标准温度点做最小二乘拟合,全量程误差压到了 0.15℃ 以内。
操作流程很固定:选定标准源/标准器具,覆盖你要工作的整个量程区间,测 k 个标准点,记录对应的 AD 值;然后以标准值为 x、AD 值为 y(或者反过来)做一次或二次多项式拟合;把系数存进 Flash/参数区,运行时直接用公式换算。关键细节是标定点必须覆盖完整工作区间,不要只集中在量程一头,否则外推部分会严重失真。
4.2 实验数据的趋势提取:从噪声里找漂移
实验测试里经常碰到这种情况:测芯片在恒温条件下的输出电压随时间的变化,想判断它有没有漂移,但数据叠加了高频噪声,肉眼根本看不清趋势。最小二乘拟合一条趋势线 y = kx + b,让数据说话:斜率 k 就是漂移速率,单位是 mV/h 或者 ppm/千小时,可以拿来对比不同批次、不同条件下的稳定性。
这个场景里最小二乘的独特优势是它给出的是定量描述,而不是“看起来还行”。配合置信区间计算,还能给漂移速率加上误差带。曾在某老化实验里,我用拟合斜率 0.032 mV/h 对比了三个批次的样品,快速锁定了一个工艺流程异常的批次——单看原始波形完全看不出差异,但斜率差异已经超出 3 个标准差。
4.3 参数辨识:一阶系统的时间常数
做控制系统或电路分析的工程师,经常要辨识系统的动态参数。比如一阶系统的阶跃响应:y(t) = K(1 - e^(-t/τ)),想从阶跃实验数据里估计增益 K 和时间常数 τ。这个模型关于 τ 是非线性的,不能直接套线性最小二乘,但可以对数据做变换:
z = ln(1 - y/K)
如果 K 已知,z 对 t 就是严格的线性关系 z = -t/τ,斜率是 -1/τ,直接线性拟合即可。K 可以用实验数据的稳态平均值估计,然后用最小二乘估计 -1/τ。注意取对数之后,原来噪声大的点(y 接近 K 时)会在对数域被放大,所以实际操作中要略掉尾部 10%~20% 的数据再拟合,这样结果才稳。
这个方法同样适用于指数衰减模型 y = A·e^(-λt):两端取对数变成 ln y = ln A - λt,一条直线就把 λ 和 A 都估计出来了。对数线性化是工程上把非线性问题拉回最小二乘框架最常用的手段。
4.4 残差分析用于异常检测
拟合之后那一串残差 eᵢ = yᵢ - ŷᵢ,不只是用来画图的,它本身就是一种检测手段。设备状态监测里,先对正常运行的数据建立基线拟合模型,然后不断计算新数据点的残差;残差超过 3σ(σ 是基线残差的标准差)就判定为异常。某旋转机械振动监测项目里,我用一个简单的二次多项式拟合振动幅值与转速的关系,靠残差阈值成功识别出了轴承早期故障——振动幅值本身没有越限,但残差突然跳变远超阈值。
这个思路的关键在于:模型残差里既包含随机噪声,也包含“模型解释不了的东西”。正常运行状态下残差稳定在小范围,一旦出现新的物理过程(比如松动、碰撞、泄漏),残差就会系统性增大。最小二乘在这里的角色是“背景建模器”,并不需要模型完美,只需要背景足够稳定。
5. 避坑指南:异常值、过拟合与病态矩阵
5.1 离群点如何毁掉拟合结果
最小二乘对离群点极其敏感,前面说过原因——平方惩罚放大了大误差的影响。一个极端坏点完全可以把拟合线拉偏 20% 以上。真实数据里离群点太常见了:传感器毛刺、通信误码、人工录入错误、实验条件突变。我踩过一个典型的坑:做压力传感器标定时,某一次读数的数据线接触不良,混进一个数值离谱的点,拟合结果整体偏移,导致标定后压力读数系统性偏低。
处理离群点的工程做法分几层。最轻量的是“拟合-剔除-再拟合”:先做一次最小二乘,计算残差和标准差,剔除 |e| > 2.5σ 的点,再用剩余数据重新拟合,循环一两轮就够。更稳健的做法是用 RANSAC 或 Huber 损失,它们本质上是通过策略或损失函数来减小离群点的权重。最简单也最容易被忽视的是:剔除异常点要有依据——超量程、超物理约束、重复测量矛盾,这些理由写进报告里才站得住脚,不能为了 R² 好看而随意删数据。
5.2 过拟合:阶数越高不代表越好
多项式拟合的阶数选择堪称新手重灾区。阶数越高,训练数据上的误差越小,R² 接近 1,但拟合曲线开始剧烈震荡,尤其在数据范围两端更是疯摆。9 阶多项式穿过 10 个数据点几乎毫无压力,但在两个数据点之间可能画出陡峭的波浪——这就是过拟合,模型学到的是噪声而不是规律。
工程上的实操建议很明确:优先根据物理背景确定模型形式,而不是靠增大阶数去“蹭”数据。不知道物理背景时,用交叉验证或者留一法评估不同阶数的泛化误差;一个保守的经验是数据量至少是参数个数的 2~3 倍以上,拟合结果才相对可信。我在某个热敏电阻标定项目里对比过二次和三次多项式,三次的 R² 只提高 0.001,但在区间的外推部分偏差反而更大——这说明多出来的参数只是在拟合噪声,没有带来实际价值。
5.3 病态矩阵与特征缩放
手写正规方程 β = (XᵀX)⁻¹Xᵀy 时,最怕遇到 XᵀX 接近奇异。两种情况常见:一是特征尺度差异极大,比如一个特征取值范围 0~1,另一个 0~1e6,XᵀX 对角线元素差了好几个数量级;二是特征高度相关,比如用 x 和 x² 拟合,当 x 集中在很小范围内时,x 和 x² 几乎线性相关,正规方程解出来的系数数值巨大、正负交替、极不稳定。
典型表现是:拟合系数一会儿是几万,一会儿是负几万,换一个数据点结果就完全翻盘。解决办法有几个:对特征做归一化/标准化,让每个特征落在同一尺度;求解时用 np.linalg.lstsq 或者 np.linalg.svd,避免直接求逆;在 XᵀX 对角线加上一个小正则项 λI,这就是岭回归的思想,既能改善数值条件,又能抑制过拟合。工程上我默认的做法是:能不用逆矩阵就不用逆矩阵,统一走 lstsq。
5.4 非线性怎么办
非线性模型分两种处理策略:能线性化就线性化,不能线性化就迭代。线性化的思路在第 4.3 节已经演示过——取对数、取倒数、变量替换,把非线性关系转化为关于参数的线性关系。这种方法简单、快速、可解释性强,缺点是在变换后的空间做最小二乘,相当于隐式改变了误差权重。
无法线性化的模型用迭代法。主流是高斯-牛顿法和 Levenberg-Marquardt 算法(LM),LM 本质上是高斯牛顿和梯度下降的折中,通过阻尼因子动态调节步长。工程上不必自己实现,直接用 scipy.optimize.curve_fit:
from scipy.optimize import curve_fit def model(t, K, tau): return K * (1 - np.exp(-t / tau)) t_data = np.linspace(0, 10, 200) y_data = model(t_data, 3.0, 2.0) + np.random.normal(0, 0.05, size=t_data.shape) popt, pcov = curve_fit(model, t_data, y_data, p0=[2, 1]) K_est, tau_est = popt print(f"K = {K_est:.3f}, tau = {tau_est:.3f}") perr = np.sqrt(np.diag(pcov)) print(f"估计误差: K±{perr[0]:.4f}, tau±{perr[1]:.4f}")curve_fit 的核心就是最小二乘——它通过迭代算法让非线性模型的误差平方和最小化。注意 p0 初始值对结果影响很大,初值给得太离谱,迭代会收敛到局部最优。我的习惯是先根据物理量级估算一个合理的初值区间,或者用网格搜索多个起点,挑拟合误差最小的那一组参数。
6. 扩展方向:最小二乘与现代算法的关系
6.1 岭回归与 LASSO:加了约束的最小二乘
机器学习里最常见的线性回归,本质就是最小二乘;普通最小二乘在实际数据上容易过拟合或数值不稳定,于是有了加正则项的变体。岭回归在目标函数里加一个 L2 惩罚:
J(β) = ‖y - Xβ‖² + λ‖β‖²
对应 β = (XᵀX + λI)⁻¹Xᵀy,很简单——就是在 XᵀX 对角线上加同一个数,既改善了条件数,又把系数压小。LASSO 则是 L1 惩罚,效果是让一部分系数精确变为零,达到特征选择的目的。从最小二乘到岭回归到 LASSO,本质是在拟合精度和解的稳定性之间做权衡,理解了最小二乘的病态性问题,再看这些变体就顺理成章了。
6.2 最小二乘在系统辨识中的地位
再往远处看,最小二乘的思想渗透在系统辨识、卡尔曼滤波、自适应滤波等一系列领域里。卡尔曼滤波每个更新周期做的事情,本质上就是最小化预测误差的平方和;AR 模型的参数估计也是直接用最小二乘或者它的递推形式。可以说,最小二乘是工程师从“数据处理”走向“系统建模”的第一块跳板,重要性不亚于傅里叶变换在信号处理里的地位。
我个人在实际操作中的体会是:最小二乘永远值得花时间吃透,因为它是那种看起来简单、用起来无穷的工具。做传感器标定和实验数据分析这些年,我靠这一招解决了一大半拟合问题,剩下的也基本是靠它的变体或者扩展解决的。最后再分享一个小技巧:拟合完一定要打印残差分布,不要只看 R²。R² 是成绩单,残差是体检报告——成绩单好看但体检有问题,迟早要出事故。