如何用Scipy curve_fit拟合曲线?遇参数协方差估计错误
问题分析与解决方案
核心问题
- 模型趋势与数据完全不符:你定义的
func(x, a, b, c) = a*np.exp(-b*x) + c是递减曲线(当b>0时,x越大,值越小),但你的数据是递增趋势(从0.5逐步增长到1),这是拟合失败的根本原因。 - x值过大导致数值溢出问题:x的取值范围是6529-6629,直接代入
exp(-b*x)会因为x过大,结果趋近于0,优化器无法找到有效的参数组合,最终只能拟合出水平线(a≈0,结果为c)。 - 未提供初始参数猜测:
curve_fit默认初始参数为[1,1,1],对于你的数据来说完全不合理,导致优化器收敛到局部最优。
解决方案
1. 修正模型为递增的饱和指数曲线
改用符合数据趋势的模型,即指数增长趋近于饱和值的形式,这样x越大,y越接近饱和值,和你的数据特征匹配:
def func_scaled(x_scaled, a, b, c): # x_scaled是中心化后的x,c是饱和值,a是初始差值,b是增长速率 return c - a * np.exp(-b * x_scaled)
2. 对x进行中心化处理
将x减去最小值,缩小数值范围,让指数项有合理的变化空间,避免数值溢出:
x_min = xdata.min() x_scaled = xdata - x_min
3. 提供合理的初始参数猜测
根据数据特征给出初始值,帮助优化器快速收敛:
c:数据最大值接近1,初始值设为1a:初始值设为c - ydata.min(),即1 - 0.547 ≈ 0.45b:设为小正数0.1(控制增长快慢)
完整代码
import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit # 定义修正后的拟合模型 def func_scaled(x_scaled, a, b, c): return c - a * np.exp(-b * x_scaled) # 原始数据 x = [6529,6530,6531,6532,6533,6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548,6549,6550,6551,6552,6553,6554,6555,6556,6557,6558,6559,6560,6561,6562,6563,6564,6565,6566,6567,6568,6569,6570,6571,6572,6573,6574,6575,6576,6577,6578,6579,6580,6581,6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6593,6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604,6605,6606,6607,6608,6609,6610,6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,6622,6623,6624,6625,6626,6627,6628,6629] y = [0.547409184506862,0.548439334507089,0.548707663683029,0.549517457040911,0.549928046272648,0.552133076225856,0.553680404362389,0.554546610865359,0.556183559635298,0.55702393264449,0.558127603440548,0.56005701419833,0.567268696243044,0.567959158455945,0.56888383955428,0.570002946460703,0.571738099634388,0.571781066314887,0.572492904162659,0.573363360104158,0.575401312020501,0.579556244995718,0.581790310757611,0.583406644934125,0.583431253838386,0.584646128487338,0.591634693424932,0.593621181449664,0.596918227952608,0.597537299010122,0.597822020010253,0.598891783912097,0.599584877929425,0.600869677067233,0.605499427101361,0.60658392374002,0.607408603951367,0.608003672935112,0.612816417541406,0.614176393253985,0.615691612727725,0.617134841882831,0.624502603183639,0.627504005062751,0.62811483139368,0.630923224681103,0.631913519350306,0.632861774084856,0.633396927216081,0.634723100574364,0.636823036518848,0.637335872514631,0.641989703420432,0.645848889736627,0.65711344945379,0.657128729116295,0.663572015593525,0.663936607768306,0.664261916284895,0.665497047151865,0.675768594810369,0.676207425367557,0.67770213122942,0.684362066388147,0.686091459831405,0.68923449405901,0.696555953074396,0.699523852803358,0.700114629853266,0.700439968032363,0.70422912133774,0.709054418203755,0.718323675919034,0.720874110082631,0.731420313805414,0.740339970645214,0.743941832408661,0.744098264483215,0.755637715557576,0.770607412727517,0.772170144147719,0.77386595160397,0.782346429315201,0.793536926034814,0.799814727138212,0.811806170429752,0.820389958104833,0.823442687233217,0.836431157138314,0.837230603981482,0.84457830759536,0.849472002016762,0.853064650303987,0.864143132487712,0.875848713252245,0.879457500784825,0.906226501241044,0.938124902423877,0.957251151492073,0.984140865987764,1] xdata = np.array(x) ydata = np.array(y) # 中心化x,缩小数值范围 x_min = xdata.min() x_scaled = xdata - x_min # 初始参数猜测 p0 = [0.45, 0.1, 1.0] # 执行拟合 popt, pcov = curve_fit(func_scaled, x_scaled, ydata, p0=p0) a_fit, b_fit, c_fit = popt # 生成拟合曲线 y_fit = func_scaled(x_scaled, *popt) # 可视化结果 plt.figure(figsize=(10,6)) plt.plot(xdata, ydata, 'b.', label='原始数据') plt.plot(xdata, y_fit, 'r-', label=f'拟合曲线: a={a_fit:.3f}, b={b_fit:.3f}, c={c_fit:.3f}') plt.xlabel('x') plt.ylabel('y') plt.legend() plt.show() # 如需对原始x求导,转换为原始x的模型并定义导数函数 def func_original(x, a, b, c, x_min): return c - a * np.exp(-b * (x - x_min)) def dfunc_original(x, a, b, c, x_min): return a * b * np.exp(-b * (x - x_min)) # 示例:计算x=6550处的导数 x_example = 6550 derivative = dfunc_original(x_example, a_fit, b_fit, c_fit, x_min) print(f"x={x_example}处的导数: {derivative:.6f}")
效果说明
- 修正后的模型完全匹配数据递增并趋近于饱和的趋势,拟合结果会和你期望的示例图一致。
- 中心化处理解决了x值过大导致的数值问题,
curve_fit可以正常估计参数和协方差,不会再出现警告。 - 初始参数引导优化器直接向合理方向收敛,避免陷入局部最优解。
内容的提问来源于stack exchange,提问作者la.poubelle
相关产品推荐
相关产品推荐

