MATLAB最小二乘法拟合:从原理推导到实战应用全解析
1. 从一次失败的实验数据说起:为什么我们需要最小二乘法?
做实验、搞测量、分析数据的朋友,估计都遇到过这种场景:你辛辛苦苦测了一组数据,比如测量了某个物理量随时间的变化,然后兴冲冲地想把它们画在图上,连成一条光滑的曲线,好看看背后的规律。结果呢?点倒是画上去了,可它们七零八落,根本不在一条直线上。你想用尺子比划着画一条“最合适”的直线或曲线穿过去,怎么画都觉得有点偏——有的点在线上方,有的在下方,凭感觉画出来的线,每次可能都不一样,更别提用它去做预测或者反推参数了。
我刚开始处理数据时也这样,总觉得“差不多就行”。直到有一次,我用两组不同的“感觉线”去计算同一个关键参数,结果差了快20%,直接被导师问得哑口无言。那时候我才明白,在科学和工程里,“感觉”是靠不住的,我们需要一个客观、精确、可重复的数学方法来找到那条“最佳”的拟合曲线。这个方法,就是最小二乘法。
简单来说,最小二乘法的核心思想非常直观:它要找的这条曲线,能让所有数据点到这条曲线的垂直距离的平方和达到最小。为什么是“平方和”而不是直接加距离?因为距离有正有负,直接相加会相互抵消,无法真实反映总的偏差;而平方既能消除正负号的影响,又对大偏差(即“离谱”的点)更为敏感,这使得拟合结果对异常值有一定的鲁棒性,同时数学上处理起来也更方便(求导后是线性方程)。在MATLAB这个“计算神器”里,实现最小二乘法拟合主要有两大途径:一是自己动手写代码,深入理解算法每一步;二是直接调用强大又便捷的拟合工具箱。这篇文章,我就结合自己多年的使用经验,带你彻底搞懂这两种方法,从原理推导到代码实现,再到图形化工具的点滴技巧,让你面对杂乱数据时,能稳稳地拿出那条最有说服力的“最佳曲线”。
2. 最小二乘法的数学内核:不仅仅是“找条线”
很多人一提起最小二乘法,就想到直线拟合y = a*x + b。这没错,但它只是冰山一角。最小二乘法的威力在于它能拟合任意形式的模型,只要这个模型关于待求参数是线性的(即“线性参数”)。理解这一点,是灵活运用该方法的关键。
2.1 核心原理与矩阵推导
我们设有一组观测数据(x_i, y_i), i=1,2,...,n。我们想用一个模型函数y = f(x, β)来拟合它,其中β = [β1, β2, ..., βm]^T是待求的 m 个参数。对于第 i 个数据点,模型预测值为f(x_i, β),观测值与预测值之差称为残差r_i = y_i - f(x_i, β)。
最小二乘法的目标函数(损失函数)就是所有残差的平方和:S(β) = Σ (r_i)^2 = Σ [y_i - f(x_i, β)]^2
我们的任务就是找到一组参数β,使得S(β)最小。这就是一个多元函数求极值的问题。当模型f(x, β)关于参数β是线性的时候,即它可以写成:f(x, β) = β1*φ1(x) + β2*φ2(x) + ... + βm*φm(x)其中φ1(x), φ2(x), ..., φm(x)是已知的基函数(比如1,x,x^2,sin(x)等),那么问题就简化了。
此时,我们可以构造一个n×m的设计矩阵X(也叫范德蒙矩阵在多项式拟合情形):
X = [ φ1(x1) φ2(x1) ... φm(x1); φ1(x2) φ2(x2) ... φm(x2); ... ... ... ...; φ1(xn) φ2(xn) ... φm(xn) ]观测值向量为Y=[y1; y2; ...; yn],参数向量为β=[β1; β2; ...; βm]。
那么模型可以写成矩阵形式:Y_pred=Xβ。残差向量r=Y - Xβ。目标函数S(β) = r^T * r = (Y - Xβ)^T (Y - Xβ)。
通过对S(β)关于β求导,并令导数为零,我们可以得到著名的正规方程:(X^T * X) β = X^T * Y
只要(X^T * X)可逆(通常要求 n ≥ m 且基函数线性无关),我们就可以直接解出参数的最优估计:β = (X^T * X)^{-1} X^T Y
为什么我要详细写出这个推导?因为这是理解一切的基础。当你自己写代码实现时,你就是在构建这个X矩阵,然后调用(X'*X) \ (X'*Y)或者pinv(X)*Y来求解。当你使用拟合工具箱选择“线性拟合”时,底层就是在解这个方程。知其然,更知其所以然,你才能判断什么情况下拟合是可靠的。
注意:这里说的“线性”是指参数线性,而不是x线性。
y = a*x^2 + b*x + c关于参数a, b, c是线性的,所以依然可以用上述线性最小二乘法。而像y = a*exp(b*x)关于参数a和b就不是线性的,属于非线性最小二乘问题,通常需要迭代法求解(如lsqcurvefit函数或拟合工具箱中的非线性模型)。
2.2 评估拟合好坏的指标
找到拟合曲线后,我们怎么知道它“好”还是“不好”?不能光看图漂亮。有几个关键指标:
- 残差平方和:就是我们的目标函数
S。其值越小,说明总的拟合偏差越小。但它受数据量纲和数量级影响,不能单独用于比较不同数据集或模型的拟合效果。 - 确定系数:也就是常说的R-square。它衡量了模型能够解释的数据波动的比例。计算公式为:
R^2 = 1 - (SS_res / SS_tot)其中SS_res是残差平方和,SS_tot是总离差平方和Σ (y_i - y_mean)^2。R^2越接近1,说明模型对数据的解释能力越强。- 一个重要的坑:对于线性最小二乘(带常数项),
R^2的值域是[0,1]。但对于不包含常数项(截距)的模型,MATLAB计算出的R^2可能为负!这时它已失去“比例”的解释意义,需要谨慎看待。拟合工具箱和fitlm函数会提供调整后的R^2,考虑了参数个数的影响,用于比较不同复杂度的模型。
- 均方根误差:
RMSE = sqrt(SS_res / n)。它相当于“平均”的残差大小,其量纲与原始数据y相同,非常直观。比如你预测房价,RMSE=10万元,就表示平均预测误差在10万左右。 - 参数的标准误与置信区间:拟合出的参数
a,b等并不是一个绝对精确的值,它们也有不确定性。MATLAB的统计工具箱函数(如fitlm)或拟合工具箱的高级输出,可以提供每个参数的标准误差以及95%置信区间。如果某个参数的置信区间包含0,通常意味着这个参数(对应的项)可能不显著,可以考虑从模型中移除。
在实际项目中,我从来不会只看R^2。我会同时观察RMSE(判断绝对误差是否在可接受范围)、残差图(判断误差是否随机分布,有无明显模式)以及参数的置信区间。一个R^2很高但残差呈现明显漏斗形或趋势的模型,很可能存在未考虑的变量或模型形式错误。
3. 手动代码实现:从零构建理解每一步
理解了原理,我们先用最“原始”的方式在MATLAB里实现它。这能让你对整个过程有绝对的掌控感,尤其当需要定制化或嵌入更大程序时。
假设我们有一组数据,想用二次多项式y = a*x^2 + b*x + c来拟合。
% 步骤1:准备数据(这里用模拟数据,你的实际数据可以从文件导入) x = [1, 2, 3, 4, 5, 6, 7]'; y = [1.5, 3.8, 6.7, 10.2, 15.0, 20.5, 27.1]'; % 步骤2:构建设计矩阵 X % 对于二次拟合,基函数为 [1, x, x^2] X = [ones(size(x)), x, x.^2]; % 注意是点乘 .^2 % 步骤3:求解正规方程 (X'*X) * beta = X' * y % 方法A:使用反斜杠运算符 \,MATLAB会自动选择高效稳定的算法 beta = (X' * X) \ (X' * y); % 方法B:使用伪逆 pinv,在 X'*X 接近奇异时更稳定 % beta = pinv(X' * X) * (X' * y); % 方法C:直接使用 X \ y,对于超定系统,MATLAB默认使用最小二乘法求解 % beta = X \ y; % 步骤4:输出拟合参数 a = beta(3); % x^2 项系数 b = beta(2); % x 项系数 c = beta(1); % 常数项 fprintf('拟合方程为: y = %.4f*x^2 + %.4f*x + %.4f\n', a, b, c); % 步骤5:计算预测值及评估指标 y_pred = X * beta; % 等价于 c + b*x + a*x.^2 residuals = y - y_pred; SS_res = sum(residuals.^2); SS_tot = sum((y - mean(y)).^2); R2 = 1 - SS_res / SS_tot; RMSE = sqrt(SS_res / length(y)); fprintf('R-square = %.4f\n', R2); fprintf('RMSE = %.4f\n', RMSE); % 步骤6:绘制图形 figure('Position', [100, 100, 900, 400]); subplot(1,2,1); scatter(x, y, 70, 'b', 'filled'); hold on; x_fine = linspace(min(x), max(x), 100)'; X_fine = [ones(size(x_fine)), x_fine, x_fine.^2]; y_fine = X_fine * beta; plot(x_fine, y_fine, 'r-', 'LineWidth', 2); xlabel('x'); ylabel('y'); title('数据点与二次拟合曲线'); legend('原始数据', '拟合曲线', 'Location', 'northwest'); grid on; subplot(1,2,2); scatter(x, residuals, 70, 'k', 'filled'); hold on; plot([min(x), max(x)], [0,0], 'r--', 'LineWidth', 1); % 零参考线 xlabel('x'); ylabel('残差'); title('残差图'); grid on;代码解读与实操心得:
- 数据列向量:确保你的
x和y是列向量(n×1),这是后续矩阵运算的基础。使用'转置或者(:)操作来保证。 - 构建设计矩阵 X:这是最关键的一步。
ones(size(x))对应常数项。对于多项式拟合,依次添加x,x.^2,x.^3... 即可。如果你想做多元线性回归(例如y = b0 + b1*x1 + b2*x2),那么X = [ones(n,1), x1, x2]。 - 求解方法选择:
(X'*X) \ (X'*y)是最直接的正规方程解法。但当X列近似线性相关(称为“多重共线性”)时,X'*X的条件数很大,求逆会放大误差,导致结果不稳定。X \ y是最推荐的用法。MATLAB的反斜杠运算符非常智能,对于矩形矩阵,它默认会使用QR分解等数值稳定的方法来求解最小二乘问题,避免了显式形成X'*X带来的数值问题。在大多数情况下,你应该优先使用这个方法。pinv(X)*y使用奇异值分解求伪逆,是最稳健的方法,能处理秩亏的情况,但计算量稍大。
- 残差图的重要性:右边的残差图是诊断模型质量的利器。理想的残差图应该像“随机散点”一样围绕零点上下均匀分布,无任何明显的趋势或规律。如果残差呈现“喇叭口”形(方差随x增大而增大),说明可能存在异方差性;如果呈现“弯曲”形,说明模型形式可能不对(比如该用二次的用了线性)。我每次拟合后必看残差图。
处理更复杂的基函数(自定义模型):
假设你的理论模型是y = a + b*sin(x) + c*exp(-x)。只需要修改设计矩阵X即可:
X = [ones(size(x)), sin(x), exp(-x)]; beta = X \ y; a = beta(1); b = beta(2); c = beta(3);这种灵活性是手动编码的最大优势。
4. 拥抱高效:MATLAB内置函数与拟合工具箱详解
对于日常快速分析和探索性工作,每次都从头写代码太麻烦。MATLAB提供了强大的内置函数和交互式工具。
4.1polyfit与polyval:多项式拟合的黄金搭档
对于多项式拟合,polyfit函数是终极简化。
% 使用 polyfit 进行二次拟合 p = polyfit(x, y, 2); % 2 表示二次多项式 % p 是一个向量,包含从高次到低次的系数,即 p = [a, b, c] 对应 a*x^2 + b*x + c % 计算拟合值 y_pred_polyfit = polyval(p, x); % 绘制 figure; scatter(x, y, 'b'); hold on; x_fine = linspace(min(x), max(x), 100); plot(x_fine, polyval(p, x_fine), 'r-', 'LineWidth', 2); legend('数据', 'polyfit拟合');polyfit内部也是通过构建范德蒙矩阵并求解最小二乘问题来实现的,但它做了更多的优化和错误检查。对于高阶多项式(比如9次以上),直接构造X矩阵可能导致X'*X病态,polyfit会采用缩放和中心化等策略来提高数值稳定性。
注意:高阶多项式拟合(“过拟合”)风险极高。它虽然能完美穿过所有数据点(
R^2接近1),但曲线会剧烈震荡,对数据中的噪声极度敏感,预测新数据的能力极差。务必通过残差图、交叉验证等方式判断模型是否合理。
4.2 统计工具箱的fitlm:面向回归分析的全面解决方案
如果你需要进行严格的回归分析,计算统计指标(如R^2,Adj R^2,F-statistic,p-value),fitlm是专业选择。
% 将数据放入 table,变量名更清晰 data = table(x, y, 'VariableNames', {'X', 'Y'}); % 拟合一个线性模型:Y ~ 1 + X + X^2 % 公式字符串中,`1`代表常数项,`X^2`表示X的二次项 mdl = fitlm(data, 'Y ~ 1 + X + X^2'); % 显示详细的回归结果摘要 disp(mdl); % 获取关键信息 coefficients = mdl.Coefficients.Estimate; % 系数估计值 coeff_CI = coefCI(mdl); % 系数的置信区间 R2 = mdl.Rsquared.Ordinary; RMSE = mdl.RMSE; % 绘制诊断图(非常有用!) figure; plotDiagnostics(mdl, 'cookd'); % 库克距离,诊断强影响点 figure; plotResiduals(mdl, 'fitted'); % 残差 vs 拟合值图fitlm的输出mdl是一个丰富的对象。mdl.Coefficients表格里不仅有估计值,还有标准误、t统计量和p值,你可以直接判断X^2项是否显著(p值小于0.05通常认为显著)。诊断图能帮你系统性地评估模型假设(如误差正态性、同方差性)是否成立。
4.3 交互式神器:曲线拟合工具箱
对于探索性数据分析,没有什么比曲线拟合工具箱更直观高效了。在MATLAB命令窗口输入cftool即可打开。
基本工作流:
- 导入数据:在工具箱界面,点击“Data”按钮,从工作区选择你的
x和y数据。 - 选择模型:切换到“Fitting”页面。你可以从丰富的库中选择:
- 多项式:从1次到9次。
- 指数:
a*exp(b*x),a*exp(b*x)+c等。 - 傅里叶:用于周期信号拟合。
- 高斯:峰值拟合。
- 幂函数:
a*x^b。 - 自定义方程:你可以输入任何形式的方程,如
a*sin(b*x+c)。对于非线性自定义方程,你需要提供初始猜测值,这对收敛至关重要。
- 拟合与查看:点击“Apply”,瞬间得到拟合曲线和参数结果。右侧会显示系数值、
R^2、RMSE等。 - 分析:在“Analysis”菜单中,可以进行“Plot residuals”(绘制残差图)、“Find confidence bounds”(显示预测置信区间)等操作。置信区间带能直观显示预测的不确定性范围。
- 导出:拟合满意后,可以导出多种结果:
- 导出拟合对象:到工作区,得到一个
cfit或fit对象,之后可以用feval函数进行预测。 - 生成代码:这是最强大的功能!点击菜单 “Fit” -> “Save to Workspace”,勾选“Save fit as a fit object”和“Save fit results to a struct”。更棒的是,点击 “File” -> “Generate Code”,MATLAB会生成一个包含所有拟合、绘图步骤的完整函数文件。你可以学习这个代码,并将其整合到自己的脚本中,实现自动化拟合。
- 导出拟合对象:到工作区,得到一个
工具箱使用心得:
- 初始值猜不准怎么办?对于复杂非线性模型,初始值不对可能无法收敛或收敛到局部最优解。我的经验是:先用简单模型(如多项式)拟合一下,看看大致趋势;或者根据物理意义给一个粗略估计。在工具箱里,你可以手动调整系数滑块,实时观察曲线变化,帮助找到合适的初始值。
- 如何比较多个模型?在“Fitting”页面,你可以创建多个拟合(如一个线性,一个二次,一个指数),它们会并列显示。通过比较
R^2、RMSE和残差图,可以直观判断哪个模型更合适。记住,并非R^2越高越好,还要考虑模型的简洁性(奥卡姆剃刀原理)。 - 拟合效果不好?看看残差图。如果有明显模式,说明模型缺失了关键成分。尝试添加更高次项、交互项,或者换一种完全不同的模型形式。
5. 进阶实战:非线性拟合与自定义函数模型
当你的模型关于参数是非线性时,例如y = a * exp(-b*x) + c,正规方程法不再适用。我们需要使用迭代优化算法来求解。
5.1 使用lsqcurvefit函数
lsqcurvefit是优化工具箱中的函数,专门用于解决非线性最小二乘曲线拟合问题。
% 步骤1:定义非线性模型函数 % 函数句柄:输入参数 (参数向量, 自变量) -> 输出因变量 model = @(beta, x) beta(1) * exp(-beta(2)*x) + beta(3); % beta(1)=a, beta(2)=b, beta(3)=c % 步骤2:准备数据 x_data = [0, 1, 2, 3, 4, 5]'; y_data = [2.1, 1.2, 0.65, 0.5, 0.35, 0.28]'; % 步骤3:提供初始猜测值 (至关重要!) initial_guess = [2, 0.5, 0.1]; % 根据数据趋势和经验猜测 % 步骤4:调用 lsqcurvefit options = optimoptions('lsqcurvefit', 'Display', 'iter'); % 显示迭代过程 [beta_opt, resnorm, residual, exitflag, output] = ... lsqcurvefit(model, initial_guess, x_data, y_data, [], [], options); % 中间两个空数组 [] 是参数的下界和上界约束,这里不设约束。 % 步骤5:输出结果 fprintf('拟合参数: a=%.4f, b=%.4f, c=%.4f\n', beta_opt(1), beta_opt(2), beta_opt(3)); fprintf('残差范数平方: %.6f\n', resnorm); % 步骤6:绘图对比 figure; scatter(x_data, y_data, 70, 'b', 'filled'); hold on; x_fine = linspace(min(x_data), max(x_data), 100); y_fine = model(beta_opt, x_fine); plot(x_fine, y_fine, 'r-', 'LineWidth', 2); xlabel('x'); ylabel('y'); legend('原始数据', '非线性拟合 (a*exp(-b*x)+c)'); grid on;关键点与避坑指南:
- 初始猜测值:这是非线性拟合成功与否的关键。糟糕的初始值可能导致算法不收敛,或收敛到错误的局部最优解。策略包括:
- 根据物理意义或经验估算。
- 在曲线拟合工具箱里手动调试,找到一个看起来不错的参数组作为初始值。
- 如果可能,将模型线性化(如对
y = a*exp(b*x)两边取对数,化为log(y) = log(a) + b*x),先用线性最小二乘估计一个粗略值。
- 参数约束:
lsqcurvefit允许设置参数的下界lb和上界ub。例如,如果你知道衰减率b必须是正数,可以设置lb = [-inf, 0, -inf];。这能防止算法跑到无意义的参数空间,也能提高收敛速度。 - 检查退出标志:
exitflag大于0通常表示收敛成功。output结构体包含了迭代次数、函数计算次数等详细信息,有助于诊断问题。 - 拟合工具箱同样强大:对于非线性拟合,曲线拟合工具箱的交互式界面优势更明显。你可以实时调整参数和初始值,立即看到曲线变化,非常适合探索。
5.2 自定义模型与fittype和fit
对于更复杂的自定义模型,或者希望使用与拟合工具箱类似的语法,可以结合fittype和fit函数。
% 定义自定义模型:阻尼正弦波 y = a*exp(-b*x)*sin(c*x + d) ft = fittype('a*exp(-b*x)*sin(c*x + d)', ... 'independent', 'x', ... 'dependent', 'y', ... 'coefficients', {'a', 'b', 'c', 'd'}); % 生成一些模拟数据 x_sim = linspace(0, 10, 100)'; a_true = 2; b_true = 0.3; c_true = 1.5; d_true = 0.5; y_sim = a_true * exp(-b_true*x_sim) .* sin(c_true*x_sim + d_true) + 0.1*randn(size(x_sim)); % 加噪声 % 提供初始猜测(对于多参数非线性模型,初始值更要小心) startPoint = [1.5, 0.2, 1.0, 0]; % 执行拟合 [fitted_model, gof] = fit(x_sim, y_sim, ft, 'StartPoint', startPoint); % 查看结果 disp(fitted_model); % 显示拟合公式和系数 disp(gof); % 显示 goodness-of-fit 统计量,包括 R^2 % 绘图 figure; plot(fitted_model, x_sim, y_sim); xlabel('x'); ylabel('y'); legend('数据', '拟合曲线', 'Location', 'best'); title('自定义阻尼正弦波拟合');fit函数返回一个cfit对象fitted_model,你可以用它来求值y_pred = fitted_model(x_new),或者计算导数、积分等。gof结构体包含了拟合优度指标。
6. 完整项目案例:传感器温度校准与预测
让我们用一个综合案例把前面的知识串起来。假设你有一个温度传感器,但其读数V(电压)与实际温度T之间的关系是非线性的,已知近似满足T = a / (b + log(V)) + c。现在通过标定实验获得了一批(V, T)数据,需要拟合出参数a, b, c,并评估拟合效果,最后用拟合模型去预测新电压值对应的温度。
%% 第一部分:数据准备与可视化 clear; close all; clc; % 模拟标定数据 (电压V, 实际温度T) V_cal = [0.8, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0]'; % 电压,单位V T_cal = [125.5, 110.2, 85.3, 70.1, 60.5, 54.0, 45.2]'; % 温度,单位°C figure; scatter(V_cal, T_cal, 80, 'b', 'filled'); xlabel('传感器电压 V (V)'); ylabel('实际温度 T (°C)'); title('传感器标定数据'); grid on; %% 第二部分:使用曲线拟合工具箱进行探索性拟合 % 在命令窗口运行 cftool,手动导入 V_cal 和 T_cal。 % 1. 尝试多项式拟合(如2次、3次),观察效果。 % 2. 尝试自定义方程: T = a / (b + log(V)) + c % 初始值可以猜测为: a=100, b=1, c=20 (根据数据趋势:V增大,T减小) % 3. 在工具箱中比较多项式模型和自定义模型的残差图、R^2和RMSE。 % 假设通过工具箱探索,我们发现自定义模型拟合更好,残差更随机。 % 下面我们以编程方式复现并自动化这个过程。 %% 第三部分:编程实现非线性最小二乘拟合 % 定义模型函数句柄 % 注意:log在MATLAB中是自然对数ln。如果数据手册用的是log10,这里要换。 model_func = @(beta, V) beta(1) ./ (beta(2) + log(V)) + beta(3); % 基于工具箱探索或物理意义设定初始值 initial_guess = [100, 1, 20]; % 设定参数约束:例如,分母 (b+log(V)) 在整个数据范围内应为正数 % 观察数据,V最小0.8,log(0.8)为负,所以b需要足够大以避免分母为0。 % 设 b > 0.5 lb = [-inf, 0.5, -inf]; % 下界 ub = [inf, inf, inf]; % 上界 % 使用 lsqcurvefit 进行拟合 options = optimoptions('lsqcurvefit', 'Display', 'final', 'Algorithm', 'trust-region-reflective'); [beta_opt, resnorm, residuals, exitflag, output] = ... lsqcurvefit(model_func, initial_guess, V_cal, T_cal, lb, ub, options); a_fit = beta_opt(1); b_fit = beta_opt(2); c_fit = beta_opt(3); fprintf('=== 拟合结果 ===\n'); fprintf('参数 a = %.4f\n', a_fit); fprintf('参数 b = %.4f\n', b_fit); fprintf('参数 c = %.4f\n', c_fit); fprintf('残差平方和 = %.4f\n', resnorm); fprintf('退出标志 exitflag = %d (>0 表示成功)\n', exitflag); %% 第四部分:拟合效果评估 % 计算预测值及指标 T_pred = model_func(beta_opt, V_cal); SS_res = sum(residuals.^2); SS_tot = sum((T_cal - mean(T_cal)).^2); R2 = 1 - SS_res / SS_tot; RMSE = sqrt(SS_res / length(T_cal)); fprintf('R-square = %.4f\n', R2); fprintf('RMSE = %.4f °C\n', RMSE); % 绘制拟合曲线与残差图 V_fine = linspace(min(V_cal)*0.9, max(V_cal)*1.1, 200)'; T_fine = model_func(beta_opt, V_fine); figure('Position', [100, 100, 1200, 400]); subplot(1,3,1); scatter(V_cal, T_cal, 80, 'b', 'filled'); hold on; plot(V_fine, T_fine, 'r-', 'LineWidth', 2); xlabel('传感器电压 V (V)'); ylabel('温度 T (°C)'); title('标定数据与拟合曲线'); legend('标定数据', sprintf('拟合: T=%.2f/(%.2f+ln(V))+%.2f', a_fit, b_fit, c_fit), 'Location', 'best'); grid on; subplot(1,3,2); scatter(V_cal, residuals, 80, 'k', 'filled'); hold on; plot([min(V_cal), max(V_cal)], [0,0], 'r--', 'LineWidth', 1); xlabel('传感器电压 V (V)'); ylabel('残差 (°C)'); title('残差图'); grid on; subplot(1,3,3); normplot(residuals); % 正态概率图 title('残差正态性检验'); grid on; %% 第五部分:模型应用与预测 % 假设传感器新测到一组电压值,预测其温度 V_new = [0.9, 1.2, 2.2, 3.5]'; T_new_pred = model_func(beta_opt, V_new); fprintf('\n=== 温度预测 ===\n'); for i = 1:length(V_new) fprintf('电压 %.2f V -> 预测温度 %.2f °C\n', V_new(i), T_new_pred(i)); end % 计算预测值的近似置信区间(简化版,使用RMSE作为误差度量) % 更严谨的方法需要计算参数协方差矩阵,这里用RMSE近似 prediction_interval = 1.96 * RMSE; % 95% 置信区间,假设误差正态分布 fprintf('预测值的近似95%%置信区间为 ±%.2f °C\n', prediction_interval);案例总结与经验:
- 探索先行:面对未知模型,先用
cftool尝试多种预设模型和自定义方程,通过图形和指标快速筛选,这比盲目编程试错高效得多。 - 初始值与约束:对于非线性模型,合理的初始值和物理约束(如本例中
b的下界)能极大提高拟合成功率和结果可靠性。 - 综合评估:不要只看
R^2。在这个案例中,我们同时观察了拟合曲线图(看趋势)、残差图(看误差模式)和正态概率图(粗略检查误差分布)。残差图无明显规律,正态概率图点近似在一条直线上,说明模型和误差假设基本合理。 - 模型应用与不确定性:用拟合模型做预测时,一定要意识到预测存在不确定性。这里我们用
RMSE简单估算了预测区间。对于关键应用,应考虑使用更严格的统计方法(如predint函数,需统计工具箱)来计算预测置信区间。
通过这个完整的流程,你不仅学会了如何用MATLAB进行最小二乘拟合,更掌握了从数据探索、模型选择、参数估计、效果评估到实际应用的一整套数据分析方法论。这远比单纯记住几个函数调用要重要得多。