最小二乘法原理与实战:从线性拟合到系统辨识的完整指南
1. 从“差不多”到“刚刚好”理解最小二乘法的灵魂做数据分析、搞工程建模甚至是处理实验数据我们总会遇到一个绕不开的问题手里有一堆散乱的数据点怎么才能找到一条最合适的曲线来描述它们背后的规律你可能会说画条线穿过去让点尽量靠近这条线不就行了这话没错但“尽量靠近”这个词太模糊了。是让所有点到线的垂直距离之和最小还是水平距离或者别的什么距离不同的定义会得到完全不同的“最佳”曲线。而最小二乘法就是给“最佳拟合”这个模糊概念一个清晰、普适且数学上极其优美的定义它追求的是所有数据点到拟合曲线的垂直距离的平方和达到最小。这个看似简单的准则自高斯和勒让德时代提出以来已经成为了科学和工程领域数据建模的基石。今天我们就抛开复杂的公式推导从根子上聊聊这个“二乘”到底乘了什么以及我们该如何用好它。2. 核心思想拆解为什么是“平方”和“最小”2.1 误差的度量从绝对值到平方的跃迁当我们用一条曲线 $y f(x)$ 去拟合数据点 $(x_i, y_i)$ 时每个点都会产生一个误差 $e_i y_i - f(x_i)$。最直观的想法是让所有误差的总和 $\sum |e_i|$ 最小即最小一乘法。这很符合直觉但它在数学上有个麻烦绝对值函数在零点不可导。不可导意味着我们很难用那些强大的微积分工具去寻找最优解计算会变得复杂。最小二乘法巧妙地选择了误差的平方 $e_i^2$ 作为度量。平方运算有两大好处可导性平方函数处处可导这使得我们可以通过求导数等于零即求极值点这个标准方法来寻找最优拟合参数。这是其计算可行性的核心。放大大误差平方运算会放大较大的误差。如果一个点的误差是2另一个是10在绝对值和中它们贡献12但在平方和中2²410²100大误差的权重被显著放大。这意味着最小二乘拟合会更倾向于惩罚那些偏离很远的点从而迫使拟合曲线不能为了照顾多数点而过分忽视少数异常点拟合结果通常对整体趋势的捕捉更稳健。注意这种对大误差的敏感性是一把双刃剑。它使得拟合对异常值非常敏感。一个明显的错误数据点野值可能会把整个拟合线“拉偏”。因此在实际应用最小二乘法前数据清洗和异常值检测至关重要。2.2 “最小”的几何与代数意义从几何角度看我们是在所有可能的曲线构成的“空间”里寻找一条到给定数据点“距离”最近的曲线。这里“距离”被定义为所有数据点在y轴方向上的偏差的平方和。最小二乘解就是这个空间中的“垂足”。从代数角度看当我们把拟合模型比如线性模型 $y ax b$代入所有数据点会得到一个方程组。通常数据点数量多于待求参数方程数多于未知数这是一个超定方程组一般无精确解。最小二乘法的目标就是寻找一组参数 $(a, b)$使得方程组左右两边的差异即残差的平方和最小从而求出一个在最小平方误差意义下的最优近似解。3. 从直线到曲线拟合模型的构建3.1 线性最小二乘一切的起点我们以最经典的线性拟合 $y ax b$ 为例透彻理解整个过程。设有n个数据点 $(x_i, y_i)$目标是找到 $a$ 和 $b$使损失函数 $L \sum_{i1}^{n} [y_i - (a x_i b)]^2$ 最小。这是一个关于 $a$ 和 $b$ 的二元二次函数求极小值问题。我们分别对 $a$ 和 $b$ 求偏导数并令其等于零$$ \begin{aligned} \frac{\partial L}{\partial a} -2 \sum_{i1}^{n} [y_i - (a x_i b)] x_i 0 \ \frac{\partial L}{\partial b} -2 \sum_{i1}^{n} [y_i - (a x_i b)] 0 \end{aligned} $$整理后得到著名的正规方程组$$ \begin{aligned} (\sum x_i^2) a (\sum x_i) b \sum x_i y_i \ (\sum x_i) a n b \sum y_i \end{aligned} $$解这个二元一次方程组即可得到 $a$ 和 $b$ 的解析解$$ \begin{aligned} a \frac{n \sum x_i y_i - \sum x_i \sum y_i}{n \sum x_i^2 - (\sum x_i)^2} \ b \frac{\sum y_i \sum x_i^2 - \sum x_i \sum x_i y_i}{n \sum x_i^2 - (\sum x_i)^2} \end{aligned} $$实操心得自己推导并编程实现一次这个公式比调用十次np.polyfit或lm()函数理解得更深刻。你会注意到分母 $n \sum x_i^2 - (\sum x_i)^2$这其实就是 $n$ 倍 $x$ 的方差。当 $x$ 值全部相同时方差为零分母为零公式失效——这对应着所有数据点垂直排列显然无法确定一条有斜率的直线。3.2 非线性关系的线性化化曲为直的智慧很多物理、生物、经济现象并非线性关系但我们可以通过变量替换将其转化为线性模型处理。多项式拟合拟合 $y a_0 a_1 x a_2 x^2 ... a_m x^m$。只需令 $X_1 x, X_2 x^2, ..., X_m x^m$原方程就变成了关于新变量 $X_1, X_2, ..., X_m$ 的多元线性模型可以直接套用多元线性最小二乘法。指数拟合拟合 $y A e^{Bx}$。两边取自然对数$\ln y \ln A Bx$。令 $Y \ln y, A \ln A$则变为 $Y A Bx$成为线性模型。幂律拟合拟合 $y A x^B$。两边取对数$\ln y \ln A B \ln x$。令 $Y \ln y, X \ln x, A \ln A$则变为 $Y A B X$。注意事项经过线性化变换后我们是在最小化变换后变量如 $\ln y$的误差平方和而不是原始变量 $y$ 的。这会导致拟合优度是针对变换后的数据而言的。有时这没问题但如果你需要最小化原始数据的误差就需要使用下一节的非线性最小二乘。3.3 非线性最小二乘迭代寻优的战场当模型无法通过变换转为线性形式时如 $y a e^{-bx} c$我们就进入了非线性最小二乘的领域。此时损失函数 $L \sum [y_i - f(x_i; \theta)]^2$ 是关于参数向量 $\theta$ 的复杂非线性函数没有解析解。核心思路是迭代优化初始化给参数 $\theta$ 一个初始猜测值。线性近似在当前参数值 $\theta_k$ 处对模型函数 $f(x;\theta)$ 进行一阶泰勒展开将其近似为一个线性模型。求解增量对这个线性近似模型使用线性最小二乘计算出一个参数增量 $\Delta \theta$。更新参数$\theta_{k1} \theta_k \Delta \theta$。判断收敛如果参数变化或误差下降足够小则停止否则回到第2步。最经典的算法是高斯-牛顿法和列文伯格-马夸尔特法。LM算法是GN法的改进更鲁棒能处理初始值不佳的情况可以说是非线性拟合的实际标准算法。实操心得使用非线性最小二乘时初始值的选择至关重要。一个糟糕的初始值可能导致算法收敛到局部最优解甚至发散。通常需要基于物理意义或通过线性化模型先得到一个粗略估计作为初始值。在代码中如SciPy的curve_fit或 MATLAB的lsqcurvefit务必关注返回的协方差矩阵它反映了参数估计的不确定性。4. 实操演练以MATLAB/Python为例理论说得再多不如动手做一遍。我们用一个简单的例子分别用MATLAB和Python实现线性与非线性拟合并解读关键输出。4.1 线性拟合实战假设我们测量了弹簧在不同负重下的伸长量数据如下 负重 (x): [1, 2, 3, 4, 5, 6] (kg) 伸长 (y): [2.1, 3.8, 6.1, 7.8, 10.2, 11.9] (cm)根据胡克定律这应该是一个线性关系 $y kx b$。Python (NumPy/SciPy) 实现import numpy as np import matplotlib.pyplot as plt from scipy import stats x np.array([1, 2, 3, 4, 5, 6]) y np.array([2.1, 3.8, 6.1, 7.8, 10.2, 11.9]) # 方法1使用np.polyfit进行一阶多项式拟合 k, b np.polyfit(x, y, 1) print(f斜率 k (劲度系数倒数) {k:.4f}) print(f截距 b (初始长度?) {b:.4f}) # 方法2使用stats.linregress它提供更多统计信息 slope, intercept, r_value, p_value, std_err stats.linregress(x, y) print(f斜率: {slope:.4f}, 截距: {intercept:.4f}) print(f相关系数 R: {r_value:.4f}, R^2: {r_value**2:.4f}) print(f斜率的标准误: {std_err:.4f}) # 绘图 y_fit k * x b plt.scatter(x, y, label原始数据) plt.plot(x, y_fit, r-, labelf拟合直线: y {k:.2f}x {b:.2f}) plt.xlabel(负重 (kg)) plt.ylabel(伸长量 (cm)) plt.legend() plt.grid(True) plt.show()关键输出解读k约为 1.98这可能是弹簧劲度系数 $K$ 的倒数因为 $FK\Delta x$这里 $Fmg\approx 10x$所以 $K \approx 10/k \approx 5.05 N/cm$。需要根据你的单位理解其物理意义。b约为 0.13理论上应为0无负重时无伸长这里的微小正值可能是测量系统误差或弹簧初始状态所致。R^2决定系数非常接近1如0.999说明线性模型解释度极高。std_err是斜率估计的标准误差可用于计算置信区间。MATLAB 实现x [1, 2, 3, 4, 5, 6]; y [2.1, 3.8, 6.1, 7.8, 10.2, 11.9]; % 使用 polyfit p polyfit(x, y, 1); % p(1)是斜率p(2)是截距 k p(1); b p(2); fprintf(斜率 k %.4f\n, k); fprintf(截距 b %.4f\n, b); % 计算拟合值和R^2 y_fit polyval(p, x); SS_res sum((y - y_fit).^2); SS_tot sum((y - mean(y)).^2); R2 1 - SS_res / SS_tot; fprintf(R^2 %.4f\n, R2); % 使用 fitlm 获取更详细的统计模型需要Statistics and Machine Learning Toolbox % mdl fitlm(x, y); % disp(mdl)4.2 非线性拟合实战药物浓度衰减假设某药物在体内的浓度随时间呈指数衰减$C(t) C_0 e^{-kt}$。我们测得一组数据 时间 t: [0.5, 1, 2, 3, 4, 6, 8] (小时) 浓度 C: [8.2, 5.7, 3.1, 1.8, 1.1, 0.4, 0.2] (mg/L)Python (SciPy) 实现import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit # 定义模型函数 def model_func(t, C0, k): return C0 * np.exp(-k * t) t_data np.array([0.5, 1, 2, 3, 4, 6, 8]) C_data np.array([8.2, 5.7, 3.1, 1.8, 1.1, 0.4, 0.2]) # 提供初始猜测值至关重要这里从数据粗略估计t0时C约10半衰期约1.5小时 k ~ ln2/1.5 initial_guess [10, 0.46] # 进行非线性最小二乘拟合 params_opt, params_cov curve_fit(model_func, t_data, C_data, p0initial_guess) C0_opt, k_opt params_opt perr np.sqrt(np.diag(params_cov)) # 计算参数的标准误差 print(f拟合参数: C0 {C0_opt:.2f} ± {perr[0]:.2f} mg/L) print(f拟合参数: k {k_opt:.3f} ± {perr[1]:.3f} /小时) print(f药物半衰期 t_{1/2} {np.log(2)/k_opt:.2f} 小时) # 绘图 t_fine np.linspace(0, 9, 100) C_fit model_func(t_fine, C0_opt, k_opt) plt.scatter(t_data, C_data, label实测浓度) plt.plot(t_fine, C_fit, r-, labelf拟合曲线: C(t){C0_opt:.1f}*exp(-{k_opt:.3f}t)) plt.xlabel(时间 (小时)) plt.ylabel(药物浓度 (mg/L)) plt.legend() plt.grid(True) plt.show()关键操作解析curve_fit函数的核心是迭代优化默认使用LM算法。p0initial_guess提供了初始值[C0, k]。尝试去掉它或用[1, 1]试试可能会收敛到错误解或失败。params_cov是参数的协方差矩阵其对角线元素的平方根perr给出了各个参数的标准误差这是评估参数估计精度的关键指标。我们从速率常数k计算了半衰期 $t_{1/2} \ln 2 / k$。MATLAB 实现t [0.5, 1, 2, 3, 4, 6, 8]; C [8.2, 5.7, 3.1, 1.8, 1.1, 0.4, 0.2]; % 定义模型函数句柄 model (p, t) p(1) * exp(-p(2) * t); % 初始猜测值 initialGuess [10, 0.46]; % 使用 lsqcurvefit 进行非线性拟合 options optimoptions(lsqcurvefit, Display, iter); % 显示迭代过程 [params_opt, resnorm, residual, exitflag, output, lambda, jacobian] ... lsqcurvefit(model, initialGuess, t, C); C0_opt params_opt(1); k_opt params_opt(2); % 计算参数置信区间需要Statistics and Machine Learning Toolbox % ci nlparci(params_opt, residual, jacobian, jacobian); fprintf(拟合参数: C0 %.2f mg/L\n, C0_opt); fprintf(拟合参数: k %.3f /小时\n, k_opt); fprintf(半衰期 %.2f 小时\n, log(2)/k_opt); % 绘图 t_fine linspace(0, 9, 100); C_fit model(params_opt, t_fine); plot(t, C, o, t_fine, C_fit, -); xlabel(时间 (小时)); ylabel(浓度 (mg/L)); legend(数据, 拟合曲线); grid on;5. 系统辨识中的应用动态模型的参数估计在控制工程和系统辨识领域最小二乘法是辨识系统模型参数的核心工具。其思想是将一个动态系统如差分方程描述的系统的输出表示成关于过去输入输出数据和待估参数的线性形式从而将动态参数估计问题转化为静态的线性最小二乘问题。考虑一个简单的单输入单输出系统可以用如下自回归外生模型描述 $$y(k) a_1 y(k-1) ... a_{na} y(k-na) b_1 u(k-1) ... b_{nb} u(k-nb) e(k)$$ 其中 $u$ 是输入$y$ 是输出$e$ 是噪声$k$ 是时间步$na, nb$ 是模型阶次。将其改写为 $$y(k) [-y(k-1), ..., -y(k-na), u(k-1), ..., u(k-nb)] \cdot [a_1, ..., a_{na}, b_1, ..., b_{nb}]^T e(k)$$对于从 $k1$ 到 $N$ 的所有数据我们可以构建矩阵方程 $$\mathbf{Y} \mathbf{\Phi} \mathbf{\theta} \mathbf{E}$$ 其中$\mathbf{Y} [y(1), y(2), ..., y(N)]^T$ 是输出向量。$\mathbf{\Phi}$ 是回归矩阵每一行由对应时刻的过去输入输出数据组成。$\mathbf{\theta} [a_1, ..., a_{na}, b_1, ..., b_{nb}]^T$ 是待估参数向量。$\mathbf{E}$ 是误差向量。此时最小二乘估计 $\hat{\mathbf{\theta}}$ 就是使 $|\mathbf{Y} - \mathbf{\Phi}\mathbf{\theta}|^2$ 最小的解其解析解为 $$\hat{\mathbf{\theta}} (\mathbf{\Phi}^T \mathbf{\Phi})^{-1} \mathbf{\Phi}^T \mathbf{Y}$$ 这就是批处理最小二乘。对于时变系统还有递推最小二乘等在线算法。注意事项在系统辨识中一个关键前提是噪声 $e(k)$ 是白噪声。如果噪声是有色的即与过去的输入输出相关普通最小二乘估计将是有偏的。此时需要用到广义最小二乘法、辅助变量法等更高级的方法。此外输入信号 $u(k)$ 需要具有持续激励性才能保证 $(\mathbf{\Phi}^T \mathbf{\Phi})$ 矩阵可逆即参数可辨识。6. 常见陷阱、问题排查与高级考量即使理解了原理和步骤在实际应用中仍会踩坑。下面是一些典型问题及应对策略。6.1 过拟合与欠拟合模型的复杂度选择这是拟合中的核心矛盾。欠拟合模型过于简单如用直线拟合明显弯曲的数据无法捕捉数据中的趋势。表现为训练误差和未来预测误差都很大。过拟合模型过于复杂如用高阶多项式拟合带噪声的数据不仅拟合了趋势还“拟合”了噪声。表现为训练误差极小但预测新数据时误差很大泛化能力差。如何判断与选择可视化始终绘制拟合曲线与原始数据点的对比图。观察曲线是否平滑地穿过数据点聚集区还是剧烈波动以穿过每一个点。交叉验证将数据分为训练集和测试集。用训练集拟合模型在测试集上评估误差。如果训练误差远小于测试误差很可能过拟合了。信息准则对于参数化模型可以使用AIC或BIC准则。它们在拟合优度残差平方和的基础上增加了对参数数量的惩罚倾向于选择更简洁的模型。正则化当模型复杂度必须较高时如多项式阶数高可以使用岭回归或LASSO。它们在损失函数中加入参数向量的L2或L1范数作为惩罚项强制参数值变小甚至为零从而抑制过拟合。6.2 病态问题与数值稳定性当求解正规方程 $(\mathbf{X}^T\mathbf{X})\mathbf{\theta} \mathbf{X}^T\mathbf{Y}$ 时如果矩阵 $\mathbf{X}^T\mathbf{X}$ 接近奇异条件数很大其逆矩阵对数据中的微小扰动会极其敏感导致参数估计结果极不稳定。这在以下情况容易出现特征量纲差异大例如一个特征范围是[0, 1]另一个是[10000, 100000]。特征高度相关例如在多项式拟合中$x$ 和 $x^2$ 高度相关。解决方案数据标准化/归一化将每个特征减去其均值除以其标准差。这是最常用且有效的方法。使用更稳定的算法避免直接计算 $(\mathbf{X}^T\mathbf{X})^{-1}$。使用QR分解或奇异值分解来求解最小二乘问题。像numpy.linalg.lstsq和 MATLAB的\运算符内部都使用了SVD这类稳定算法。增加正则化岭回归 $(X^TX \lambda I)^{-1}X^TY$ 通过引入一个小常数 $\lambda$ 改善矩阵的条件数使其可逆且稳定。6.3 加权最小二乘当误差并不平等时标准最小二乘假设所有数据点的误差方差相同同方差。但现实中不同数据点的测量精度可能不同。例如某些点由精密仪器测得误差小另一些点由粗略方法测得误差大。此时我们应该给高精度数据点更大的权重。加权最小二乘的损失函数变为 $$L_w \sum_{i1}^{n} w_i [y_i - f(x_i)]^2$$ 其中 $w_i$ 是权重通常与测量误差方差 $\sigma_i^2$ 成反比即 $w_i 1/\sigma_i^2$。其解为 $$\hat{\mathbf{\theta}} (\mathbf{X}^T \mathbf{W} \mathbf{X})^{-1} \mathbf{X}^T \mathbf{W} \mathbf{Y}$$ 其中 $\mathbf{W}$ 是以 $w_i$ 为对角元素的对角矩阵。6.4 鲁棒回归对抗异常值的铠甲如前所述最小二乘对异常值敏感。当数据中存在少量但严重的异常点时鲁棒回归方法能提供更可靠的拟合。其核心思想是降低大残差数据点的权重。Huber损失在残差较小时使用平方损失较大时使用线性损失平滑过渡。Tukey双权重损失当残差超过某个阈值时权重降为零完全忽略该点。RANSAC一种随机采样一致性算法。它反复随机选取一个子集进行拟合然后计算有多少点符合这个模型即残差小于阈值最后选择共识集最大的模型。对包含大量外点的数据非常有效。在Python中sklearn.linear_model提供了RANSACRegressor和HuberRegressor。在MATLAB中robustfit函数提供了多种鲁棒拟合选项。7. 评估拟合质量不止看R²得到一个拟合模型后如何判断它好不好残差分析这是最强大的诊断工具。绘制残差 $e_i y_i - \hat{y}_i$ 随自变量 $x_i$ 或拟合值 $\hat{y}_i$ 变化的散点图。理想情况残差随机、均匀地分布在0线上下无明显模式。出现趋势如果残差呈现曲线趋势如先正后负再正说明模型可能漏掉了非线性成分。漏斗形状残差范围随 $x$ 增大而增大说明可能存在异方差性考虑加权最小二乘或对y做变换如取对数。决定系数 R²$R^2 1 - \frac{SS_{res}}{SS_{tot}}$表示模型解释的数据变异比例。越接近1越好。但要注意增加模型参数复杂度总会使R²增加即使增加的是无意义的变量。因此更推荐看调整后的R²它惩罚了参数数量。参数置信区间通过协方差矩阵计算出的参数标准误可以构建参数的置信区间如95%置信区间。如果区间包含0则该参数可能不显著。预测区间对于新的 $x_0$我们不仅可以给出拟合值 $\hat{y}_0$还可以给出其预测区间。这个区间比置信区间宽因为它包含了单个观测值的随机误差。最后记住一句老生常谈但无比正确的话所有模型都是错的但有些是有用的。最小二乘法为我们提供了一个强大的工具来找到那个“有用”的模型但模型的最终选择必须结合物理背景、工程常识和对数据的深入洞察。它始于数学但绝不止于数学。