曲线拟合

曲线拟合

理论在WIKIPEDIA里介绍的很详细

Curve fitting - Wikipedia

数据拟合的最小二乘法

数据拟合的最小二乘问题是:根据给定的数据组 ( x i , y i ) , i = 1 , 2 , … , n , (x_i,y_i),i = 1,2,\dots,n, (xi,yi),i=1,2,,n,选取近似函数形式,即给定函数类 φ ( x ) ∈ H \varphi(x) \in H φ(x)H,使得
∑ i = 1 n δ i 2 = ∑ i = 1 n [ y i − φ ( x i ) ] 2 \sum\limits_{i = 1}^{n}\delta_i^2 = \sum\limits_{i = 1}^{n}[y_i - \varphi(x_i)]^2 i=1nδi2=i=1n[yiφ(xi)]2
最小,即
∑ i = 1 n [ y i − φ ( x i ) ] 2 = min ⁡ φ ∈ H ∑ i = 1 n [ y i − ϕ ( x i ) ] 2 \sum\limits_{i = 1}^{n}[y_i - \varphi(x_i)]^2 = \min\limits_{\varphi \in H}\sum\limits_{i = 1}^{n}[y_i - \phi(x_i)]^2 i=1n[yiφ(xi)]2=φHmini=1n[yiϕ(xi)]2
函数 φ ( x ) \varphi(x) φ(x)称为这组数据的最小二乘函数,通常取 H H H为一些比较简单函数的集合,如低次多项式,指数函数等。通常是先把已知数据先绘图出来,观察其数据分布特征,从直观上选择用什么拟合函数合适,当然,很多时候也会有分成几段使用不同拟合函数来交叉拟合的情况。

我是从二次多项式( φ ( x ) = a 0 + a 1 x + a 2 x 2 \varphi(x) = a_0 + a_1x + a_2x^2 φ(x)=a0+a1x+a2x2)拟合举例说明这个曲线拟合到底是个什么流程。个人感觉理解效果会好点。

观察数据特征
import numpy as np
import matplotlib.pyplot as plt 
x = np.array([1,3,4,5,6,7,8,9,10]).reshape(-1,1)
y = np.array([10,5,4,2,1,1,2,3,4]).reshape(-1,1)
plt.plot(x,y)
plt.plot(x,y,'ro', label = 'Original data')
plt.legend()

在这里插入图片描述

选取拟合函数

通过观察上图结果,我们其实肉眼就可以看出来,有点像我们学过的二次函数。所以这里我们选择二次多项式来拟合。
φ ( x ) = a 0 + a 1 x + a 2 x 2 \varphi(x) = a_0 + a_1x + a_2x^2 φ(x)=a0+a1x+a2x2
其中, φ ( x 1 ) = 1 , φ ( x 2 ) = x , φ ( x 3 ) = x 2 \varphi(x_1) = 1,\varphi(x_2) = x,\varphi(x_3) = x^2 φ(x1)=1,φ(x2)=x,φ(x3)=x2

def fun(x,a0,a1,a2):
    y = a0 + a1 * x + a2 * x**2
    return y
拟合

正则方程组为:

Φ T Φ a = Φ T y \Phi^T \Phi a = \Phi^Ty ΦTΦa=ΦTy
其中,
Φ T Φ = ( ( φ 0 , φ 0 ) ( φ 0 , φ 1 ) … ( φ 0 , φ n ) ( φ 1 , φ 0 ) ( φ 1 , φ 1 ) … ( φ 1 , φ n ) ⋮ ⋱ ⋮ ( φ n , φ 0 ) ( φ n , φ 1 ) … ( φ n , φ n ) ) \Phi^T \Phi = \left(\begin{array}{ccc} (\varphi_0,\varphi_0) & (\varphi_0,\varphi_1) & \dots & (\varphi_0,\varphi_n) \\ (\varphi_1,\varphi_0) & (\varphi_1,\varphi_1) & \dots & (\varphi_1,\varphi_n) \\ \vdots & \ddots & \vdots \\ (\varphi_n,\varphi_0) & (\varphi_n,\varphi_1) & \dots & (\varphi_n,\varphi_n) \end{array}\right) ΦTΦ=(φ0,φ0)(φ1,φ0)(φn,φ0)(φ0,φ1)(φ1,φ1)(φn,φ1)(φ0,φn)(φ1,φn)(φn,φn)
a = [ a 1 , a 2 , … , a n ] T a = [a_1,a_2,\dots,a_n]^T a=[a1,a2,,an]T

根据最小二乘估计
a = ( Φ T Φ ) − 1 Φ T y a =(\Phi^T \Phi)^{-1}\Phi^Ty a=(ΦTΦ)1ΦTy

def get_params(x,y):
    phi_matrix = np.hstack((np.ones(x.shape[0]).reshape(-1,1),x,x ** 2))
    A = phi_matrix.T @ phi_matrix
    b = phi_matrix.T @ y
    params = np.linalg.pinv(A) @ b
    return params
params = get_params(x,y)
print(params)
[[13.45966387]
 [-3.6053094 ]
 [ 0.26757066]]

根据上述求解参数,拟合函数为
ϕ ( x ) = 13.45966387 − 3.6053094 x + 0.26757066 x 2 \phi(x) = 13.45966387 -3.6053094x + 0.26757066x^2 ϕ(x)=13.459663873.6053094x+0.26757066x2

