【机器学习】数值分析03——任意曲线拟合
数值分析<3>——任意曲线拟合
全文目录
万物皆是展开式
你是否想过这样一个问题,任何的函数,也许都能通过一个“大一统”理论将他们整合在一起。事实上对于任意一种对应关系,我们都能找到一个函数尽可能去贴合它,我们采取的是一种极限的思维,举个不恰当的例子来描述,如果一个东西看起来像牛粪,闻起来像牛粪,尝起来像牛粪,那么它就是牛粪。并且,更加一般的是,在一个微小的区间内,函数还可以写成一个幂函数的形式去大致的估算。
泰勒定理给了我们一个很通用的方法去研究函数:
设n是一个正整数。如果定义在一个包含a的区间上的函数
其中,上式的
如果仅仅只是背下来,你就不能领略到泰勒展开的精妙之处了。我们用一些很精妙的手段,自然的推导出泰勒公式。首先我们先假定一个函数存在一阶导函数,来看一看一阶导数的定义
其实从这里不难发现,导数定义上,给了我们一种当
余项估计
从上面的推导规程很明显可以看出最后一项无穷小是
当然我们会认为,一阶导数所带来的那个余项无穷小是不够精确的,提高精确值的方法就是让这个无穷小的阶升高,那么我们可以继续考虑二阶展开,让这个无穷小的阶数升高。首先我们来单独的研究一下这个无穷小。
很明显,只要我们的函数二阶可导,那么上述式子就满足了0/0型的洛必达法则!那么我们直接开始洛必达!
这样,我们就得到了拥有二阶无穷小的泰勒公式!如法炮制,只要我们的
接下来我们来估计余项,也就是误差的大小。在上面的推断中,我们一直使用无穷小进行误差的估计,这种类型的余项叫做皮亚诺余项,这种好处是可以很直观的看出误差的阶数,但是并不算是一个特别容易计算的值,我们仅仅知道最大的误差不超过
利用泰勒公式,我们就可以将一个可导函数的任意点进行一个拟合,从而得到一个较为精确的结果,从函数图像上看,泰勒公式是一个级数累加不断前进逼近的过程。
多项式插值法
如果针对的是一个有穷数列,例如请你寻找以下数字的规律并填写接下来的数字
- 4、7、11、18、29、47、(__)
你的第一眼可能会发现这是一个类似 Fibonacci数列的规律数字,但实际上我们对于有穷离散型函数,也可以推出这样一个奇怪的对应关系
并且对于任何的有穷的数列,我们总是能使用一个通项公式进行数据的拟合计算。如果我们把上述拓展到函数领域,也就是对于任意有限多的
插值的思想和泰勒很类似,就是说如果观测误差足够小的时候,那么就认为它就是准确的值。我们给定的点符合
通常我们采用累加的多项式进行插值计算,因为我们的计算都是建立在计算机之上,计算机对于特殊的运算是非常缓慢且复杂的,例如之前提到的泰勒展开,因此我们尽可能需要将计算变成四则运算,同时我们的插值函数还需要保证解的唯一性,因此我们通常将插值函数写成如下形式:
利用多项式幂函数和作为插值函数的好处是,它是恒可微的,并不需要像泰勒展开一样,需要先确定可微分的函数才可以继续求解。同时至于这种方式的唯一性,我们可以做一个简单的推导。
假设给定了n+1个数据采样
如果熟悉线性代数,我们可以很清楚的发现这是一个非齐次线性方程组(a为未知数),我们可以得到该方程组的
故有
因为我们取得采样点都是不同的点,因此可以保证有
拉格朗日插值法
拉格朗日插值是一种朴素拟合的手段,假设我们给定n个数据点
一次线性插值
我们先从最容易的一次线性插值来入手,假设我们有曲线
我们看一个简单的函数拟合,已知
实际上我们利用信息的角度去考虑,比如在警察办案过程中,往往会涉及到一个犯罪嫌疑人画像的制作,如果说目击者告诉警察的有效信息为:犯罪分子是一个高鼻梁,蓝眼睛。那么警察在绘制图像的时候,眼睛和鼻子或者周边的颧骨可能会相对精确不少,但是通过鼻子和眼睛的信息,你让警察去猜想他是否是秃顶或者升高体重,自然会不准确。所以这里面也涉及到了一部分信息熵的内容,因为信息过少而导致的数据不精确。
拉格朗日插值
通过一次插值的实践过程和之前对于多项式插值的,我们知道采样点很少导致的信息缺失,那么我们就应该找到更多的采样点进行构造更好的多项式。
根据观察,其实我们可以思考一下,之前多项式插值法中,我们最终是需要通过行列式解出各个多项式的系数,那么我们假定为这样的插值函数
我们将多项式系数写为
通过这个我们很容易分析出插值基函数
同时因为
因此我们得到了我们的插值基函数和我们的插值拟合函数
因此,我们可以编写出一程序,作为我们拉格朗日插值法的
double[] Lagrange(double[]x,double[] y,double[] xi)
{
double[] result = new double[xi.Length];
if (x.Length != y.Length)
{
throw new ArgumentException("dim x not equal dim y");
}
int n = x.Length;
for (int k = 0; k < xi.Length; k++)
{
double value = 0;
for (int i = 0; i <n; i++)
{
double mulValue = 1;
for (int j = 0; j < n; j++)
{
if (i == j)
continue;
mulValue = mulValue * (xi[k] - x[j]) / (x[i]- x[j]);
}
value += mulValue * y[i];
}
result[k] = value;
}
return result;
}
更有matlab、python版本的代码在仓库中可以对照使用。仓库地址见文章开头
*拉格朗日插值余项
既然是拟合,总归是有误差的,接下来我们研究一下最终的误差。对于拉格朗日插值法,误差实际上是一个阶段误差
那么我们作一辅助函数如下
容易发现
- 当
在插值区间内有n+2个互异零点时,则一阶导数至少有n+1个互异零点 - 当
在插值区间内有n+1个互异零点时,则 至少有n个互异零点
以此类推,容易知道
故我们插值的截断误差为
Newton差商插值
拉格朗日插值多项式的得出并不容易,至少在计算机眼中,他是复杂的,需要计算多个n-1阶的多项式。同时,这里我们给出一个定理:所有的插值多项式和拉格朗日插值多项式的结果应当是相同的,只是以不同的方式计算和书写。。
差商
首先我们介绍以下差商的概念,实际差商是一个很简单的东西,定义如下:假设顺序排列的节点
二阶差商为
那么K阶差商为:
k阶差商亦可写成
实际上就是个递推的公式,看起来似乎很复杂,但实际上自己尝试去计算的时候会发现,是一个很简单的东西,同时差商是函数的一个线性组合,这又可以说到线性代数了,我们假设组合出的多项式是一个类似坐标的玩意,而每一阶的差商实际上就是向量的基,因此通过差商,我们可以表示出多项式空间中任一多项式。
牛顿差商公式
牛顿差商公式就是将我们的差商做出组合:
随便给出几个插值采样点,进行计算拉格朗日和牛顿插值公式,我们在计算出各阶差商以后,会发现最终得到的插值多项式和拉格朗日多项式是一致的。那我们为什么还需要多此一举引入牛顿法进行插值而不是直接使用拉格朗日插值呢?
答案很简单,拉格朗日法在构建多项式时,我们必须先知道采样点的数量,如果后续有了新的采样点,我们就需要重新计算整个多项式,这是对于性能浪费极大的。如果采用牛顿法,那么在有新的采样点时,我们只需要多计算一次差商(因为之前的差商已经计算好),可以很容易的将新的点加入我们的多项式中。
牛顿法的代码如下(更多代码见仓库)
double Newton(double[] x, double[] y,double xi)
{
if (x.Length != y.Length)
{
throw new ArgumentException("dim x not equal dim y");
}
int n = x.Length;
double[,] args = new double[n,n];
for (int i = 0; i < n; i++)
{
args[i,0] = y[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
args[j,i] = (args[j - 1,i - 1] - args[j,i - 1]) / (x[j - i] - x[j]);
}
}
double result = y[0];
for (int i = 1; i < x.Length; i++)
{
double mulValue = 1.0;
for (int j = 0; j < i - 1; j++)
{
mulValue = mulValue * (xi - x[j]);
}
result += args[i, i] * mulValue;
}
return result;
}
插值余项
实际上,牛顿法因为得到的结果和拉格朗日插值一样,他们的误差也应该是一样的,不过既然我们选择了不同的计算方法,那么误差也可以使用新的手段进行计算。如果我们把我们的插值点看作一固定点,带入我们的差商公式,依次递推,会得到最后的插值余项。
高次插值的Runge(龙格)现象
在多项式插值以及基于多项式插值的拉格朗日插值方法和含有高次幂的牛顿插值中,我们提到过龙格现象,那么什么是龙格现象呢?首先我们先提出这样一个问题,当我们在拉格朗日多项式之中提高项数和多项式的次数,是否可以提高我们数据的精度?这个答案是否定的,我们需要减少误差,也就是说针对任意的
我们可以看到,我们的误差,随着n的增大,(n+1)!逐渐趋于无穷,而后向的乘法,并不会增大太多会变小太多,因此误差很大程度取决于n+1阶导数的范围。对于类似
假设给定
我们可以很清楚的发现,当靠近端点的时候。拟合曲线出现了剧烈的波动,误差到了一个不太可以接受的程度。并且次数越高的时候,抖动出现的更频繁、更剧烈。龙格现象的出现是由于插值节点是等距所造成的。同时,针对导函数不稳定的函数而言,盲目增加插值节点数列,则更加容易出现龙格现象。
一般而言,为了避免龙格现象,我们可以有以下的手段:
- 避免使用高次的插值多项式,一般而言,即使为了提高插值精度,也尽可能保证
。 - 分段进行插值,在每一小段使用一次插值,从而降低阶数。
- 对于性态不稳定的函数,选用其他插值手段,如Hermite插值等。
Hermite插值
在之前的插值方法中,我们的要求仅仅是在满足
最常见的是我们通常会要求曲线的一阶导数接近或相同,这样可以保证我们曲线的一个平滑性能。那么假设曲线
不难发现,我们不仅要求了相等的条件,更是要求了相切的条件,因此上述公式具有2(n+1)个采样点(曲线和曲线导数各一半),那么我们仿造拉格朗日插值条件。可以构造一个这样的插值多项式:
其中,需要满足:
接下来我们用比较长的篇幅证明推导以下我们的Hermite插值的基函数
首先我们来求
易知满足上述条件的该点为二重根,所以必有因子
易知满足上述条件的该点为单根,因此还会有另一个因子,
上述式子满足我们之前的规定,同时因为
同样的,因为
同样代入我们的条件,最终可以求得:
最后我们证明一下我们得到的插值多项式是唯一的,我们利用反证法:
假设存在另一个
由于有n+1个i,因此
Hermite插值余项
若我们的函数
做出辅助函数
根据定义易知
因此辅助函数至少有n+1个二重零点x_i,以及零点x,根据罗尔定理,容易知道在每两个二重零点之间都有一个零点,同时
n+2个零点,以此类推,那么有
所以余项公式为:
Hermite的代码如下(更多代码见仓库)
Chebyshev插值
我们之前谈论的插值函数通常采样点是一个平均分布的采样点,比如说我们一个实际的例子,我们需要采集子弹发射速度,我们往往会使用等间隔时间发射一颗子弹。但是往往等间隔这件事情本身就是有难度的,也就是说我们选取的点对结果是有影响的,如下图,在端点的时候,误差可能会达到一个无穷大的地步。切比雪夫插值就是采用最优点间距选取的插值方法。
切比雪夫插值的出发点就是在插值区间上,控制插值误差的上下界,即
将这个误差控制在绝对值为1以内,实际上这是一个关于x的多项式,那么我们的问题就是,这个多项式是否存在极值,是否有特定的
我们再考虑一个问题,我们既然要构造多项式,并且控制误差在1以内,那么考虑一下三角函数,因为三角有两个优秀的性质:
- 三角函数的值域在
- 根据傅里叶展开,我们知道曲线可以用三角函数进行拟合
因此如果区间在
我们利用切比雪夫根(建议学习切比雪夫微分方程),构造的多项式就是切比雪夫多项式,在此之前,我们看一看三角函数的展开:
通过观察,那么我们假设
,于是:
可以发现
我们就得到了切比雪夫多项式的递推公式。切比雪夫插值的代码如下:
两种曲线的拟合与预测
我们之前往往要求插值曲线严格的经过各个插值采样点,这个方式往往具有不可操控的地方。例如我们的采样点往往也只是一个粗略的估计,如果我们强行使插值曲线通过这些点,那么数据本身的误差也会反映在数据之中,例如下图中,使用一条直线去拟合获得的误差理论上要比经过这些点的曲线更加优秀。因此我们需要使用某种方式进行限制,这里我们介绍三种拟合方式。
直线拟合法
直线拟合是最为简单的拟合方法,也就是构造一套直线
- 准则一:使得误差最大绝对值最小
- 准则二:使得总误差的绝对值最小
- 准则三:使得误差平方和最小
其中准则三就是我们介绍的最小二乘法,我们就以准则三构造拟合的直线,那么设采样点有
容易发现我们的误差Q是关于a,b的函数,因此这里我们可以使用二元函数求导法则求得参数值。
联立上述式子可以解出a与b:
采用直线拟合法,则需要保证采样点大致像直线的分布,或者说均匀分布在直线两侧,如果不符合这个则尽量不使用直线拟合。
指数拟合
对于某些增长型的曲线,我们可以采用指数函数
利用这个方程,我们可以利用直线拟合的手段,确定系数。不过检验我们的拟合精度的指标略微不同,指标为:
- 误差总平方和尽可能小
- 标准差
- 相关系数
,R尽可能接近1
习题
Reference
《数值分析》(原书第二版) —— Timothy Sauer
《数值计算方法》(清华大学出版社) —— 吕同富等
About Me
作 者:WarrenRyan
出 处:https://www.cnblogs.com/WarrenRyan/
本文对应视频:BiliBili
关于作者:热爱数学、热爱机器学习,喜欢弹钢琴的不知名小菜鸡。
版权声明:本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。若需商用,则必须联系作者获得授权。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是作者坚持原创和持续写作的最大动力!
博主一些其他平台:
微信公众号:寤言不寐
QQ群:236996657
BiBili——小陈的学习记录
Github——StevenEco
BiBili——记录学习的小陈(计算机考研纪实)
掘金——小陈的学习记录
知乎——小陈的学习记录
联系方式
<a style="font-family: 微软雅黑; font-size: 18px;" href="mailto:cxtionch@gmail.com">
电子邮件:cxtionch@live.com </a>
<br/>
<br/>
社交媒体联系二维码:

本文来自博客园,作者:WarrenRyan,转载请注明原文链接:https://www.cnblogs.com/WarrenRyan/p/16733139.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步