Функция объекта может возвращать None с определенными параметрами, как пропустить или избежать при конвергенции в Curvefit? - PullRequest
0 голосов
/ 28 мая 2018

В моей ситуации целевая функция представляет собой числовой процесс, содержащий процесс поиска корня для уравнения методом деления пополам.При определенном наборе параметров уравнение не имеет корня для промежуточной переменной.Я думал, что выполнение процедуры поиска корня бисексуального возврата None может решить такую ​​проблемуПоскольку объектная функция с набором дат, регрессируемых на scipy.optimize.curve_fit с p0, разделенными этой промежуточной ситуацией, ошибка затем останавливает процесс.

Для изучения этого случая показан упрощенный случай.

import numpy as np

#Define object function:
def f(x,a1,a2):
    if a1 < 0:
        return None
    elif a2 < 0:
        return np.inf
    else:
        return a1 * x**2 + a2

#Making data:
x = np.linspace(-5,5,10)
i = 0
y = np.empty_like(x)
for xi in x:
    y[i] = f(xi,1,1)
    i += 1

import scipy.optimize as sp

para,pvoc = sp.curve_fit(f,x,y,p0=(-1,1))
#TypeError: unsupported operand type(s) for -: 'NoneType' and 'float'

para,pvoc = sp.curve_fit(f,x,y,p0=(1,-1))
#RuntimeError: Optimal parameters not found: Number of calls to function has reached maxfev = 600.

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

Дополнительное мышление: я пытался try...except... игнорировать ошибку, а также имитировать случай, когда p0находится в разрешаемом диапазоне, но передаст неразрешимый сегмент в истинное соответствие.

import numpy as np

def f(x,a1,a2):
    if a1 < 0:
        return None
    elif a1 < 2:
        return a1 * x**2 + a2
    elif a2 < 0:
        return np.inf
    else:
        return a1 * x**2 + a2

def ff(x,a1,a2):
    output = f(x,a1,a2)
    if output == None:
        return 0
    else:
        return output

x = np.linspace(-5,5,10)
i = 0
y = np.empty_like(x)
for xi in x:
    y[i] = f(xi,1,1)
    i += 1


import scipy.optimize as sp

#para,pvoc = sp.curve_fit(f,x,y,p0=(-1,1))
#TypeError: unsupported operand type(s) for -: 'NoneType' and 'float':
#para,pvoc = sp.curve_fit(f,x,y,p0=(1,-1))

try:
    para,pvoc = sp.curve_fit(f,x,y,p0=(-3,1))
except TypeError:
    pass

Очевидно, что при сближении произошла ошибка, о которой было сообщено и что она была исключена.Что я должен сделать, чтобы продолжить curve_fit с исходным направлением схождения?Даже если я могу пойти на уступку, как я могу сказать curve_fit вернуть последнюю попытку a1?

С другой стороны, я попытался поместить это try... except... в объектную функцию, чтобы вернуть 0когда есть ошибка.Результат, как я и ожидал:

para,pvoc = sp.curve_fit(ff,x,y,p0=(-3,1))

#OptimizeWarning: Covariance of the parameters could not be estimated
  category=OptimizeWarning)

1 Ответ

0 голосов
/ 28 мая 2018

Я думаю, вы хотите использовать другой подход.То есть вы написали свою целевую функцию, чтобы она возвращала None или Inf, чтобы сигнализировать, когда значение для a1 или a2 выходит за пределы: a1<0 и a2<0 не являются приемлемыми значениями для целиfunction.

Если это правильная интерпретация того, что вы пытаетесь сделать, было бы лучше установить границы как для a1, так и a2, чтобы целевая функция никогда не получала эти значения вообще.Чтобы сделать это с curve_fit, вам нужно создать кортеж массивов для нижних и верхних границ, с порядком, соответствующим вашему p0, поэтому

pout, pcov = sp.curve_fit(f, x, y, p0=(1, 1), bounds=([0, 0], [np.inpf, np.inf])

В качестве отступления: я понятия не имею, почемувы используете начальное значение для a1, которое <0, и так выходит за пределы.Кажется, ты напрашиваешься на неприятности.</p>

Для еще лучшего опыта установки границ для подгоночных параметров вы можете рассмотреть возможность использования lmfit, который позволит вам написать:

import numpy as np
from lmfit import Model

def f(x, a1, a2):
    return a1 * x**2 + a2

fmod = Model(f)

params = fmod.make_params(a1=1, a2=0.5)
params['a1'].min = 0
params['a2'].min = 0

x = np.linspace(-5, 5, 10)

np.random.seed(0)
y = f(x, 1, 1) + np.random.normal(size=len(x), scale=0.02)

result = fmod.fit(y, params, x=x)
print(result.fit_report())

, который будет печатать

[[Model]]
    Model(f)
[[Fit Statistics]]
    # fitting method   = leastsq
    # function evals   = 13
    # data points      = 10
    # variables        = 2
    chi-square         = 0.00374066
    reduced chi-square = 4.6758e-04
    Akaike info crit   = -74.9107853
    Bayesian info crit = -74.3056151
[[Variables]]
    a1:  0.99998038 +/- 7.6225e-04 (0.08%) (init = 1)
    a2:  1.01496025 +/- 0.01034565 (1.02%) (init = 0.5)
[[Correlations]] (unreported correlations are < 0.100)
    C(a1, a2) = -0.750

Надеюсь, это поможет.

...