使用lmfit拟合函数获取参数置信区间时遇错误求助
问题:lmfit拟合外部exe生成的函数后无法获取参数置信区间
背景
使用lmfit拟合一个调用C++生成exe的函数func(x, region, E0, C0, R1, R3, R4, R5, R6, R7, R8, alpha, beta, rho, theta, delta, d),拟合过程正常但无法获取参数置信区间。其中x为0-78的浮点数/数组,函数通过exe输出流返回对应函数值,拟合数据total_data_array的尺寸和值均确认正确。
现有拟合代码
import numpy as np import lmfit import subprocess import codecs # 定义拟合函数 def func(x,region,E0,C0,R1,R3,R4,R5,R6,R7,R8,alpha,beta,rho,theta,delta,d):#x should be between 0 and 75 print("Function called") print(E0,C0,R1,R3,R4,R5,R6,R7,R8,alpha,beta,rho,theta,delta,d) # 处理单个x值的情况 if not hasattr(x,'__iter__'): x = np.array([x]) result = [] # 调用外部exe获取结果 output = subprocess.check_output([r'...\model.exe', str(region),str(E0),str(C0),str(R1),str(R3),str(R4),str(R5),str(R6),str(R7),str(R8),str(alpha),str(beta),str(rho),str(theta),str(delta),str(d)]) output_str = codecs.decode(output) output_str_list = output_str.split('\r\n') output_str_list.pop() dataarray = [] index = 0 for word in output_str_list: if index in range(416,624) or index in range(728,832): if word == '-nan(ind)': dataarray.append(0.0) else: dataarray.append(float(word)) index+=1 # 匹配x对应的函数值 for x0 in x: y = dataarray[int(x0*4)] result.append(y) return result # 设置拟合参数 parameters = lmfit.Parameters() parameters.add('region',value=1) parameters.add('E0',value=500,min=0.0,max = 10000.0) parameters.add('C0',value=200,min=0.0,max = 10000.0) parameters.add('R1',value=0.587,min=0.0,max=1.0) parameters.add('R3',value=0.3125,min=0.0,max=1.0) parameters.add('R4',value=0.1666,min=0.0,max=1.0) parameters.add('R5',value=0.1,min=0.0,max=1.0) parameters.add('R6',value=0.2,min=0.0,max=1.0) parameters.add('R7',value=0.4,min=0.0,max=1.0) parameters.add('R8',value=0.125,min=0.0,max=1.0) parameters.add('alpha',value=0.09,min=0.0,max=1.0) parameters.add('beta',value=0.25,min=0.0,max=1.0) parameters.add('rho',value=0.2,min=0.0,max=1.0) parameters.add('theta',value=0.26,min=0.0,max=1.0) parameters.add('delta',value=0.77,min=0.0,max=1.0) parameters.add('d',value=0.99,min=0.0,max=1.0) parameters['region'].vary = False xData = np.arange(0, 78, 1) # 拟合执行正常 my_model = lmfit.Model(func) results = my_model.fit(total_data_array,params=parameters,x=xData)
尝试的方法及错误
方法1:基于现有Model计算置信区间
添加代码:
ci = lmfit.conf_interval(my_model,results) lmfit.report_ci(ci)
错误信息:
lmfit.minimizer.MinimizerException: Cannot determine Confidence Intervals without sensible uncertainty estimates
即使固定多数参数只保留2个可变参数,错误依然存在,拟合报告显示无法估计参数不确定性。
方法2:使用Minimizer计算置信区间
添加代码:
min = lmfit.Minimizer(func,params=parameters,fcn_args=np.arange(0,78, 1)) min.minimize(method='leastsq') ci = lmfit.conf_interval(min) lmfit.report_ci(ci)
错误信息:TypeError: func() takes 17 positional arguments but 79 were given;将fcn_args改为args后出现ValueError: The truth value of an array with more than one element is ambiguous。
解决方案
核心问题分析
- Model方法无法计算置信区间:外部exe生成的函数通常是黑盒,不可导,lmfit无法自动计算Jacobian矩阵,导致无法得到参数的标准误差(stderr),进而无法计算置信区间。
- Minimizer方法使用错误:Minimizer要求目标函数返回残差数组(数据-模型值),而非模型值;同时参数传递方式错误,导致参数数量不匹配。
方案1:修正Minimizer的使用
步骤1:修改为残差函数
将原拟合函数改为返回残差(数据与模型值的差值):
def residual(params, x, data): # 提取参数值 region = params['region'].value E0 = params['E0'].value C0 = params['C0'].value R1 = params['R1'].value R3 = params['R3'].value R4 = params['R4'].value R5 = params['R5'].value R6 = params['R6'].value R7 = params['R7'].value R8 = params['R8'].value alpha = params['alpha'].value beta = params['beta'].value rho = params['rho'].value theta = params['theta'].value delta = params['delta'].value d = params['d'].value # 原func的计算逻辑,生成模型值 if not hasattr(x,'__iter__'): x = np.array([x]) output = subprocess.check_output([r'...\model.exe', str(region),str(E0),str(C0),str(R1),str(R3),str(R4),str(R5),str(R6),str(R7),str(R8),str(alpha),str(beta),str(rho),str(theta),str(delta),str(d)]) output_str = codecs.decode(output) output_str_list = output_str.split('\r\n') output_str_list.pop() dataarray = [] index = 0 for word in output_str_list: if index in range(416,624) or index in range(728,832): if word == '-nan(ind)': dataarray.append(0.0) else: dataarray.append(float(word)) index+=1 model_vals = [] for x0 in x: y = dataarray[int(x0*4)] model_vals.append(y) # 返回残差:数据 - 模型值 return np.array(data) - np.array(model_vals)
步骤2:正确使用Minimizer
# 初始化Minimizer,传递残差函数、参数、x数据和拟合数据 minimizer = lmfit.Minimizer(residual, parameters, fcn_args=(xData, total_data_array)) # 执行拟合,强制计算协方差矩阵 result = minimizer.minimize(method='leastsq', calc_covar=True) # 查看拟合报告,确认参数有stderr值 print(lmfit.fit_report(result)) # 计算并输出置信区间 ci = lmfit.conf_interval(minimizer, result) lmfit.report_ci(ci)
方案2:优化Model方法的拟合参数
如果坚持使用Model方法,需指定数值导数或使用支持数值导数的拟合方法,让lmfit能计算参数的标准误差:
# 使用Nelder-Mead方法(无需导数)并强制计算协方差 results = my_model.fit(total_data_array, params=parameters, x=xData, method='nelder', calc_covar=True) # 或用L-BFGS-B方法,指定两点数值导数 # results = my_model.fit(total_data_array, params=parameters, x=xData, method='lbfgsb', jac='2-point') # 查看拟合报告确认stderr存在 print(lmfit.fit_report(results)) # 计算置信区间 ci = lmfit.conf_interval(my_model, results) lmfit.report_ci(ci)
额外注意事项
- 若参数过多存在共线性,会导致协方差矩阵无法计算,需固定对拟合结果影响较小的参数,或添加正则化约束。
- 确保外部exe的输出稳定,避免出现NaN或异常值干扰拟合的数值稳定性。
内容的提问来源于stack exchange,提问作者Paul Joh
相关产品推荐
相关产品推荐

