Python中Sigmoid函数曲线拟合与外推效果不佳求解决方案
问题描述
给定如下数据集,需用Python的curve_fit工具对y从1下降到0的曲线进行校准与外推:
import numpy as np from scipy.optimize import curve_fit import matplotlib.pyplot as plt # 给定数据点 xdata = np.array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]) ydata = np.array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.99330715, 0.98201379, 0.95257413, 0.88079708, 0.73105858, 0.5]) # 绘制数据 plt.plot(xdata, ydata) plt.xlabel('x') plt.ylabel('sigmoid(x)') plt.xlim([-1,31]) plt.ylim(0, 1.05) plt.show()
数据特征为:前21个x对应的y值保持1,从x=21开始y逐渐下降,到x=26时降至0.5。
尝试使用Sigmoid函数(公式:$y = \frac{1}{1+e^{k(x-x_0)}}$),但选择了用y求解x的反向函数拟合,代码如下:
# 定义用于拟合xdata的Sigmoid函数 def sigmoid(y, x0, k): x = x0 + ((1/k)*(np.log((1/y)-1))) return x # 初始猜测参数 p0 = [np.median(xdata), # x0 0.1] # k # 初始化曲线拟合 popt, pcov = curve_fit(sigmoid, ydata, xdata) # 定义y的取值范围 y = np.arange(1,0,-0.001) # 计算对应的x值 x = sigmoid(y, *popt) # 绘制实际数据与拟合曲线 plt.plot(xdata, ydata, 'o', label='data') plt.plot(x,y, label='fit') plt.xlim([-10,31]) plt.ylim(0, 1.05) plt.legend(loc='best') plt.show()
拟合效果极差,曲线无法贴合实际数据,需改进方法得到贴合的拟合曲线。
解决方案
拟合失效的核心问题是拟合方向错误,同时原数据中大量y=1的点会导致反向函数计算报错($\log((1/1)-1)=\log(0)$无意义),以下是修正步骤:
1. 使用正向Sigmoid函数拟合(y关于x)
回归原始Sigmoid公式,定义y作为x的函数,符合curve_fit常规逻辑(输入自变量x,输出因变量y):
def sigmoid(x, x0, k): return 1 / (1 + np.exp(k * (x - x0)))
其中x0是曲线中点(y=0.5对应的x值),k控制曲线陡峭程度,下降趋势下k为负数。
2. 处理y=1的特殊点
原数据中前21个点y=1,直接代入会引发数值问题,可选择两种处理方式:
- 给y=1的点添加极小偏移(如
1 - 1e-8),避免$\log(0)$; - 仅使用y<1的点拟合,再外推到全范围。
3. 优化初始参数猜测
根据数据特征,x0应接近y=0.5对应的x=26,k设为负数,初始参数可设为p0=[26, -0.5],比中位数更贴合数据趋势。
完整修正代码
import numpy as np from scipy.optimize import curve_fit import matplotlib.pyplot as plt # 给定数据点 xdata = np.array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]) ydata = np.array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.99330715, 0.98201379, 0.95257413, 0.88079708, 0.73105858, 0.5]) # 处理y=1的点,添加极小偏移 ydata_adjusted = np.where(ydata == 1, 1 - 1e-8, ydata) # 定义正向Sigmoid函数 def sigmoid(x, x0, k): return 1 / (1 + np.exp(k * (x - x0))) # 优化初始参数:x0接近y=0.5的x=26,k为负数(下降趋势) p0 = [26, -0.5] # 执行拟合 popt, pcov = curve_fit(sigmoid, xdata, ydata_adjusted, p0=p0) print(f"拟合参数:x0={popt[0]:.2f}, k={popt[1]:.2f}") # 生成拟合曲线的x范围 x_fit = np.linspace(-1, 35, 1000) y_fit = sigmoid(x_fit, *popt) # 绘制结果 plt.plot(xdata, ydata, 'o', label='原始数据') plt.plot(x_fit, y_fit, '-', label='拟合曲线') plt.xlabel('x') plt.ylabel('y') plt.xlim([-1, 35]) plt.ylim(0, 1.05) plt.legend(loc='best') plt.show()
效果说明
修正后的拟合曲线会完美贴合x≥21的下降段,同时外推x<21时y趋近于1,完全匹配原始数据的平台+下降特征;若仅用y<1的点拟合,结果会更精准,因为前21个y=1的点本身就符合Sigmoid左半段的平台趋势。
内容的提问来源于stack exchange,提问作者NN_Developer
相关产品推荐
相关产品推荐

