lmfit и scipy curve_fit возвращают начальные предположения как наиболее подходящие параметры

Я хочу приспособить функцию к некоторым данным и столкнулся с проблемой. Я пробовал использовать lmfit или curve_fit от scipy. Ниже описываю проблему.

Вот мои данные:

dataOT = pd.read_csv("KIC3239945e.csv", sep=';') 
t=dataOT['time']
y=dataOT['flux']

Кроме того, вот модель-функция, которая будет соответствовать данным:

def model(t, Rp, Rs, a, orb_inclination, Rin, Rout, tau):  
    gps=Rp/Rs
    gis=Rin/Rs
    gos=Rout/Rs
    Agps=A(t, gps, Rp, Rs, a, orb_inclination, Rin, Rout)
    Agos=A(t, gos, Rp, Rs, a, orb_inclination, Rin, Rout)
    Agis=A(t, gis, Rp, Rs, a, orb_inclination, Rin, Rout)
    return (np.pi*(1-u1/3-u2/6)-Agps-(1-np.exp(-tau))*(Agos-Agis))/(np.pi*(1-u1/3-u2/6))

где u1, u2 - известные числа, а параметры, которые необходимо подобрать, следующие: Rp, Rs, a, orb_inclination, Rin, Rout, tau, и они содержатся в количествах Agps, Agos, Agis. Вот определение функции A:

def A(t, gamma, Rp, Rs, a, orb_inclination, Rin, Rout):  
Xp,Zp= planetary_position(t, a, orb_inclination)
return np.where(rho(Xp,Zp,Rs)<1-gamma,
                np.pi*gamma**2*(1-u1-u2*(2-rho(Xp,Zp,Rs)**2-gamma**2/2)+(u1+2*u2)*W11(Xp,Zp,gamma,Rs) ) , 
                np.where(np.logical_and(  (1-gamma<=rho(Xp,Zp,Rs)),  (rho(Xp,Zp,Rs)<=1+gamma)  ), 
                (1-u1-3*u2/2)*(gamma**2*np.arccos(zeta(Xp,Zp,gamma,Rs)/gamma)+np.arcsin(zo(Xp,Zp,gamma,Rs))-rho(Xp,Zp,Rs)*zo(Xp,Zp,gamma,Rs))+(u2/2)*rho(Xp,Zp,Rs)*((rho(Xp,Zp,Rs)+2*zeta(Xp,Zp,gamma,Rs))*gamma**2*np.arccos(zeta(Xp,Zp,gamma,Rs)/gamma)-zo(Xp,Zp,gamma,Rs)*(rho(Xp,Zp,Rs)*zeta(Xp,Zp,gamma,Rs)+2*gamma**2))  +(u1+2*u2)*W3(Xp,Zp,gamma,Rs)    , 0))       

1-я попытка: curve_fit

from scipy.optimize import curve_fit
p0=[4.5*10**9, 4.3*10**10, 1.4*10**13, 1.2, 4.5*10**9, 13.5*10**9, 1]
popt, pcov = curve_fit(model, t, y, p0, bounds=((0, 0, 0, 0, 0, 0 ,0 ),(np.inf, np.inf, np.inf,np.inf, np.inf, np.inf ,np.inf )), maxfev=6000)
print(popt)

2-я попытка: lmfit

   from lmfit import Parameters, Minimizer, report_fit, Model
gmodel=Model(model)

def residual(p,t, y):
    Rp=p['Rp']
    Rs=p['Rs']
    a=p['a']
    orb_inclination=p['orb_inclination']
    Rin=p['Rin']
    Rout=p['Rout']
    tau=p['tau']
    tmp = model(t, Rp, Rs, a, orb_inclination, Rin, Rout, tau)-y
    return tmp

p = Parameters()

