Модульный (питонный) способ подбора комбинированных параметров составной функции - PullRequest
0 голосов
/ 18 декабря 2018

Мой вопрос касается подбора параметров сложной модели, состоящей из различных параметрических функций.

Точнее, я хочу описать сложный эксперимент.В результате эксперимента получается одномерный массив измеренных данных data, где каждая его запись соответствует (набору) экспериментальных контрольных переменных x.

Теперь я теоретическая модель (на самом деле несколько моделей, см.ниже) model(x,pars), который принимает x и множество параметров pars, чтобы дать прогноз для data.Однако не все параметры известны, и мне нужно их подогнать.

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

К сожалениюпереключение одного компонента на другой может привести к появлению новых (неизвестных) параметров, то есть теперь у нас есть modelA(x,parsA) и modelB(x,parsB), которые имеют разные параметры.

По сути, модель состоит из функций f(x, pars, vals_of_subfuncs), где x - независимая переменная, pars - некоторые явные параметры f, а vals_of_subfuncs - результаты оценки некоторых функций более низкого уровня, которые сами зависят от своих собственных параметров (и, возможно, результаты их собственных более низкихфункции высокого уровня и т. д.) Очевидно, что рекурсии невозможны, и на самом низком уровне функций, которые не зависят от значения других функций.

Ситуация лучше всего проиллюстрирована на следующем рисунке:

Модульная модель архитектуры

Независимый вариантзначение равно x (синий), параметры a,b,c,d (красный), а значения подфункций отображаются в виде зеленых стрелок в узлах, представляющих функции.

В (1) у нас самый низкий уровеньфункция G(x; (a,b); {}) без подфункций и функция более высокого уровня F(x; c; G(x; (a,b)), оценка которой дает результат модели, который зависит от x и pars=(a,b,c).

In (2) и (3)мы меняем компонент модели (F->F') и (G->G') соответственно.Это меняет зависимость параметров окончательной модели.

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

В данный момент я пытаюсь найти решение этой проблемы с помощью lmfit.Я также думал о том, что, возможно, пытаюсь использовать sympy для работы с символическими «параметрами», но я не думаю, что все появляющиеся функции могут быть легко написаны как выражения, которые могут быть оценены как asteval.

Кто-нибудь знает естественный способ подойти к такой ситуации?

Ответы [ 3 ]

0 голосов
/ 20 декабря 2018

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

def model_func(x, a, b, c, d):
     gresult = G(x, a, b, d)
     return F(x, b, c, gresult)

, но вы также хотите контролировать, действительно ли d и b являются переменными, и передается ли c в F.Это правильно?

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

Например, вы можете выполнить некоторую перестановку следующим образом:

def G(x, a, b=None, d=None):
    if b is not None and d is None:
       return calc_g_without_d(x, a, b)
    return calc_g_with_d(x, a, d) 

def F(x, gresult, b, c=None):
    if c is None:
       return calc_f_without_c(x, gresult, b)
    return calc_f_with_c(x, gresult, b, c) 

def model_func(x, a, b, c, d, g_with_d=True, f_with_c=True):
     if g_with_d:
        gresult = G(x, a, d)
     else:
        gresult = G(x, a, b)
     if f_with_c:
         return F(x, gresult, b, c=c)
     else:
         return F(x, gresult, b)

Теперь, когда вы создаете свою модель, вы можете переопределитьзначения по умолчанию f_with_c и / или g_with_d:

import lmfit
mymodel = lmfit.Model(model_func, f_with_c=False)
params = mymodel.make_params(a=100, b=0.2201, c=2.110, d=0)

, а затем оценка модели с помощью mymodel.eval() или выполнение подбора с помощью mymodel.fit() и передача явных значений для аргументов ключевого слова f_with_cи / или g_with_d, например

test = mymodel.eval(params, x=np.linspace(-1, 1, 41), 
                    f_with_c=False, g_with_d=False)

или

result = mymodel.fit(ydata, params, x=xdata, g_with_d=False)

Я думаю, как вы это указали, вы бы хотели убедиться, что d не былпеременная в соответствии, когда g_with_d=False, и есть случаи, когда вы бы хотели, чтобы b не изменялось в соответствии.Вы можете сделать это с

params['b'].vary = False
params['d'].vary = False

по мере необходимости.Я могу себе представить, что ваша настоящая проблема немного больше, чем эта, но я надеюсь, что это поможет вам начать в правильном направлении.

0 голосов
/ 09 января 2019

Спасибо за ответы.

Я думаю, lmfit мог бы делать то, что я хочу, но мне придется самому реализовать "модульность".Пример, который я имею, был просто концептуальной и мимической моделью.В общем, «сети» функций и их зависимости намного сложнее, чем то, что я делаю в примере.

Мой текущий план таков: я напишу класс Network для «сети», который содержит определенные Node с.Примечания определяют их возможную «символическую» зависимость от sub Node s, явных параметров и независимых переменных.

Класс Network будет иметь подпрограммы для проверки согласованности такой построенной сети.Более того, он будет иметь (lmfit) Parameters объект (то есть объединение всех параметров, от которых явно зависят узлы) и предоставит некоторый метод для генерации lmfit Model из этого.

Тогда я буду использовать lmfit для примерки.

По крайней мере, это план.Если мне удастся построить это, я опубликую обновление этого поста с моим кодом.

0 голосов
/ 19 декабря 2018

Так как вы подняли sympy, я думаю, вам стоит взглянуть на symfit, который точно соответствует тому, что вы просили в последнем абзаце.С помощью symfit вы можете написать символические выражения, которые затем будут снабжены scipy.Вам будет очень легко комбинировать различные подмодели.

Позвольте мне реализовать ваш второй пример, используя symfit:

from symfit import variables, parameters, Fit, Model

a, b, c = parameters('a, b, c')
x, G, F = variables('x, G, F')

model_dict = {
    G: a * x + b,
    F: b * G + c * x
}
model = Model(model_dict)
print(model.connectivity_mapping)

Я выбираю эти довольно тривиальные функции,но вы, очевидно, можете выбрать все, что хотите.Чтобы увидеть, что эта модель соответствует вашей иллюстрации, вот что печатает connectivity_mapping:

{F: {b, G, x, c}, G: {b, a, x}}

Итак, вы видите, что это действительно отображение, отображающее то, что вы нарисовали.(Аргументы не имеют определенного порядка в каждом наборе, но они будут оцениваться в правильном порядке, например, G до F.) Чтобы потом соответствовать вашим данным, просто выполните

fit = Fit(model, x=xdata, F=Fdata)
fit_results = fit.execute()

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

Отказ от ответственности: я автор symfit.

...