如何拟合并绘制平滑的Sigmoid函数曲线?拟合结果异常求助
Sigmoid曲线拟合异常:得到非平滑类平方曲线的解决方法
问题描述
尝试为现有数据拟合并绘制Sigmoid曲线,但得到的是类似非平滑的类平方曲线。使用的代码如下:
import numpy as np import pyplot from scipy.optimize import curve_fit def sigmoid(x, a, b): return 1.0 / (1.0 + np.exp(-a*(x-b))) xdata = [ 26457.28874429, 32614.0743368 , 36375.76229015, 39371.21198926, 41892.69310906, 44162.12906554, 46223.90776821, 48197.46060769, 50022.99546123, 51848.03215704, 53568.91252972, 55354.78073918, 57040.61643994, 58818.59692639, 60676.00000216, 62510.8110985 , 64586.59777512, 66562.62315247, 68742.97216949, 71078.33629888, 73648.94810531, 76587.27871678, 79749.19546409, 83334.78854123, 87282.81262044, 91925.69906738, 97551.59686331, 104496.29453508, 112988.76665607, 124907.20130917, 144733.52923071, 193062.81222137] ydata = [0. , 0.57224724, 0.67521945, 0.76716544, 0.7535795 , 0.69637917, 0.81766149, 0.80906626, 0.71942446, 0.7670196 , 0.77549438, 0.80370093, 0.77160494, 0.78232104, 0.87796313, 0.89326037, 1.05848115, 1.08499096, 1.13992674, 1.16009281, 1.36193537, 1.58227848, 1.78731153, 1.98694295, 2.19359185, 2.51098556, 2.92349108, 3.47826087, 3.82897412, 4.23472474, 4.19622344, 5.19657584] popt, pcov = curve_fit(sigmoid, xdata, ydata, method='dogbox', bounds=([min(ydata), min(xdata)],[max(ydata), max(xdata)])) print(popt) x = np.linspace(np.min(xdata), np.max(xdata)) y = sigmoid(x, *popt) pyplot.plot(xdata, ydata, 'o', label='data') pyplot.plot(x,y, label='fit') pyplot.legend(loc='best') pyplot.show()
问题根源与修复方案
你的Sigmoid函数定义和拟合参数设置存在两个核心问题:
Sigmoid函数的取值范围不匹配
你当前的Sigmoid函数1.0 / (1.0 + np.exp(-a*(x-b)))输出范围固定为(0,1),但你的ydata取值范围是0到5.2左右,完全超出这个区间,导致拟合算法无法找到合理参数,最终输出错误曲线。修复:扩展Sigmoid函数,加入上下限参数,适配任意y值范围:
def sigmoid(x, a, b, c, d): # c=下限,d=上限,a=斜率,b=中点 return c + (d - c) / (1.0 + np.exp(-a*(x - b)))拟合参数的边界设置错误
原代码中bounds只给了两个参数的边界,且将斜率参数a的边界设为ydata范围,这完全不合理——a是控制曲线陡峭程度的参数,和y值范围无关。修复:重新设置合理边界,同时提供初始猜测值帮助算法收敛:
# 初始猜测值:a设为极小正数,b取x均值,c取y最小值,d取y最大值 p0 = [1e-5, np.mean(xdata), np.min(ydata), np.max(ydata)] # 边界限制:a>0,b在x范围内,c略小于y最小值,d略大于y最大值 bounds = ([1e-6, np.min(xdata), -np.inf, np.max(ydata)*0.9], [1e-4, np.max(xdata), np.min(ydata)*1.1, np.max(ydata)*1.1]) popt, pcov = curve_fit(sigmoid, xdata, ydata, p0=p0, bounds=bounds, method='dogbox')
完整修复后的代码
import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit def sigmoid(x, a, b, c, d): return c + (d - c) / (1.0 + np.exp(-a*(x - b))) xdata = [ 26457.28874429, 32614.0743368 , 36375.76229015, 39371.21198926, 41892.69310906, 44162.12906554, 46223.90776821, 48197.46060769, 50022.99546123, 51848.03215704, 53568.91252972, 55354.78073918, 57040.61643994, 58818.59692639, 60676.00000216, 62510.8110985 , 64586.59777512, 66562.62315247, 68742.97216949, 71078.33629888, 73648.94810531, 76587.27871678, 79749.19546409, 83334.78854123, 87282.81262044, 91925.69906738, 97551.59686331, 104496.29453508, 112988.76665607, 124907.20130917, 144733.52923071, 193062.81222137] ydata = [0. , 0.57224724, 0.67521945, 0.76716544, 0.7535795 , 0.69637917, 0.81766149, 0.80906626, 0.71942446, 0.7670196 , 0.77549438, 0.80370093, 0.77160494, 0.78232104, 0.87796313, 0.89326037, 1.05848115, 1.08499096, 1.13992674, 1.16009281, 1.36193537, 1.58227848, 1.78731153, 1.98694295, 2.19359185, 2.51098556, 2.92349108, 3.47826087, 3.82897412, 4.23472474, 4.19622344, 5.19657584] # 设置初始猜测值和边界 p0 = [1e-5, np.mean(xdata), np.min(ydata), np.max(ydata)] bounds = ([1e-6, np.min(xdata), -np.inf, np.max(ydata)*0.9], [1e-4, np.max(xdata), np.min(ydata)*1.1, np.max(ydata)*1.1]) popt, pcov = curve_fit(sigmoid, xdata, ydata, p0=p0, bounds=bounds, method='dogbox') print("拟合参数:", popt) # 生成更多点数让曲线更平滑 x = np.linspace(np.min(xdata), np.max(xdata), 1000) y = sigmoid(x, *popt) plt.plot(xdata, ydata, 'o', label='原始数据') plt.plot(x, y, label='拟合曲线') plt.legend(loc='best') plt.show()
额外说明
- 原代码中的
import pyplot需改为import matplotlib.pyplot as plt,否则会出现导入错误。 - 增加
linspace的点数(如1000)可让拟合曲线显示更平滑。 - 初始猜测值
p0的设置能帮助curve_fit快速收敛,避免陷入局部最优解。
内容的提问来源于stack exchange,提问作者Marlon Teixeira
相关产品推荐
相关产品推荐