带入拟合函数,求一下根据我们拟合函数得到的y值,记为y_fitted

y_fitted = fun(x,params[0],params[1],params[2])
print(y_fitted)
[[10.12192513]
 [ 5.05187166]
 [ 3.31955691]
 [ 2.1223835 ]
 [ 1.46035141]
 [ 1.33346066]
 [ 1.74171123]
 [ 2.68510313]
 [ 4.16363636]]

其实,直接直观看值,我们就可以发现,拟合效果已经很好了,误差都比较小。但为了更清晰的看到结果,我们通过MAPE(%)来看一下拟合效果。

def mape(y,y_fitted):
    e = (sum(abs((y - y_fitted)/y)) / len(y)) * 100
    return e
print(mape(y,y_fitted)[0],"%")
14.69667260842274 %

画图对比一下原始数据图像与拟合效果图

plt.plot(x,y)
plt.plot(x,y,'ro', label = 'Original data')
plt.plot(x,y_fitted,'-b', label ='Fitted')
plt.legend()
plt.show()

在这里插入图片描述

可以看到,拟合效果还是比较好的,当然,现在的工具是非常多的。如果不是要了解原理都话,直接调用命令也是非常不错的。下面给出一些拟合的直接调用方法。也根据标准的对照参考一下,刚刚我用理论求解得到的结果正确性。

numpy ployfit包,多项式拟合
import numpy as np
import matplotlib.pyplot as plt 
x = np.array([1,3,4,5,6,7,8,9,10])
y = np.array([10,5,4,2,1,1,2,3,4])
plt.plot(x,y)

params = np.polyfit(x, y, deg = 2)
print(params)

def fun(x):
    y = params[0]*x**2 + params[1]*x + params[2]
    return y
y_fitted = fun(x)

print(mape(y,y_fitted),"%")
plt.plot(x,y,'ro', label = 'Original data')
plt.plot(x,y_fitted,'-b', label ='Fitted data')
plt.legend()
plt.show()
[ 0.26757066 -3.6053094  13.45966387]
14.696672608437163 %

在这里插入图片描述

可以看到,和我前面写的结果是一模一样的。但是这里有一点细节要注意,这里的拟合函数形式是
φ ( x ) = a 0 x 2 + a 1 x + a 2 \varphi(x) = a_0x^2 + a_1x + a_2 φ(x)=a0x2+a1x+a2 和我前面写的 φ ( x ) = a 0 + a 1 x + a 2 x 2 \varphi(x) = a_0 + a_1x + a_2x^2 φ(x)=a0+a1x+a2x2要注意区别哪个参数是哪项的参数。

Scipy的leastsq和curve_fit拟合

直接掉包的事情,直接看代码,这里参考了知乎大佬的,

Python-最小二乘法曲线拟合 - 知乎 (zhihu.com)

我自己懒没有写,只是稍微改了丢丢哈哈哈哈

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
 
def Fun(p,x):                        
    a1,a2,a3 = p
    return a1*x**2+a2*x+a3
def error (p,x,y):                   
    return Fun(p,x)-y 
def fit():
    x = np.array([1,3,4,5,6,7,8,9,10])
    p_value = [1,1,50] # 原始数据的参数,随便设定
    y = np.array([10,5,4,2,1,1,2,3,4])
    plt.plot(x,y)
    p0 = [0.1,-0.01,100] # 拟合的初始参数设置,随便设定
    para =leastsq(error, p0, args=(x,y)) 
    y_fitted = Fun (para[0],x) 
 
    plt.plot(x,y,'r', label = 'Original data')
    plt.plot(x,y_fitted,'-b', label ='Fitted data')
    plt.legend()
    plt.show()
    return (para[0])
print(fit())
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
 
def Fun(x,a1,a2,a3):                   # 定义拟合函数形式
    return a1*x**2+a2*x+a3
def error (p,x,y): # 拟合残
    return Fun(p,x)-y
def fit():
    x = x = np.array([1,3,4,5,6,7,8,9,10])
    p_value = [1,1,50] # 原始数据的参数,随便设定
    y = np.array([10,5,4,2,1,1,2,3,4])
    p0 = [0.1,-0.01,100] # 拟合的初始参数设置
    para,pcov=curve_fit(Fun,x,y)
    y_fitted = Fun(x,para[0],para[1],para[2]) # 画出拟合后的曲线
 
    plt.figure
    plt.plot(x,y,'r', label = 'Original curve')
    plt.plot(x,y_fitted,'-b', label ='Fitted curve')
    plt.legend()
    plt.show()
    return para
print(fit())

两者实现的结果和之前的都是一样的。如图所示。但是考虑的求解角度稍微不同,后面两者是基于scipy随便设定参数值一步一步通过迭代找最最优参数实现的。其中,

  1. curve_fit

是在method =‘lm’(底层应该是lapack) 的情况下,该算法通过Minimumsq使用Levenberg-Marquardt算法。 注意,该算法只能处理不受约束的问题。

  1. leastsq

leastsq 是MINPACK的lmdif和lmder算法的包装,cov_x是最小二乘目标函数的Hessian的雅可比近似。 该近似值假设目标函数基于某些观测目标数据(ydata)与参数f(xdata,params)的(非线性)函数之间的差 。
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值