资讯动态

最小二乘拟合从入门到实战:推导、代码与排错全解析

发布时间:2026/10/20 8:45:07 来源:尧图企业网站定制
从“还没开始看”到上手我这样把最小二乘拟合彻底吃透前阵子列学习计划时随手写下一个标题“最小二乘拟合还没开始看”。写完之后它就安静地躺在待办清单里直到某天做实验数据处理时被一组带噪声的散点图逼得不得不动手解决。如果你也正卡在类似的位置——听说过最小二乘这个名字知道它和回归、拟合有关但不清楚原理和推导过程也没亲手写过实现代码——这篇笔记就是写给你的。我会从“为什么需要它”讲起把数学推导、矩阵形式、Python实现、常见坑位一并梳理清楚最后给出几条压箱底的排查经验。最小二乘拟合Least Squares Fitting是数据分析领域最基础也最常用的工具之一它解决的核心问题并不复杂给出一堆散落的数据点找到一条最合适的曲线来概括它们背后的趋势规律。这里的“最合适”有明确数学定义——让所有数据点到拟合曲线的误差平方和最小。这种“误差平方和最小化”的思路贯穿了从简单线性回归、多项式拟合到机器学习中线性模型的完整脉络。这篇文章适合三类读者一是刚开始接触数据拟合、对公式推导感到很模糊的初学者二是会用现成库但没真正理解背后逻辑的开发者三是需要在实验或项目里手工完成拟合、调试拟合结果的工程师。读完你应该能独立推导出一元最小二乘的闭式解能写出矩阵形式的正规方程求解代码也清楚遇到拟合结果异常时该从哪里排查。1. 最小二乘到底在干什么先建立直观感受1.1 从一个带噪声的测量场景说起想象一个常见的现实场景你在某个物理实验中反复测量同一段距离但由于仪器精度和环境扰动每次测出的数值都略有差异。你手里有一组测量数据点理论上它们应该落在同一条直线附近可实际画出来却是一座模糊的“点云”。你希望找到一条直线能尽可能贴近这一堆点。另一种常见场景是趋势预测。比如记录了过去若干个月的销量数据想推测下个月的大致范围或者记录了一个器件在不同温度下的输出值想建立温度与输出的对应关系。这类问题的共同点是你相信变量之间存在某种函数关系直线、曲线、多项式但数据本身带着噪声你需要在噪声中把真实的规律“提取”出来。最小二乘做的事情就是在这种场景下给出一个确定的答案在所有可能的直线中根据“误差平方和最小”这一准则选出唯一一条。它不是唯一可行的拟合思路——比如还有最小一乘绝对值误差和切比雪夫拟合最大误差最小化——但它在数学上最好处理有解析解计算也最简单所以成为默认选择。1.2 为什么偏偏是“平方”而不是绝对值第一次看到“最小二乘”这个名字时很多人都会有疑问为什么误差要平方直接用误差的绝对值求和不行吗这确实是个好问题背后有历史和数学两个层面的原因。从历史角度说这种方法最早由法国数学家勒让德在1805年公开发表高斯也独立提出过并用它预测了谷神星的轨道位置。当时选择平方和一个重要原因是平方函数处处可导能直接用微积分求极值。从数学性质上说平方误差对大误差的惩罚更重。误差为2时平方是4误差为10时平方是100。这意味着最小二乘拟合会尽量避免出现个别误差特别大的点这种做法在误差服从正态分布时是统计意义上的最优选择。但反过来说这个特性也导致它对离群值很敏感——一个严重偏离的坏点会强烈拉扯拟合曲线这一点我们到后面“避坑”部分再展开。1.3 最小二乘的适用边界说清楚它“能干什么”也得说清楚它“不适合干什么”。最小二乘最擅长处理的场景是自变量x相对可靠因变量y带噪声噪声大致随机分布变量之间的潜在关系大致能用线性函数或多项式刻画。它不擅长的情况包括数据中存在大量离群值容易被带偏、自变量因变量都存在较大测量误差需要更复杂的正交回归、变量之间的关系高度非线性需要转成线性再拟合或用非线性方法。在这些场景下要么对数据做预处理要么换用更复杂的方法。但无论如何最小二乘都是理解更复杂拟合方法的基石搞懂它再学别的会顺畅很多。2. 核心推导与原理拆解从公式到矩阵形态2.1 一元线性拟合的完整推导先从最简单的一元线性拟合开始。假设有一组数据点 (x₁, y₁), (x₂, y₂), ..., (xₙ, yₙ)想要拟合一条直线y kx b这里的k是斜率也叫回归系数b是截距。对第i个数据点模型预测值是 kxᵢ b实际值是 yᵢ两者之差就是残差。最小二乘的目标是让所有残差的平方和最小写成数学形式S(k, b) Σᵢ(yᵢ - kxᵢ - b)²要找到使S最小的k和b分别对k和b求偏导并令其为零。计算过程不复杂先对b求偏导得到∂S/∂b -2Σᵢ(yᵢ - kxᵢ - b) 0整理后得到 nb Σyᵢ - kΣxᵢ即 b ȳ - kx̄这里 ȳ 是y的均值x̄ 是x的均值。这个结果说明拟合直线必然穿过数据点的重心 (x̄, ȳ)这是一个很实用的检查点。再对k求偏导经过整理可得k [Σ(xᵢ - x̄)(yᵢ - ȳ)] / [Σ(xᵢ - x̄)²]这个公式就是一元线性回归的闭式解。分母是所有自变量与其均值的离差平方和只和数据本身的离散程度有关分子是自变量与因变量的协方差相关项。如果分子为0说明x和y毫无线性相关性此时最优斜率就是0这也符合直觉。2.2 为什么均值点必然落在拟合直线上上面推导过程中有个容易被忽略但很好用的性质拟合直线必过点 (x̄, ȳ)。我在多组数据上验证过这个结论它不仅是数学推导的必然结果也可以用来快速检查拟合结果是否合理。假设你拟合出了 y kx b如果把 x̄ 代进去算出的值恰好等于 ȳ。这个性质在做数据可视化时有实际价值——当你看到一条拟合直线没有穿过数据点的中心区域时往往意味着代码里的均值计算或参数更新出了bug而不是数据本身的问题。2.3 矩阵形式与正规方程一元情形的推导可以手工完成但现实中的拟合很少只有单个自变量。更多时候我们要处理的是多元线性拟合比如 y β₀ β₁x₁ β₂x₂或者多项式拟合比如 y β₀ β₁x β₂x²。这些场景如果还用逐项推导就太繁琐了统一的矩阵方法才是正解。把模型写成矩阵形式y Xβ其中y是n维列向量所有观测值X是n行m列的“设计矩阵”每行对应一个数据点每列对应一个特征项β是m维参数向量。最小二乘的目标是找到β使残差向量 e y - Xβ 的平方范数最小即min ||y - Xβ||²展开后对β求梯度并设为零得到著名的正规方程Normal EquationXᵀXβ Xᵀy当XᵀX可逆时解为 β (XᵀX)⁻¹Xᵀy。这个公式是一元情形在多元场景下的自然推广。特别地如果设计矩阵X只有一列加上截距项后是两列用这个公式解出的β和前面一元推导的k、b完全一致。我建议初学者自己用一个小数据把两个方法的结果对照一下验证一致性会帮助建立很大的信心。2.4 正规方程的数值风险为什么有时不能直接用正规方程的表达式写起来很简洁但在实际计算机实现中直接用它存在隐患。关键问题在于 XᵀX 的数值稳定性。如果X的各列之间存在高度相关性多重共线性或者数据量纲差距极大XᵀX 可能接近奇异矩阵求逆时会产生巨大的数值误差。这意味着微小的数据扰动会导致拟合参数发生剧烈变化。我曾在某个实验场景中遇到过这种情况两个特征列几乎线性相关直接用正规方程求逆得到的结果波动很大跑一次一个样。后来改用最小二乘的QR分解解法数值表现立刻稳定下来。这也是为什么在实际的数值计算库中正规方程通常不是首选解法QR分解或奇异值分解SVD在数值稳定性上更可靠。后面讲代码实现时我会具体演示不同解法之间的差异。3. 从零上手最小二乘拟合的完整实操3.1 准备环境与模拟数据动手环节我用Python NumPy来做原因很简单NumPy内置的lstsq函数封装了良好的数值算法同时我们也能手动实现正规方程方便对照理解。你本机只要有Python 3.8以上版本并安装NumPy和Matplotlib即可。先造一组带噪声的线性模拟数据。假设真实规律是 y 2.5x 4在y方向上叠加均值为0、标准差为2的高斯噪声取50个数据点。这样做的目的是数据既具备已知规律又贴近真实情况。import numpy as np np.random.seed(42) # 固定随机种子保证结果可复现 x np.linspace(0, 10, 50) true_k, true_b 2.5, 4.0 noise np.random.normal(0, 2.0, sizex.shape) y true_k * x true_b noise这里固定随机种子是一个我很推荐的操作习惯。调参和复现结果时如果每次跑出来的数据都不一样很多对比工作根本没法做。实际项目中随机种子还能让测试用例稳定可查。3.2 三种实现方式对照手写公式、NumPy的lstsq、Polyfit接下来按三种方式拟合刚才的模拟数据。第一种直接按前面的闭式解手写第二种用NumPy的np.linalg.lstsq第三种用np.polyfit做一阶多项式拟合。方式一手写正规方程# 构建设计矩阵每行是 [1, x] X_design np.vstack([np.ones_like(x), x]).T beta np.linalg.inv(X_design.T X_design) X_design.T y k_manual, b_manual beta[1], beta[0] print(f手写正规方程: k{k_manual:.4f}, b{b_manual:.4f})方式一简化版直接用均值公式x_mean np.mean(x) y_mean np.mean(y) k_simple np.sum((x - x_mean) * (y - y_mean)) / np.sum((x - x_mean) ** 2) b_simple y_mean - k_simple * x_mean print(f均值公式: k{k_simple:.4f}, b{b_simple:.4f})方式二NumPy lstsqbeta_lstsq, residuals, rank, s np.linalg.lstsq(X_design, y, rcondNone) k_lstsq, b_lstsq beta_lstsq[1], beta_lstsq[0] print(flstsq: k{k_lstsq:.4f}, b{b_lstsq:.4f})方式三polyfitk_poly, b_poly np.polyfit(x, y, 1) print(fpolyfit: k{k_poly:.4f}, b{b_poly:.4f})跑完之后你会发现三种方式得到的k和b几乎完全一致都能稳定地逼近真实的 2.5 和 4.0。这个结果完全在预期中——它们本质上都在解同一个数学问题。我建议初学者一定要亲手把三种方式各跑一遍这不仅是一次数值验证更是一次“知识落地”的过程。值得注意的一个细节是np.linalg.lstsq默认使用的算法基于LAPACK的gelsd例程内部通过SVD分解来处理可能出现的秩亏问题因此在面对近奇异矩阵时比直接求逆可靠得多。3.3 多项式拟合与设计矩阵的扩展线性拟合只是最小二乘的入门应用。真实场景中变量关系很少是纯直线的更多时候需要用多项式来拟合。做法和线性情形几乎一样只需扩展设计矩阵的列即可。比如要拟合二次曲线 y β₀ β₁x β₂x²设计矩阵的每一行就是 [1, xᵢ, xᵢ²]。更高阶的多项式也是同样思路不断往上添加x的幂次列。# 生成二次关系模拟数据 x2 np.linspace(-3, 3, 80) y2 0.8 * x2**2 - 1.5 * x2 2 np.random.normal(0, 1.0, sizex2.shape) # 二次多项式拟合 degree 2 X_poly np.vstack([x2**i for i in range(degree 1)]).T beta_poly np.linalg.lstsq(X_poly, y2, rcondNone)[0] print(二次拟合系数:, beta_poly)注意系数向量是从低次到高次排列的beta_poly[0] 对应常数项beta_poly[1] 对应一次项beta_poly[2] 对应二次项。这一点在用np.polyfit时容易搞反polyfit返回的系数是从高次到低次排列的和这里列设计的顺序不一致混着用时需要特别留意。3.4 拟合优先用正规方程还是QR分解实操中我处理少量数据几千条以内、特征维度不高时直接用正规方程问题不大。但当数据量大、特征多或特征之间存在相关性时我更倾向于使用np.linalg.lstsq因为它内部采用了更稳健的数值算法即便面对秩亏矩阵也能返回最小范数解。# 含共线性特征的模拟x2特征几乎是x1的线性变换 X_bad np.column_stack([np.ones_like(x), x, x * 2 np.random.normal(0, 0.01, sizex.shape)]) beta_bad_inv np.linalg.inv(X_bad.T X_bad) X_bad.T y beta_bad_lstsq np.linalg.lstsq(X_bad, y, rcondNone)[0] print(inv求逆结果:, beta_bad_inv) print(lstsq结果:, beta_bad_lstsq)实际跑一遍就会发现inv求逆的结果可能非常大且对噪声极其敏感lstsq的结果则稳定得多。Numpy官方文档中也明确指出lstsq是求解最小二乘问题的推荐入口尽量别手动走正规方程求逆。这一点你踩一次坑就会牢记。3.5 设计矩阵与特征缩放使用多项式或多元特征时不同列的数值范围往往相差悬殊。比如要拟合 y β₀ β₁x β₂x²其中 x 取值在 1 到 100 之间那么 x² 列的范围就是 1 到 10000。这样 XᵀX 中不同元素的量级差异会严重放大数值误差。标准做法是在构造设计矩阵之前对特征做归一化处理比如z-score标准化减去均值除以标准差或min-max缩放缩放到0和1之间。做完拟合后再把求得的系数还原回原始坐标系的表达式。这一步在手工实现时经常被忽略导致高阶拟合时结果抖动严重。4. 拟合质量的评估数字指标与残差图4.1 决定系数R²拟合好坏的“评分卡”拟合完一条曲线光看系数还不够得知道这条曲线对数据的解释程度有多高。最常用的指标是决定系数 R²。它的定义是1 减去“残差平方和与总平方和的比值”R² 1 - Σᵢ(yᵢ - ŷᵢ)² / Σᵢ(yᵢ - ȳ)²其中 ŷᵢ 是拟合值ȳ 是真实值的均值。R²的直观含义是拟合曲线捕捉了数据中多大比例的变化。R² 1 表示完美拟合所有点都在曲线上R² 0 表示模型和直接用均值预测的效果一样差R²为负说明模型比直接取均值还差。实际项目中R² 0.8 以上通常算不错的拟合。但千万别“唯R²论”——高R²可能是过拟合的结果尤其当多项式阶数过高时曲线会为了贴合每一个噪声点而剧烈扭曲。y_pred X_design beta_lstsq ss_res np.sum((y - y_pred) ** 2) ss_tot np.sum((y - np.mean(y)) ** 2) r_squared 1 - ss_res / ss_tot print(fR² {r_squared:.4f})4.2 残差分析从图上挖出模型隐患R²只是数值层面的整体指标画残差图Residual Plot能看到更多信息。残差就是每个数据点的实际值与拟合值之差 eᵢ yᵢ - ŷᵢ。把残差按x坐标画出来如果拟合正确残差应该随机分布在0附近没有明显趋势。如果残差图呈现出弧形或喇叭形分布通常说明两个问题一是模型形式不合适比如数据明明是二次关系却用了直线拟合残差会呈现系统性曲线二是噪声方差不是常数这违背了最小二乘的等方差假设。看到这种图第一反应不是调参数而是重新思考模型形式。import matplotlib.pyplot as plt residuals y - X_design beta_lstsq plt.scatter(x, residuals, alpha0.7) plt.axhline(y0, colorred, linestyle--) plt.xlabel(x) plt.ylabel(residual) plt.title(Residual Plot) plt.show()4.3 数据分割与过拟合判断判断拟合模型是否过拟合通用的办法是数据分割把数据切成训练集和测试集用训练集拟合参数再用测试集评估R²。如果训练R²很高但测试R²很低说明模型把自己的训练集背下来了推广能力差。这一招在机器学习里已成标配但在传统的曲线拟合场景中很多人懒得做。我的建议是只要数据量允许超过200个点就很充裕做一次简单分割很值得能帮你直观理解过拟合的真实含义。4.4 多项式阶数越高越好吗误差曲线的凹形规律把训练误差和测试误差同时画出来横轴是多项式阶数纵轴是R²或均方误差你能看到那条著名的U形曲线。阶数太低模型欠拟合阶数太高训练误差持续下降、测试误差反弹上升。最优阶数通常出现在曲线底部附近。我在模拟数据上做过对比真实模型是二次的拟合从1阶到6阶逐次尝试测试集R²在2阶附近达到峰值之后随着阶数升高反而恶化。这个现象值得亲手验证一次因为它是理解“复杂度控制”这一概念的关键体验。5. 实战中的典型问题与排查笔记5.1 拟合结果不稳定特征共线性与数值病态现象同样的数据只是微小噪声扰动拟合系数出现大幅变化。排查顺序按三步来。第一步检查特征列是否接近线性相关——计算设计矩阵各列之间的相关系数如果超过0.99基本可以确诊共线性。第二步检查特征数值范围是否差距过大——归一化后重跑一遍很多问题瞬间消失。第三步保留共线性问题但不处理改用np.linalg.lstsq看看是否稳定。系统性解决共线性可以从三个方向入手删除高度相关的一个特征用主成分分析PCA先降维再拟合改用带正则化的线性回归岭回归、Lasso它们专门应对这类情况。5.2 离群值把拟合曲线拉偏最小二乘对大误差的惩罚是平方级的一个残差为20的点相当于400个残差为1的点的影响力。所以数据里只要有一个离群值比如传感器故障产生的异常读数拟合曲线就会被明显带偏甚至朝着离群值方向倾斜。处理离群值有几个思路。最简单直接的是先画散点图肉眼观察并剔除异常点。更客观的做法是使用RANSAC随机抽样一致性算法它通过反复随机选取子集拟合模型再统计内点数量选出最优模型能自动屏蔽离群值。若不想引入复杂算法也可以先用一次初步拟合找出残差超过某个阈值如3倍标准差的点剔除后再重新拟合。5.3 数据本身非线性转变与分段处理实际项目中很多看似非线性的关系可以通过变量替换变成线性。比如指数关系 y a·e^(bx)两边取对数后变成 ln(y) ln(a) bx就可以对 (x, ln(y)) 用线性拟合幂律关系 y a·x^b 同理取对数后变成 ln(y) ln(a) b·ln(x)。这种“化非线性为线性”的技巧在手工业数据分析时极其常用效果也很好。但注意一点对数变换改变了误差的分布性质拟合结果在原始尺度上并非严格最小二乘最优。如果精度要求高可以先用变换后的结果作为初值再用非线性最小二乘细调。5.4 代码层面的低级错误索引最后整理一份我在实操和辅导中经常碰见的代码错误速查表很多是拼写和维度问题但卡起人来毫不含糊。症状常见原因快速处置拟合结果恒定不变设计矩阵漏了常数项1那一列检查X是否包含全1列矩阵乘法报维度错误X.T X的顺序写反了确认形状是(m,n)(n,m)结果特别大或特别小未归一化且特征量级悬殊标准化后重试每次运行结果都不同随机种子未固定设置np.random.seedpolyfit系数和预期对不上系数顺序理解反了高次到低次是polyfit的惯例R²为负模型比均值预测还差检查模型形式是否正确这张表我贴着显示器很久每次排查问题时先扫一遍往往能省下大半天时间。6. 最小二乘在真实世界的落地场景6.1 传感器校准仪表读数的线性纠偏实际项目中最小二乘最常见的应用之一是传感器校准。某次需要把一个电压信号转换为物理量读数先采集多个标准物理量对应的电压输出值然后用最小二乘拟合出“物理量 k × 电压 b”的换算关系。这样只需要一个线性公式仪表就能从电压精准倒推物理量。斜率k表示灵敏度截距b表示零偏两者都是传感器的重要性能指标。6.2 实验数据标定与曲线拟合在科研和工程实验里最小二乘是处理标定数据的标准手段。比如给某个电路测量温度特性时采集若干温度点下的输出值用多项式拟合出温度-输出曲线后续即可用该曲线从输出反推温度。常见的做法是先在理论模型指导下确定多项式阶数再通过最小二乘求系数最后做残差分析验证模型假设是否成立。6.3 机器学习里的最小二乘基因很多刚接触机器学习的人可能没意识到线性回归模型本质上就是最小二乘的机器学习形态。在机器学习框架里线性回归通常用梯度下降来求解但对于中小规模问题正规方程或SVD解法同样有效而且无需调学习率。另外最小二乘的思想也延伸到逻辑回归的迭代重加权最小二乘算法、正则化线性模型岭回归的目标函数里多了一个惩罚项本质上还是最小二乘风格等更复杂的模型。可以说搞懂了最小二乘你就抓住了许多机器学习算法的一条关键主线。6.4 数据插值与平滑除了拟合趋势最小二乘也常用于数据平滑。给出一组含噪采样点用低阶多项式做滑动窗口拟合在每个窗口中心点输出拟合值就能得到一条平滑后的曲线。这种Savitzky-Golay滤波的做法本质上就是“滑动窗口内的最小二乘多项式拟合”在信号处理和光谱分析中使用极广。7. 写在最后几个亲测有效的学习建议这篇内容从最小二乘的直觉概念讲到了推导、矩阵实现、实际应用和避坑清单基本上覆盖了从零上手所需的全部主干。回头再看“还没开始看”那几个字会发现最难的其实是迈出第一步的犹豫。如果你也正准备系统掌握这个方法我的建议是三步走第一步先按文中的代码自己造一组数据并跑通三种实现方式确认结果一致第二步把一个真实项目中的简单线性拟合问题哪怕只有十几条数据完完整整地手工算一遍用表格列出手写推导过程再和程序结果比对第三步刻意制造一些问题数据加离群值、加共线性、加噪声观察最小二乘出什么岔子再想办法修复。最后分享一个小技巧把《误差平方和最小化》这句话转化成自己的“公式心智模型”。以后看到任何“找一条曲线穿过一堆点”的问题第一反应就应该是构造设计矩阵、套最小二乘、检查残差图。这个反应一旦建立数据分析类任务你就能跑赢大多数还在“调库碰运气”的人了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