Оптимизация параметров для системы ODE с использованием scipy.differential_evolution завершается ошибкой из-за того, что resolve_ivp выбрасывает runtime_warnings

Я пытаюсь решить модель на Python и подогнать неизвестные параметры модели к экспериментальным данным. Модель состоит из двух ODE, и я решаю ее с помощью scipy.integrate.solve_ivp. Параметры модели неизвестны, поэтому я хочу подогнать их с помощью множества методов. Мой первый выбор - diff_evolution, так как он может обеспечить действительно хорошие результаты для гораздо более сложных моделей (раньше использовал его как часть другого пакета). Однако проблема заключается в том, что когда я даю свою модель дифференциальной эволюции (я хочу, чтобы она находила глобальный минимум для наименьших квадратов между расчетными и экспериментальными точками), она находит параметры, при которых модель становится нестабильной (LSODA не может интегрировать ее из-за внутреннего шаг равен 0). Я пытался поймать предупреждения среды выполнения, которые выдает 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)

person Alexey Martyanov    schedule 29.11.2020    source источник
comment
Ошибки в LSODA выглядят следующим образом: lsoda - указанное выше предупреждение было выдано i1 раз. он больше не будет выдаваться для этой проблемы в сообщении выше, i1 = 10 D: /Science/GPVI/GPV_v2/GPVI_PyModel/Equations.py: 11: RuntimeWarning: обнаружено переполнение в double_scalars dGPVIdt = - k1 * GPVI_Clust * GPVI + k_1 * (GPVI_0 - GPVI) - 2 * k2 * GPVI * GPVI D: /Science/GPVI/GPV_v2/GPVI_PyModel/Equations.py: 12: RuntimeWarning: обнаружено недопустимое значение в double_scalars dGPVI_Clustdt = - (kust_2 * GPVI_Clust) * + GPVI_Clust k2 * GPVI * GPVI   -  person Alexey Martyanov    schedule 29.11.2020
comment
Добавьте print(parameters,output) в конце res_squares, чтобы увидеть, есть ли у остатка какие-либо полезные значения. // Уравнения квадратные, поэтому при определенном выборе параметров возможно расхождение в бесконечность на интервале интегрирования.   -  person Lutz Lehmann    schedule 15.12.2020


Ответы (1)


Есть несколько способов сделать это, и, вероятно, для получения стабильного решения необходимо их сочетание:

  1. Избегайте комбинаций параметров, которые приводят к нестабильному числовому поведению. Текущие диапазоны, которые вы устанавливаете для параметров, очень широки. Я уверен, что у вас есть хотя бы немного знаний о порядке величины параметров, чтобы вы могли несколько больше ограничить пространство параметров. Если вам неудобно устанавливать жесткие границы, я бы предложил использовать байесовский подход и установить предыдущие распределения, которые не исключают полностью очень большие или маленькие значения, но делают их менее вероятными. (Если вы пойдете вторым путем, я могу порекомендовать пакет PyMC3 для построения байесовской модели в сочетании с Sunode для интеграции ODE.)
  2. Постарайтесь сделать решение более стабильным в числовом отношении. Что раньше мне очень помогало в подобных ситуациях, так это логарифмическое преобразование ODE. Это означает, что вместо решения dy/dt = f(y) вы решаете dx/dt = exp(-x) f(exp(x)), где x = log(y). (Это можно получить, применив цепное правило дифференцирования к dlog(y)/dt.)
  3. Одна из проблем в вашем случае, похоже, заключается в том, что решатель не останавливается и не выдает ошибку, когда не может интегрироваться дальше. Вместо этого он, кажется, продолжает бесконечно пытаться следующий шаг интеграции (?). Использование аргумента minstep решателя LSODA может помочь решить эту проблему. Установка min_step=1e-14, по крайней мере, позволяет оптимизатору пройти первые 11 этапов алгоритма дифференциальной эволюции. После этого ни решатель, ни оптимизатор не выводят никаких сообщений, поэтому я не уверен, что происходит.

Кстати, вы можете передать t_eval=timepoints в solve_ivp, чтобы решатель уже возвращал решение в нужные вам моменты времени. Это избавит вас от интерполяции, которую вы делаете сейчас.

person astoeriko    schedule 15.12.2020