You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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。


解决方案

核心问题分析

  1. Model方法无法计算置信区间:外部exe生成的函数通常是黑盒,不可导,lmfit无法自动计算Jacobian矩阵,导致无法得到参数的标准误差(stderr),进而无法计算置信区间。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:44:53