scipy.optimize.curve_fit чувствителен к форме функции.Каковы правила? - PullRequest
0 голосов
/ 22 октября 2018

scipy.optimize.curve_fit отлично работает с функцией, но задыхается от эквивалентной функции.Пример:

def func(x, a, b, c, d):
#   return np.exp(a + b * (c -d * x ) ) # works fine
    return a * np.exp( b * (c -d * x ) ) # gives error

Ошибка в ковариационной матрице:

params= [ 1.16507769 13.26573913  5.90351144  6.24181411]
cov=
 [[-2.16168732e+13  2.55685110e+12  2.64410274e+11
-1.20320851e+12]
 [ 6.54220223e+12  7.78321447e+11 -7.70863674e+11 
-3.66264006e+11]
 [-1.50943415e+12 -5.12305287e+11  3.25950385e+11  
2.41081648e+11]
 [-3.07864319e+12 -3.66264061e+11  3.62754606e+11  1.72357248e+11]]

C:\Python34\hsf\pandas\sample.py:119: RuntimeWarning: 
invalid value encountered in sqrt
perr = np.sqrt(np.diag(pcov))
perr= [ nan 882225.28145113 570920.64728287 415159.3038493 ]

Предполагается, что первым числом в ковариационной матрице является дисперсия, сумма квадратов.Как это может быть отрицательным ???

Версия, которая работает без ошибок, дает то, что выглядит как идентичный график и

params= [ 4.32069414 39.26093245  1.8885566   2.10902333]
cov=
 [[ 3.82867157e+14 -3.12417211e+14  5.27625493e+12  1.67824840e+13]
 [-2.81245333e+14  2.17844627e+14 -3.31542721e+12 -1.17022169e+13]
 [ 3.77680355e+12 -2.52146086e+12  2.50916226e+10  1.35448290e+11]
 [ 1.51079875e+13 -1.17022170e+13  1.78098719e+11  6.28621803e+11]]
perr= [19566991.51220365 14759560.52776919   158403.35405783   792856.73527642]

1 Ответ

0 голосов
/ 23 октября 2018

У вас есть два избыточных параметра.Версия функции, которую вы называете находящейся работой, это exp(a + b*(c - d*x)).Выражение

a + b*(c - d*x)

можно записать

a + b*c - b*d*x = A + B*x

, где A = a + b*c и B = -b*d.Таким образом, вы можете упростить вашу функцию до exp(A + B*x).Проблема с наличием слишком большого количества параметров состоит в том, что решение не является изолированным - пространство решений имеет то же измерение, что и число избыточных параметров.Это приводит к тому, что матрица Гессе является единственной.Из-за обычной числовой неточности матрица Гессе в избыточном случае не будет точно единственного числа, но будет плохо обусловлена ​​и почти единственной.Ковариационная матрица получена из обратной матрицы Гессе, поэтому, если матрица Гессиана близка к единственному, вычисление ковариации является численно нестабильным и не должно вызывать доверия.

Вот полный скрипт, демонстрирующийПроблема:

import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt


def func(x, a, b, c, d):
    return np.exp(a + b*(c - d*x))


def func2(x, a, b):
    return np.exp(a - b*x)


np.random.seed(8675309)
x = np.linspace(0, 10, 16)
y = (10*np.exp(-0.5*x)*np.random.lognormal(sigma=0.25, size=len(x))
     + 0.5*np.random.rand(len(x)))

p, pcov = curve_fit(func, x, y)
print("p:", p)
print("pcov:")
print(pcov)
print()

p2, pcov2 = curve_fit(func2, x, y)
print("p2:", p2)
print("pcov2:")
print(pcov2)


plt.plot(x, y, 'bo', label="data")

xx = np.linspace(0, 10, 101)

yy = func(xx, *p)
plt.plot(xx, yy, 'k--', label="4 parameter fit")

yy2 = func2(xx, *p2)
plt.plot(xx, yy2, 'g', linewidth=4, alpha=0.3, label="2 parameter fit")

plt.legend(shadow=True)
plt.grid(True)
plt.xlabel('x')

plt.show()

Сценарий генерирует следующий график:

plot

Использовали ли мы два параметра или четыре, curve_fitнашел то же решение.

Вот распечатка скрипта:

p: [1.15386327 1.18656718 1.09383746 0.47713239]
pcov:
[[-2.43958994e+12 -7.92454940e+12  9.37347302e+12  3.19227931e+12]
 [-3.98689093e+13 -1.05598664e+13  4.33789687e+13  4.25387501e+12]
 [ 3.88631613e+13  1.64330041e+13 -4.79524254e+13 -6.61977557e+12]
 [ 1.60605592e+13  4.25387505e+12 -1.74745310e+13 -1.71360622e+12]]

p2: [2.45177495 0.56614914]
pcov2:
[[0.00143014 0.00077371]
 [0.00077371 0.00130843]]

pcov, результат подбора из четырех параметров, по сути, мусор.В этом примере все диагональные элементы отрицательны, а матрица не симметрична.

pcov2, ковариационная матрица для подгонки двух параметров в порядке.

Мораль этой истории: не используйте избыточные параметры в функции модели, если вам нужно использовать ковариационную матрицу, возвращаемую curve_fit.


Кстати, как вы уже отметили,Вы можете переписать exp(A + B*x) как exp(A)*exp(B*x), а затем определить C = exp(A), чтобы выразить свою функцию как C*exp(B*x).Затем вы можете использовать параметры B и C вместо A и B в curve_fit.Просто помните, что эта версия позволяет C быть меньше или равным 0, поэтому возможен ответ, такой как -2*exp(-3*x).Отрицательная функция, подобная этой, невозможна при использовании exp(A + B*x).Чтобы обе параметризации имели одинаковые возможные решения, вы должны использовать аргумент bounds для ограничения C.

...