使用scipy.odr拟合曲线:解决曲线过平及参数异常问题
解决SciPy ODR拟合曲线不贴合、参数异常的问题
问题背景
我最近在尝试用依赖mudr(xdata)和dmss22(sdata)的自定义函数拟合一组实验数据时,遇到了两个棘手的问题:
- 拟合曲线过于平缓,完全贴合不了数据点的趋势
- 用
scipy.odr拟合后,参数结果完全不符合物理预期:- 拟合函数中的
p[0]*X[1]项本应远小于1,但得到的p[0]是1e-18量级,和dmss22(1e+17量级)相乘后完全不满足条件 p[0]为负值,导致函数呈现下降趋势,和数据的上升趋势完全相反
- 拟合函数中的
我最初使用的拟合代码如下:
import numpy as np from math import pi, sqrt, log from scipy import optimize import matplotlib.pyplot as plt from scipy.optimize import curve_fit from scipy.odr import * # 数据准备 mudr=np.array([ 57.43708609, 46.26119205, 55.60688742, 33.21615894, 28.27072848, 22.54649007, 21.80662252, 11.21483444, 5.80211921]) dme=np.array([ 128662.54890776, 105265.32915726, 128652.56835434, 77968.67019573, 66273.56542068, 58464.58559543, 54570.66624991, 27286.90038703, 19480.92689266]) dmss22=np.array([ 4.90050000e+17]*9) dmse=np.array([ 1.09777592e+21, 1.11512117e+21, 1.13381702e+21, 1.15033267e+21, 1.14883089e+21, 1.27076265e+21, 1.22637165e+21, 1.19237598e+21, 1.64539205e+21]) F=np.array([ 115.01944248, 110.24354867, 112.77812389, 104.81830088, 104.35746903, 101.32016814, 100.54513274, 96.94226549, 93.00424779]) dF=np.array([ 72710.75386699, 72590.6256987 , 176539.40403673, 130555.27503081, 124299.52080164, 176426.64340597, 143013.52848306, 122117.93022746, 157547.78395513]) # 自定义拟合函数(注:afij为提前初始化的数组) def Ffitsso(p,X,B=2.58,Fc=92.2,mu=770,Za=0.9468): temp1 = (2*B*X[0])/(4*pi*Fc)**2 temp2 = temp1*(afij[0]+afij[1]*np.log((2*B*X[0])/mu**2)) temp3 = temp1**2*(afij[2]+afij[3]*np.log((2*B*X[0])/mu**2)+afij[4]*(np.log((2*B*X[0])/mu**2))**2) temp4 = temp1**3*(afij[5]+afij[6]*np.log((2*B*X[0])/mu**2)+afij[7]*(np.log((2*B*X[0])/mu**2))**2+afij[8]*(np.log((2*B*X[0])/mu**2))**3) return Fc/Za*(1+p[0]*X[1])*(1+temp2+temp3+temp4)+p[1] # ODR拟合(初始版本) xtot=np.row_stack( (mudr, dmss22) ) etot=np.row_stack( (dme, dmse) ) fitting = Model(Ffitsso) mydata = RealData(xtot, F, sx=etot, sy=dF) myodr = ODR(mydata, fitting, beta0=[0, 100]) myoutput = myodr.run() myoutput.pprint() bet=myoutput.beta plt.plot(mudr,F,"b^") plt.plot(mudr,Ffitsso(bet,[mudr,dmss22])) plt.show()
问题排查与解决
经过反复测试和调整,我发现问题的核心在于ODR拟合对初始参数值beta0非常敏感:
- 最初设置的
beta0=[0, 100],对于p[0]来说初始值离真实值太远,拟合算法陷入了局部最优解,导致参数异常、曲线趋势错误 - 结合物理预期(
p[0]*X[1]远小于1,且p[0]应为正值),我将初始参数调整为beta0=[1.5e-15, 100]——这个值是根据X[1](1e+17量级)估算的,确保p[0]*X[1]在1e-2左右,符合预期范围
调整后的关键拟合代码片段:
# 调整初始参数后的ODR拟合 myodr = ODR(mydata, fitting, beta0=[1.5e-15, 100]) myoutput = myodr.run() myoutput.pprint() bet=myoutput.beta plt.plot(mudr,F,"b^") plt.plot(mudr,Ffitsso(bet,[mudr,dmss22])) plt.show()
效果验证
调整初始参数后,拟合结果完全符合预期:
- 拟合曲线完美贴合数据点的趋势
p[0]变为正值,且量级合适,p[0]*X[1]远小于1,满足物理约束
内容的提问来源于stack exchange,提问作者Linus
相关产品推荐
相关产品推荐