p.add('Rp' ,  value=0.000394786,     min= 0,max=1)
p.add('Rs' ,  value=0.003221125,    min= 0,max=1)
p.add('a',   value=1.86,            min= 0,max= 1)
p.add('orb_inclination',  value=1,   min= 0,max=4)
p.add('Rin',  value=0.000394786,    min= 0,max=1)
p.add('Rout',  value=0.000394786,    min= 0,max=1)
p.add('tau',  value=0,                 min= 0,max=2)

mini = Minimizer(residual,params=p,fcn_args=(t,y))

out = mini.minimize(method='leastsq')

print(report_fit(out))

Все случаи возвращают как наиболее подходящие параметры первоначальные предположения. Что мне делать, чтобы он работал нормально?

Примечание. Предполагая, что параметры известны, модель имеет ожидаемое поведение (Рисунок 1), поэтому я полагаю, что модель четко определена и проблема не связана с этим.

Любая помощь будет оценена по достоинству. Заранее спасибо!


person pap    schedule 11.05.2020    source источник


Ответы (3)


У меня есть две идеи, и я думаю, что первая, вероятно, ваша виновница.

  1. Мне кажется, что использование nan_policy = 'omit' работает в очень конкретных случаях. Если вы получите сообщение об ошибке "nan_policy = 'omit', вероятно, не будет работать" при попытке подгонки, что ж, вероятно, это не сработает. Вы можете выполнить простую проверку значений NaN, чтобы убедиться, что функция выводит значения NaN для вашего интервала.
  2. Границы переменных огромны. Попробуйте поднять минимум интервалов.
person sbjartmar    schedule 11.05.2020
comment
Я изменил границы переменных. Например, я написал: p.add('Rp' , value=4.5*10**9, min= 3*10**9,max=8*10**9), но проблема все еще существует. Кроме того, ни функция невязки, ни функция модели не содержат значений nan. Что еще могло быть причиной неправильных результатов подгонки? - person pap; 11.05.2020

Без реальных данных и полного примера очень сложно догадаться, что может пойти не так. Итак, это будет включать несколько советов о том, как подойти к проблеме.

Во-первых: поскольку вы выполняете подгонку кривой и имеете функцию модели, я рекомендую начать со второй версии, используя lmfit.Model. Но я бы предложил явно создать набор параметров, например:

from lmfit import Model
def A(t, gamma, Rp, Rs, a, orb_inclination, Rin, Rout):  
    Alpha = np.zeros(len(t))
    Xp, Zp = planetary_position(t, a, orb_inclination)
    values_rho = rho(Xp, Zp, Rs)
    v_W11 = W11(Xp,Zp, gamma, Rs)
    v_W11 = pd.Series(v_W11)
    v_zeta = zeta(Xp,Zp, gamma,Rs)
    v_zo = zo(Xp, Zp, gamma,Rs)
    v_W3 = W3(Xp,Zp, gamma,Rs)
    for i in range(len(values_rho)):
        Alpha[i]=np.where(values_rho[i]<1-gamma, np.pi*gamma**2*(1-u1-u2*(2-values_rho[i]**2-gamma**2/2)+(u1+2*u2)*v_W11[i] ) ,  np.where(((1-gamma<=values_rho[i]) and (values_rho[i]<=1+gamma)),  (1-u1-3*u2/2)*(gamma**2*np.arccos(v_zeta[i]/gamma)+np.arcsin(v_zo[i])-values_rho[i]*v_zo[i])+(u2/2)*values_rho[i]*((values_rho[i]+2*v_zeta[i])*gamma**2*np.arccos(v_zeta[i]/gamma) -v_zo[i]*(values_rho[i]*v_zeta[i]+2*gamma**2))+(u1+2*u2)*v_W3[i]    , 0)) 
    return Alpha

model = Model(model)
params = model.make_params(Rp=4.5*10**9, Rs=4.3*10**10, 
                           a=1.4*10**13, orb_inclination=1.2, 
                           Rin=4.5*10**9, Rout=13.5*10**9, tau=1)
result = gmodel.fit(y, params, t=t)
print(result.fit_report())

