Почему scipy.optimize.curve_fit не соответствует данным?

Я некоторое время пытался подобрать экспоненту к некоторым данным, используя scipy.optimize.curve_fit, но у меня возникли реальные трудности. Я действительно не вижу причин, по которым это не сработает, но это просто создает прямую линию, понятия не имею, почему!

Любая помощь приветствуется

from __future__ import division
import numpy
from scipy.optimize import curve_fit
import matplotlib.pyplot as pyplot

def func(x,a,b,c):
   return a*numpy.exp(-b*x)-c


yData = numpy.load('yData.npy')
xData = numpy.load('xData.npy')

trialX = numpy.linspace(xData[0],xData[-1],1000)

# Fit a polynomial 
fitted = numpy.polyfit(xData, yData, 10)[::-1]
y = numpy.zeros(len(trailX))
for i in range(len(fitted)):
   y += fitted[i]*trialX**i

# Fit an exponential
popt, pcov = curve_fit(func, xData, yData)
yEXP = func(trialX, *popt)

pyplot.figure()
pyplot.plot(xData, yData, label='Data', marker='o')
pyplot.plot(trialX, yEXP, 'r-',ls='--', label="Exp Fit")
pyplot.plot(trialX,   y, label = '10 Deg Poly')
pyplot.legend()
pyplot.show()

введите здесь описание изображения

xData = [1e-06, 2e-06, 3e-06, 4e-06,
5e-06, 6e-06, 7e-06, 8e-06,
9e-06, 1e-05, 2e-05, 3e-05,
4e-05, 5e-05, 6e-05, 7e-05,
8e-05, 9e-05, 0.0001, 0.0002,
0.0003, 0.0004, 0.0005, 0.0006,
0.0007, 0.0008, 0.0009, 0.001,
0.002, 0.003, 0.004, 0.005,
0.006, 0.007, 0.008, 0.009, 0.01]

yData = 
[6.37420666067e-09, 1.13082012115e-08,
1.52835756975e-08, 2.19214493931e-08, 2.71258852882e-08, 3.38556130078e-08, 3.55765277358e-08,
4.13818145846e-08, 4.72543475372e-08, 4.85834751151e-08, 9.53876562077e-08, 1.45110636413e-07,
1.83066627931e-07, 2.10138415308e-07, 2.43503982686e-07, 2.72107045549e-07, 3.02911771395e-07,
3.26499455951e-07, 3.48319349445e-07, 5.13187669283e-07, 5.98480176303e-07, 6.57028222701e-07,
6.98347073045e-07, 7.28699930335e-07, 7.50686502279e-07, 7.7015576866e-07, 7.87147246927e-07,
7.99607141001e-07, 8.61398763228e-07, 8.84272900407e-07, 8.96463883243e-07, 9.04105135329e-07,
9.08443443149e-07, 9.12391264185e-07, 9.150842683e-07, 9.16878548643e-07, 9.18389990067e-07]

person user1696811    schedule 25.03.2013    source источник
comment
Я получаю несколько ошибок, когда пытаюсь запустить ваш код: сначала trialX написано с ошибкой, а затем я получаю ошибку operands could not be broadcast together with shapes. Вы уверены, что это ваш точный код?   -  person David Robinson    schedule 26.03.2013
comment
@DavidRobinson: чтобы решить проблему с операндами, убедитесь, что xData и yData оба равны ndarray.   -  person DSM    schedule 26.03.2013


Ответы (3)


Численные алгоритмы, как правило, работают лучше, когда им не подаются очень маленькие (или большие) числа.

В этом случае график показывает, что ваши данные имеют чрезвычайно малые значения x и y. Если вы масштабируете их, соответствие будет заметно лучше:

xData = np.load('xData.npy')*10**5
yData = np.load('yData.npy')*10**5

from __future__ import division

import os
os.chdir(os.path.expanduser('~/tmp'))

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

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


xData = np.load('xData.npy')*10**5
yData = np.load('yData.npy')*10**5

print(xData.min(), xData.max())
print(yData.min(), yData.max())

trialX = np.linspace(xData[0], xData[-1], 1000)

# Fit a polynomial 
fitted = np.polyfit(xData, yData, 10)[::-1]
y = np.zeros(len(trialX))
for i in range(len(fitted)):
   y += fitted[i]*trialX**i

# Fit an exponential
popt, pcov = optimize.curve_fit(func, xData, yData)
print(popt)
yEXP = func(trialX, *popt)

plt.figure()
plt.plot(xData, yData, label='Data', marker='o')
plt.plot(trialX, yEXP, 'r-',ls='--', label="Exp Fit")
plt.plot(trialX, y, label = '10 Deg Poly')
plt.legend()
plt.show()

введите здесь описание изображения

Обратите внимание, что после масштабирования xData и yData параметры, возвращаемые curve_fit, также должны быть масштабированы. В этом случае a, b и c необходимо разделить на 10**5, чтобы получить подходящие параметры для исходных данных.


Одно возражение, которое у вас может возникнуть в связи с вышеизложенным, заключается в том, что масштабирование должно быть выбрано довольно "тщательно". (Читайте: не каждый разумный выбор масштаба работает!)

