[MATLAB 在科学计算中的应用] 使用MATLAB 进行非线性拟合
文章目录
[MATLAB 在科学计算中的应用] 使用MATLAB 进行非线性拟合前言引述MATLAB 曲线拟合函数简述一二维数据非线性拟合一维数据拟合例子二维数据拟合例子高维数据非线性拟合lsqcurvefitnlinfit 函数数据拟合工具箱的使用写在后面的话前言引述
所谓曲线拟合,就是给定数据和数据可能满足的带参函数表达,希望能找到一组最好的参数,是的这个函数能够“最大限度”地刻画给定的数据。在一维(自变量维数)的情况下,换句话说,就是给定平面上一些点,希望能找一条某种形式的曲线,使得它“尽可能”地通过这些点。
一些工程上的朋友,手头有一些数据,简单地,就希望通过拟合能得到一个不错的函数表达。他们并不在乎也不想去了解底层的算法,只是想要一个结果,这个时候呢,他们就可以利用 MATLAB 自带的函数达到他们的目的。
令他们头大的是,关于 MATLAB 的拟合,他们网上一查,出现了十几个甚至几十个相关的函数。他们懵逼了。一个问题是,他们不理解为什么一个软件要搞出这么多类似功能的函数。另外一个问题是,在众多的选择下,他们不知道该何去何从。
下面,我将来回答这两个问题。
MATLAB 曲线拟合函数简述
MATLAB 拟合曲线的函数有非常多,列举几个最最最常用的:
regress 和 polyfit 适用于线性函数的拟合。lsqcurvefit 和 nlinfit 用于较复杂的非线性函数的拟合。fit 适用于最常用函数的拟合,如指数函数,傅立叶函数、高斯函数、多项式函数、幂函数、有理函数等等。它更适用显式地给你函数表达式的情况。cftool 工具箱提供拟合的 GUI 界面,最大限度地体谅了不写程序的选手。
MathWorks 公司为什么要搞这么多似乎功能相同的函数呢?
这其实不怪他们。MATLAB 是个科学工程计算的软件,体量庞大,涉及的领域方方面面。MATLAB 有很多工具箱,每个函数都分属于不同的工具箱。按照分而治之的思想,每个工具箱可能各自有各自的发展,相对独立。这样呢,基于不同的背景和算法,他们可能就有了各自的,看似实现相同功能的模块。这就好比不同的汽车公司,都造轮子,造的轮子的外观和造轮子的方式不尽相同,但是最终都是要装到车上去的。
对于曲线拟合也是一样,有优化、统计、曲线拟合、机器学习等等人都需要用到拟合这个东西,所以在 MATLAB 的优化和统计等工具箱下面都有了各自的拟合实现,只是不同的地方可能叫法不一样。比如统计上喜欢叫“回归”。regress、nlinfit 等是统计工具箱下的,polyfit 是多项式工具箱下的,lsqcurvefit是优化工具箱下的,而 fit 和 cftool 都是曲线拟合工具箱下的。
因为大部分情况下,我们需要用到的是非线性拟合,线性拟合其实也是非线性拟合的特例,所以,下面,我们着重讨论非线性拟合。通过一些简单的例子来掌握非线性拟合工具,包括 fit、nlinfit、lsqcurvefit、cftool的选择和使用。简单地说,一二维的数据拟合,我们选择 fit (cftool),更高维数据的拟合,我们选择 lsqcurvefit 和 nlinfit。这里的维数,我们是对自变量说的。
下面我们都以例子开始进行说明,更详细的内容,请看 MATLAB 的 doc 文档。
一二维数据非线性拟合
一维数据拟合例子
假如我们现在有一组数据,横纵坐标分别用 x 和 y 表示。我们希望通过一个指数函数 y=aebx+cy = ae^{bx}+cy=aebx+c,对它进行拟合,那么我们就可以这么写:
clcclear%% 先造个数据用于测试x = 0:0.01:1;x = x';%fit函数只支持列向量y = 1*exp(2*x)+3;% scatter(x,y);% hold on;%% 输入函数形式,进行拟合ft = fittype('a*x*exp(b*x)+c');%给定函数表达coeffnames(ft);%查看model = fit(x,y,ft,'StartPoint',[1,3,5]);%StartPoint表示参数迭代的初值,不知道的情况下随便给一个就行%% 查看结果和可视化误差modelplot(model,x,y)
代码简洁明了,注释也很清楚,就不详加解释了。这里需要强调的一点是,fittype 函数默认使用 x 来表示自变量的值,依次使用 a、b、c…… 来表示第一个参数、第二个参数、第三个参数……这也就解释了为什么 fit 函数能够识别 ft 中变量哪些是自变量,哪些是参数,而不至于出问题。
结果如下:
从结果中可以看到,虽然得到 a、b、c 的值和真正的值结果有些差距,但是图上的拟合误差还是很小的。
二维数据拟合例子
二维和一维的 fit 的用法是一样的,不同的是,这个时候我们要让程序清楚哪个是第一自变量,哪个是第二自变量,哪些又是参数。要实现这一点,可以在 fittype 中用形如'coefficients',{'a1','a2'}
等方式指定系数,用 形如'independent','time'
的方式指定自变量。当然,更简单的方式,是直接采用匿名函数的方式。如下,还是举个例子。
假设我们要拟合 z=ax+beyz=ax+be^yz=ax+bey 这个函数,给定数据点 xxx 和 yyy ,那么用匿名函数来拟合,程序如下。
clcclear%% 先造个数据fun = @(a,b,x,y) a.*x+b.*exp(y);%注意这里要用点乘x = 0:0.01:1;y = 0:0.01:1;x = x';y = y';%一定要列向量z = fun(x,y,1,2);%% 对匿名函数拟合,绘图sf = fit([x,y],z,fun,'StartPoint',[1,3]);sfplot(sf,[x,y],z);
结果和一维的例子类似,我就不黏贴了。一二维的例子,用 fit 来拟合是极为简单的,但是 fit 也仅仅是支持最多二维的。fit 函数自动识别了 fun 函数的最后 n (n=1 or 2)个输入变量作为了自变量,其他输入变量自动顺序地成为参数。比如说,这个例子中,fun 输入参数表中的后两个变量x,yx,yx,y,刚好就对应了 fit 中的自变量 [x,y][x,y][x,y] ,而起始点对应关系为 a=1,b=3a=1,b=3a=1,b=3。这是 fit 函数对于参数指派的一种潜规则。
高维数据非线性拟合
lsqcurvefit
lsqcurvefit 用最小二乘求解非线性曲线拟合(数据拟合)问题。
c = lsqcurvefit(fun,c0,xdata,ydata)
从 c0 开始,求取合适的系数 c,使非线性函数 fun(x,xdata) 对数据 ydata 的拟合最佳(基于最小二乘指标)。ydata 必须与 fun 返回的向量(或矩阵)F 大小相同。
我们依然从一个例子开始学习。假设我们现在要拟合y=c1x1+c2sinx2+ec3x3y=c_1x_1+c_2\sin x_2+e^{c_3x_3}y=c1x1+c2sinx2+ec3x3,已知一组数组 X=[x1,x2,x3]X=[x_1,x_2,x_3]X=[x1,x2,x3] 和 yyy ,要求最佳的拟合参数 c1,c2,c3c_1,c_2,c_3c1,c2,c3,则一个简单的程序如下:
clcclearX = rand(10,3);fun = @(C,X) C(1)*X(:,1)+C(2)*sin(X(:,2))+exp(C(3)*X(:,3));y = fun([1,2,5.01],X);C0 = [1,2,3];C = lsqcurvefit(fun,C0,X,y);C
这里也用到了匿名函数,而且 lsqcurvefit 也有所谓的潜规则,即 fun 的第一个变量表示参数组,第二个变量表示变量组。这样最后能得到一组拟合系数。值得一提的是,这个方法是基于最小二乘的,如果初值选得合适,精度相当高。
nlinfit 函数
依然以 y=c1x1+c2sinx2+ec3x3y=c_1x_1+c_2\sin x_2+e^{c_3x_3}y=c1x1+c2sinx2+ec3x3 为例子。编程如下:
clcclearX = rand(10,3);fun = @(C,X) C(1)*X(:,1)+C(2)*sin(X(:,2))+exp(C(3)*X(:,3));y = fun([1,2,5.01],X);C0 = [1,2,30];C = nlinfit(X,y,fun,C0)
我们发现,在这个例子中,nlinfit 和 lsqcurvefit 有异曲同工之妙。
除了 nlinfit,统计工具箱中和拟合有关的函数还包括
betafit.m dfittool.m fitgmdist.m histfit.m nlinfit.p poissfit.m wblfit.m binofit.m distributionFitter.m gamfit.m lognfit.m nlmefit.m raylfit.m copulafit.m evfit.m gevfit.m mnrfit.m nlmefitoutputfcn.m robustfit.m copulafit.p expfit.m glmfit.m nbinfit.m nlmefitsa.m stepwisefit.m coxphfit.m fitdist.m gpfit.m nlinfit.m normfit.m unifit.m
等 。
数据拟合工具箱的使用
拟合工具箱,本质上不过是 fit 的一个 GUI 可视化界面而已。
下面我们依然用一个例子开始。
在命令行输入cftool
打开数据拟合工具箱。之后,从工作空间中选择自变量和因变量,即 x,y,zx,y,zx,y,z 变量。
鼠标点选你想要的拟合函数表达式即可。依然习惯用 a,b,c…a,b,c\ldotsa,b,c… 表示顺序的参数,用 x,yx,yx,y 表示自变量。这里我们选择 Custom Equation,输入我们要拟合的表达式。结果如下:
对于拟合得到的结果,我们可以保存数据到工作空间,也可以生成代码,还可以做对已有数据进行验证等等操作,这里就不再赘述了。生成代码如下:
function [fitresult, gof] = createFit(x, y, z)%CREATEFIT(X,Y,Z)% Create a fit.%% Data for 'untitled fit 1' fit:%X Input : x%Y Input : y%Z Output: z% Output:%fitresult : a fit object representing the fit.%gof : structure with goodness-of fit info.%% 另请参阅 FIT, CFIT, SFIT.% 由 MATLAB 于 22-Dec- 00:42:46 自动生成%% Fit: 'untitled fit 1'.[xData, yData, zData] = prepareSurfaceData( x, y, z );% Set up fittype and options.ft = fittype( 'a + b*sin(c*pi*x*y)+d+log(e*x+10)+f', 'independent', {'x', 'y'}, 'dependent', 'z' );opts = fitoptions( 'Method', 'NonlinearLeastSquares' );opts.Display = 'Off';opts.StartPoint = [0.189570472126967 0.108311946115659 0.59204493946 0.957384022595723 0.26532203619292 0.924580895239601];% Fit model to data.[fitresult, gof] = fit( [xData, yData], zData, ft, opts );% Plot fit with data.figure( 'Name', 'untitled fit 1' );h = plot( fitresult, [xData, yData], zData );legend( h, 'untitled fit 1', 'z vs. x, y', 'Location', 'NorthEast' );% Label axesxlabel xylabel yzlabel zgrid on
从这里也可以看出,工具箱的拟合本质上用的还是 fit 函数,只不过提供了一个界面。而且这里的拟合选项用的也是非线性最小二乘拟合。
所以这里的拟合自变量维数也无法超过 2。每个自变量都是一个向量,不能是矩阵。因此,拟合工具箱和 fit 函数函数有相同的局限性,即维数不能太高。
仔细观察工具箱你会发现,在自变量为一维的时候,它比起 fit 有个缺陷,就是不能自定义关于系数的非线性拟合函数。不过可以生成代码后再进行修改。
写在后面的话
综上所提,MATLAB 函数是默认地是接受输入表 “先参数后变量” 的形式。这点可以记住。
如果不追求速度的话,线性拟合和多项式拟合以及插值,当然也可以用非线性拟合的函数来做拟合。
那么,我们现在对于 MATLAB 拟合方法的选择,就有了一个思路。简便最优选择是:当你拟合的自变量维数是一维二维的时候,实用 cftool 拟合工具箱,生成代码,加以改写利用。当你拟合的自变量维数大等于三的时候,如果你和优化更近的话,用 lsqcurvefit 函数,如果你和统计更近的话,用 nlinfit 函数。