使用scipy.differential_evolution对ODE系统进行参数优化由于解决了_ivp抛出运行时_警告而失败

2024-10-01 00:16:01 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图用python解决一个模型,并将模型的未知参数与实验数据相匹配。该模型由2个ODE组成,我使用scipy.integrate.solve_ivp进行求解。模型的参数是未知的,所以,我想用大量的方法来拟合它们。我的第一个选择是差分进化,因为它可以为更复杂的模型提供非常好的结果(早些时候作为另一个包的一部分使用)。然而,问题是,当我给出微分进化时,我的模型(我希望它找到计算点和实验点之间最小二乘的全局最小值),它找到了模型变得不稳定的参数(由于内部步长为0,LSODA无法对其进行积分)。我试图捕捉LSODA在这些情况下抛出的运行时警告,但没有帮助。解决这个问题的最佳方式是什么

我的型号和代码如下:

def aggregation_model(time, z, k1, k2, k3, k_1, k_2):
    GPVI_0 = 55.5
    GPVI, GPVI_Clust = z

    dGPVIdt = - k1 * GPVI_Clust * GPVI + k_1 * (GPVI_0 - GPVI) - 2 * k2 * GPVI * GPVI
    dGPVI_Clustdt = - (k_2 * GPVI_Clust + k3) * GPVI_Clust + k2 * GPVI * GPVI

    return [dGPVIdt, dGPVI_Clustdt]


def res_squares(parameters):

    time = np.linspace(0, 300, 1000)

    timepoints = [0, 25, 50, 75, 100, 125, 150, 300]
    val = [0, 2, 5, 10, 15, 20, 20, 18]

    model_calc = solve_ivp(aggregation_model, [0, 300], [55.5, 0], args=parameters, max_step=100000,
                           dense_output=True, method='LSODA', rtol=1e-12, atol=1e-6)

    solution = model_calc.sol(time)
    transposed = list(map(list, zip(*solution.T)))

    size = [0, ]
    for i in range(1, len(transposed[0])):
        size.append((55.5 - transposed[0][i]) / transposed[1][i])

    diff = []
    for i in range(len(timepoints)):
        indexval = min(range(len(time)), key=lambda j: abs(time[j] - timepoints[i]))
        diff.append((val[i] - size[indexval])**2)

    # print(np.sqrt(np.sum(diff)))

    output = np.sum(diff)

    return output

if __name__ == '__main__':

    from scipy.integrate import solve_ivp
    from scipy.optimize import differential_evolution
    import numpy as np


    parameterBounds = []
    parameterBounds.append([-1000000, 1000000])  # parameter bounds for a
    parameterBounds.append([-1000000, 1000000])  # parameter bounds for a
    parameterBounds.append([-1000000, 1000000])  # parameter bounds for a
    parameterBounds.append([-1000000, 1000000])  # parameter bounds for a
    parameterBounds.append([-1000000, 1000000])  # parameter bounds for a

    result = differential_evolution(res_squares, parameterBounds, seed=3, disp=True, init='random')
    print(result.x)

    sol = solve_ivp(aggregation_model, [0, 300], [55.5, 0], args=parvalues,
                    dense_output=True, method='LSODA', rtol=1e-6, atol=1e-12)

Tags: 模型foroutputmodelparametertimenpsolve
1条回答
网友
1楼 · 发布于 2024-10-01 00:16:01

有几种方法可以实现这一点,可能需要将它们结合起来才能获得稳定的解决方案:

  1. 避免导致数值行为不稳定的参数组合。您为参数设置的当前范围非常广泛。我相信您至少对参数的数量级有一点了解,这样您就可以更严格地约束参数空间。如果您对设置硬边界感到不舒服,我建议您采用贝叶斯方法并设置先验分布,这些分布不会完全排除非常大或很小的值,但会降低它们的可能性。(如果您选择后一种方式,我可以推荐PyMC3包与Sunode一起构建贝叶斯模型来进行ODE集成。)
  2. 尝试使解在数值上更稳定。在以前类似的情况下,ODE的日志转换对我帮助很大。这意味着,不是求解dy/dt = f(y),而是求解dx/dt = exp(-x) f(exp(x)),其中x = log(y)。(这可以通过对dlog(y)/dt应用微分链规则得出。)
  3. 您案例中的一个问题似乎是,当解算器无法进一步集成时,解算器不会停止并抛出错误。相反,它似乎永远在继续尝试下一个集成步骤(?)。使用LSODA解算器的minstep参数可能有助于解决这个问题。设置min_step=1e-14至少可以让优化器通过差分进化算法的前11个阶段。之后,解算器和优化器都不会打印任何消息,因此我不确定发生了什么

顺便说一下,您可能希望将t_eval=timepoints传递给solve_ivp,这样解算器就可以在您需要的时间点返回解。这将节省您当前执行的插值

相关问题 更多 >