Python使用curve_fit拟合多元幂函数模型未达预期,求修正
修正幂函数拟合的问题
你的代码存在几个关键错误,导致拟合结果完全不符合预期,以下是具体修正步骤:
1. 模型函数定义错误
curve_fit要求模型函数的第一个参数是自变量,后续是待拟合的参数。你当前的函数把参数放在前面,还直接引用全局变量,完全不符合API要求。正确的函数应该接收自变量(qt、fs、ov)和拟合参数(a、b、c、d):
def func(x, a, b, c, d): qt, fs, ov = x # 解包自变量 return a * (qt ** b) * (fs ** c) * (ov ** d)
2. curve_fit调用参数错误
你当前调用curve_fit(func, Vs, d)是把Vs当成自变量、d当成因变量,和你目标拟合的Vs = a*(qt^b)*(fs^c)*(ov^d)完全无关。正确的做法是把qt、fs、ov作为自变量打包传入,Vs作为因变量:
# 打包自变量 xdata = (qt, fs, ov) # 执行拟合 popt, pcov = curve_fit(func, xdata, Vs)
3. 幂函数拟合的收敛优化
由于你的自变量数值范围差异极大(比如fs从0.1到229.6,qt是几千级别),直接拟合幂函数容易导致拟合不收敛。可以先通过对数转换转成线性模型,得到初始参数估计,再传入curve_fit帮助收敛:
import numpy as np # 对数转换,将幂函数转为线性模型:ln(Vs) = ln(a) + b*ln(qt) + c*ln(fs) + d*ln(ov) ln_Vs = np.log(Vs) ln_qt = np.log(qt) ln_fs = np.log(fs) ln_ov = np.log(ov) # 构造线性回归的特征矩阵 X = np.column_stack((ln_qt, ln_fs, ln_ov)) y = ln_Vs # 线性回归求初始参数 from sklearn.linear_model import LinearRegression reg = LinearRegression() reg.fit(X, y) # 转换回原参数 initial_a = np.exp(reg.intercept_) initial_b = reg.coef_[0] initial_c = reg.coef_[1] initial_d = reg.coef_[2] p0 = [initial_a, initial_b, initial_c, initial_d] # 用初始参数执行拟合 popt, pcov = curve_fit(func, xdata, Vs, p0=p0)
4. 修正绘图逻辑
你原来的绘图是把Vs和d数组关联,这和目标拟合无关。正确的绘图应该对比原始Vs和拟合得到的Vs_pred:
Vs_pred = func(xdata, *popt) plt.scatter(range(len(Vs)), Vs, label="原始数据") plt.scatter(range(len(Vs_pred)), Vs_pred, label="拟合数据") plt.legend() plt.show()
完整修正代码
import matplotlib.pyplot as plt from scipy.optimize import curve_fit import numpy as np from sklearn.linear_model import LinearRegression # 原始数据 qt = [10867.073, 8074.986, 2208.366, 3066.566, 2945.326, 4795.766, 2249.813, 2018.3] fs = [229.6, 17.4, 5.3, 0.1, 0.1, 0.1, 0.1, 0.1] ov = [19.159, 29.054, 37.620, 44.854, 51.721, 58.755, 65.622, 72.492] Vs = [149.787, 125.3962, 133.927, 110.047, 149.787, 137.809, 201.506, 154.925] # 定义正确的模型函数 def func(x, a, b, c, d): qt, fs, ov = x return a * (qt ** b) * (fs ** c) * (ov ** d) # 对数转换获取初始参数 ln_Vs = np.log(Vs) ln_qt = np.log(qt) ln_fs = np.log(fs) ln_ov = np.log(ov) X = np.column_stack((ln_qt, ln_fs, ln_ov)) y = ln_Vs reg = LinearRegression() reg.fit(X, y) initial_a = np.exp(reg.intercept_) initial_b = reg.coef_[0] initial_c = reg.coef_[1] initial_d = reg.coef_[2] p0 = [initial_a, initial_b, initial_c, initial_d] # 打包自变量并执行拟合 xdata = (qt, fs, ov) popt, pcov = curve_fit(func, xdata, Vs, p0=p0) # 输出拟合参数 print(f"a: {popt[0]:.6f}, b: {popt[1]:.6f}, c: {popt[2]:.6f}, d: {popt[3]:.6f}") # 计算拟合值并绘图 Vs_pred = func(xdata, *popt) plt.figure(figsize=(10,6)) plt.scatter(range(len(Vs)), Vs, color='red', label="原始Vs数据") plt.plot(range(len(Vs_pred)), Vs_pred, color='blue', label="拟合Vs曲线") plt.xlabel("样本索引") plt.ylabel("Vs值") plt.legend() plt.show()
额外注意点
- 避免参数名和数据变量名冲突:你原来的代码里参数
d和数据数组d重名,容易导致逻辑混乱,建议重命名参数(比如把参数d改成e)。 - 如果你的实际需求是拟合d和其他变量的关系,需要重新明确目标方程,当前代码是基于你给出的
Vs = a*(qt^b)*(fs^c)*(ov^d)修正的。
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