Само по себе это не решит проблему, но ясность имеет значение. Но вы можете вызвать эту функцию модели самостоятельно или сделать

  gmodel.eval(params, t=t)

и посмотрите, что он фактически вычисляет для любого набора значений параметров.

Во-вторых: вы должны быть осторожны с переменными в задаче подбора, которые охватывают много порядков. Пусть переменные будут больше похожи на порядок 1 (или, точнее, между порядком 1.e-6 и 1.e6), а затем умножьте их на множители 1e9 или 1e12 в зависимости от ситуации - или просто работайте в единицах со значениями, близкими к 1. Числа подгонки - все в формате с плавающей запятой двойной точности, и относительные значения параметров имеют значение.

Третье: функция вашей модели, ага. Подсчет читаемости. Написание непонятной функции никому не помогает. Включая тебя. Я гарантирую, что вы не знаете, что это значит. Например, вы могли бы избежать цикла и просто использовать ufunc-ness numpy, но это невозможно сказать. И чтобы было ясно, это невозможно сказать, потому что вы написали это так. Как, черт возьми, должны быть u1 и u2? На самом деле этой функции не было, и вы написали полный бардак, а потом что-то пошло не так.

Итак: напишите свою модельную функцию, как если бы вы ожидали ее прочитать в следующем году, а затем проверьте, что она вычисляет, с разумными входными значениями. Когда это сработает, подгонка тоже должна подойти.

person M Newville    schedule 12.05.2020
comment
Я изменил значения параметров и границы. Итак, 1-й случай, который я написал ранее, также включает: params=gmodel.make_params(Rp=0.000394786, Rs=0.003221125, a=1.86, orb_inclination=1.2, Rin=0.000394786, Rout=0.000394786, tau=1) model_values=gmodel.eval(params, t=t) Когда я запускаю это, появляется следующая ошибка: ValueError: The model function generated NaN values and the fit aborted! Please check your model function and/or set boundaries on parameters where applicable. In cases like this, using "nan_policy='omit'" will probably not work. - person pap; 12.05.2020
comment
Я также попытался выполнить третью попытку, которую я написал ранее, в том числе: gmodel=Model(model) model_values=gmodel.eval(p, t=t) Теперь model_values ​​представляет собой серию, полную 1.. Это странно, потому что когда я вызываю модельную функцию, предполагая, что параметры известны, например: relative_fluxNR=model(t=dataOT['time'], Rp=0.000394786, Rs=0.003221125, a=1.86, orb_inclination=89.997*np.pi/180, Rin=0.000394786, Rout=0.000394786, tau=0 ), функция работает правильно: ссылка - person pap; 12.05.2020
comment
Что ж, хорошая новость в том, что теперь вы знаете, где искать. Генерация NaN абсолютно убьет аппетит - вам нужно выяснить, откуда они берутся, и убедиться, что вы их не генерируете. Опять же, я настоятельно рекомендую написать вашу модельную функцию, чтобы ее можно было прочитать. Кроме того, если вызов gmodel.eval(params, t=t) отличается от gmodel(t, ....), ваши параметры не должны иметь этих значений. Опять же, хорошая новость в том, что вы знаете, где искать. - person M Newville; 12.05.2020
comment
Основываясь на вашей рекомендации, я переопределил функцию A, чтобы сделать код читаемым - я больше не использую цикл for. Я думаю, проблема в том, что переменная t не отображается явно в выражении модели. Это выражение является результатом комбинации многих функций, и кажется невозможным написать его более простым способом (я имею в виду, когда t появляется в последнем выражении). Может в этом проблема? Я также проверил значения модели, и нет значений Nan. Обратите внимание, что модельная функция имеет ожидаемое поведение в программе, за исключением части, связанной с lmfit. - person pap; 13.05.2020

Решил проблему уменьшением количества параметров. Еще одна проблема заключалась в том, что один из параметров вообще не влиял на подгонку.

person pap    schedule 19.05.2020