Вы можете повысить надежность curve_fit, предоставив разумное начальное предположение для параметров. Обычно у вас есть некоторые априорные знания о данных, которые могут служить основанием для предположений относительно разумных значений параметров.

Например, вызов curve_fit с

guess = (-1, 0.1, 0)
popt, pcov = optimize.curve_fit(func, xData, yData, guess)

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

person unutbu    schedule 25.03.2013
comment
Это намного лучше! есть ли причина, по которой ему не нравятся маленькие числа? - person user1696811; 26.03.2013
comment
Я недостаточно внимательно изучил алгоритм curve_fit's, чтобы точно сказать вам, почему. Но в целом эти алгоритмы должны проверять предположение о значениях параметров, а затем корректировать предположение. Размер начальной настройки может работать хорошо, если данные имеют величину около 1, но может полностью превзойти правильный ответ, если данные имеют величину около 10**-6. - person unutbu; 26.03.2013
comment
@unutbu Вы были правы в том, что первоначальное предположение было около 1. Из docs.scipy.org/doc/scipy/reference/generated/ p0 : None, scalar, or M-length sequence Initial guess for the parameters. If None, then the initial values will all be 1 (if the number of parameters for the function can be determined using introspection, otherwise a ValueError is raised). Где scipy.optimize.curve_fit(f, xdata, ydata, p0=None, sigma=None, **kw)[source] - person ffledgling; 21.08.2013

(Небольшое) улучшение этого решения, не учитывающее априорное знание данных, может заключаться в следующем: возьмите обратное среднее значение набора данных и используйте его в качестве «масштабного коэффициента», который будет передан в базовый метод наименьших квадратов(). вызывается функцией curve_fit(). Это позволяет установщику работать и возвращает параметры в исходном масштабе данных.

Соответствующая строка:

popt, pcov = curve_fit(func, xData, yData)

который становится:

popt, pcov = curve_fit(func, xData, yData,
    diag=(1./xData.mean(),1./yData.mean()) )

Вот полный пример, который создает это изображение:

curve_fit без ручного масштабирования данных или результатов

from __future__ import division
import numpy
from scipy.optimize import curve_fit
import matplotlib.pyplot as pyplot

def func(x,a,b,c):
   return a*numpy.exp(-b*x)-c


xData = numpy.array([1e-06, 2e-06, 3e-06, 4e-06, 5e-06, 6e-06,
7e-06, 8e-06, 9e-06, 1e-05, 2e-05, 3e-05, 4e-05, 5e-05, 6e-05,
7e-05, 8e-05, 9e-05, 0.0001, 0.0002, 0.0003, 0.0004, 0.0005,
0.0006, 0.0007, 0.0008, 0.0009, 0.001, 0.002, 0.003, 0.004, 0.005
, 0.006, 0.007, 0.008, 0.009, 0.01])

yData = numpy.array([6.37420666067e-09, 1.13082012115e-08,
1.52835756975e-08, 2.19214493931e-08, 2.71258852882e-08,
3.38556130078e-08, 3.55765277358e-08, 4.13818145846e-08,
4.72543475372e-08, 4.85834751151e-08, 9.53876562077e-08,
1.45110636413e-07, 1.83066627931e-07, 2.10138415308e-07,
2.43503982686e-07, 2.72107045549e-07, 3.02911771395e-07,
3.26499455951e-07, 3.48319349445e-07, 5.13187669283e-07,
5.98480176303e-07, 6.57028222701e-07, 6.98347073045e-07,
7.28699930335e-07, 7.50686502279e-07, 7.7015576866e-07,
7.87147246927e-07, 7.99607141001e-07, 8.61398763228e-07,
8.84272900407e-07, 8.96463883243e-07, 9.04105135329e-07,
9.08443443149e-07, 9.12391264185e-07, 9.150842683e-07,
9.16878548643e-07, 9.18389990067e-07])

trialX = numpy.linspace(xData[0],xData[-1],1000)

# Fit a polynomial
fitted = numpy.polyfit(xData, yData, 10)[::-1]
y = numpy.zeros(len(trialX))
for i in range(len(fitted)):
   y += fitted[i]*trialX**i

# Fit an exponential
popt, pcov = curve_fit(func, xData, yData,
    diag=(1./xData.mean(),1./yData.mean()) )
yEXP = func(trialX, *popt)

pyplot.figure()
pyplot.plot(xData, yData, label='Data', marker='o')
pyplot.plot(trialX, yEXP, 'r-',ls='--', label="Exp Fit")
pyplot.plot(trialX,   y, label = '10 Deg Poly')
pyplot.legend()
pyplot.show()
person Johann    schedule 21.06.2013
comment
Очень хорошее дополнение к ответу! Априорные знания практически всегда могут быть доступны при интерактивном анализе, но не всегда в случае автоматизированных настроек. - person PhilMacKay; 22.07.2013

модель a*exp(-b*x)+c хорошо соответствует данным, но я предлагаю небольшую модификацию:
вместо этого используйте это

a*x*exp(-b*x)+c

удачи

person benlala    schedule 24.10.2016