2017-02-13 12 views
7

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

Для простоты мой случай кратко изложен ниже с помощью простого примера. На самом деле мое ограничение состоит в том, что все сделано с помощью графического интерфейса, и действия, подобные «find_MAP», должны быть внутри методов, связанных с кнопками pyqt.

Я хочу установить функцию «FunctionIWantToFit» над точками данных. Проблема, следующий код:

import numpy as np 
import pymc3 as pm3 
from scipy.interpolate import interp1d 
import theano.tensor as tt 
import theano.compile 

class cprofile: 
    def __init__(self): 
     self.observed_x = np.array([0.3,1.4,3.1,5,6.8,9,13.4,17.1]) 
     self.observations = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1]) 
     self.x = np.arange(0,18,0.5) 

    @theano.compile.ops.as_op(itypes=[tt.dscalar,tt.dscalar,tt.dscalar], 
           otypes=[tt.dvector]) 
    def FunctionIWantToFit(self,t,y,z): 
     # can be complicated but simple in this example 
     # among other things, this FunctionIWantToFit depends on a bunch of 
     # variables and methods that belong to this instance of the class cprofile, 
     # so it cannot simply be put outside the class ! (like in the following example) 
     val=t+y*self.x+z*self.x**2 
     interp_values = interp1d(self.x,val) 
     return interp_values(self.observed_x) 

    def doMAP(self): 
     model = pm3.Model() 
     with model: 
      t = pm3.Uniform("t",0,5) 
      y = pm3.Uniform("y",0,5) 
      z = pm3.Uniform("z",0,5) 
      MyModel = pm3.Deterministic('MyModel',self.FunctionIWantToFit(t,y,z)) 
      obs = pm3.Normal('obs',mu=MyModel,sd=0.1,observed=self.observations) 
      start = pm3.find_MAP() 
      print('start: ',start) 

test=cprofile() 
test.doMAP() 

дает следующее сообщение об ошибке:

Traceback (most recent call last): 

    File "<ipython-input-15-3dfb7aa09f84>", line 1, in <module> 
    runfile('/Users/steph/work/profiles/GUI/pymc3/so.py', wdir='/Users/steph/work/profiles/GUI/pymc3') 

    File "/Users/steph/anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 866, in runfile 
    execfile(filename, namespace) 

    File "/Users/steph/anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 102, in execfile 
    exec(compile(f.read(), filename, 'exec'), namespace) 

    File "/Users/steph/work/profiles/GUI/pymc3/so.py", line 44, in <module> 
    test.doMAP() 

    File "/Users/steph/work/profiles/GUI/pymc3/so.py", line 38, in doMAP 
    MyModel = pm3.Deterministic('MyModel',self.FunctionIWantToFit(x,y,z)) 

    File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gof/op.py", line 668, in __call__ 
    required = thunk() 

    File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gof/op.py", line 912, in rval 
    r = p(n, [x[0] for x in i], o) 

    File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/compile/ops.py", line 522, in perform 
    outs = self.__fn(*inputs) 

TypeError: FunctionIWantToFit() missing 1 required positional argument: 'z' 

Что случилось?

примечание 1: Я систематически получаю сообщение об ошибке, касающееся последнего параметра «FunctionIWantToFit». здесь это 'z', но если я удалю z из подписи, сообщение об ошибке касается «y» (идентично, кроме имени переменной). если я добавлю 4-ю переменную 'w' в подпись, сообщение об ошибке относится к 'w' (идентично, кроме имени переменной).

rk2: похоже, я пропустил что-то очень основное в 'anano' или 'pymc3', потому что, когда я кладу 'FunctionIWantToFit' вне класса, он работает. См. Следующий пример.

class cprofile: 
    def __init__(self): 
     self.observations = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1]) 

    def doMAP(self): 
     model = pm3.Model() 
     with model: 
      t = pm3.Uniform("t",0,5) 
      y = pm3.Uniform("y",0,5) 
      z = pm3.Uniform("z",0,5) 
      MyModel = pm3.Deterministic('MyModel',FunctionIWantToFit(t,y,z)) 
      obs = pm3.Normal('obs',mu=MyModel,sd=0.1,observed=self.observations) 
      start = pm3.find_MAP() 
      print('start: ',start) 

@theano.compile.ops.as_op(itypes=[tt.dscalar,tt.dscalar,tt.dscalar], 
           otypes=[tt.dvector]) 
def FunctionIWantToFit(t,y,z): 
     observed_x = np.array([0.3,1.4,3.1,5,6.8,9,13.4,17.1]) 
     x = np.arange(0,18,0.5) 
     val=t+y*x+z*x**2 
     interp_values = interp1d(x,val) 
     return interp_values(observed_x) 

test=cprofile() 
test.doMAP() 

дает:

Warning: gradient not available.(E.g. vars contains discrete variables). MAP estimates may not be accurate for the default parameters. Defaulting to non-gradient minimization fmin_powell. 
WARNING:pymc3:Warning: gradient not available.(E.g. vars contains discrete variables). MAP estimates may not be accurate for the default parameters. Defaulting to non-gradient minimization fmin_powell. 
Optimization terminated successfully. 
     Current function value: 1070.673818 
     Iterations: 4 
     Function evaluations: 179 
start: {'t_interval_': array(-0.27924150484602733), 'y_interval_': array(-9.940000425802811), 'z_interval_': array(-12.524909223913992)} 

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

На самом деле я даже не уверен, что знаю, как это сделать, поскольку «FunctionIWantToFit» должен иметь объекты в аргументах (которые я использую в настоящее время через self), и я не уверен, как это сделать с помощью декоратора theano ,

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


добавил на 9 апреля 2017 года:

Даже без интерполяции вопрос, он не работает, потому что я должен пропустить что-то очевидное с Теано и/или pymc3. Пожалуйста, объясните проблему? Я просто хочу сравнить модель и данные. Во-первых, такой стыд застрял в pymc2. ; во-вторых, я уверен, что я не единственный, у кого такая основная проблема.

Например, давайте рассмотрим варианты вокруг этого очень простой код:

import numpy as np 
import theano 
import pymc3 
theano.config.compute_test_value = 'ignore' 
theano.config.on_unused_input = 'ignore' 

class testclass: 
    x = np.arange(0,18,0.5) 
    observed_x = np.array([0.3,1.4,3.1,5,6.8,9,13.4,17.1]) 
    observations = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1]) 

    def testfunc(self,t,y,z): 
     t2 = theano.tensor.dscalar('t2') 
     y2 = theano.tensor.dscalar('y2') 
     z2 = theano.tensor.dscalar('z2') 
     val = t2 + y2 * self.observed_x + z2 * self.observed_x**2 
     f = theano.function([t2,y2,z2],val) 
     return f 

test=testclass() 
model = pymc3.Model() 
with model: 
    t = pymc3.Uniform("t",0,5) 
    y = pymc3.Uniform("y",0,5) 
    z = pymc3.Uniform("z",0,5) 

with model: 
    MyModel = pymc3.Deterministic('MyModel',test.testfunc(t,y,z)) 

with model: 
    obs = pymc3.Normal('obs',mu=MyModel,sd=0.1,observed=test.observations) 

этот код не в последней строке с сообщением об ошибке: TypeError: unsupported operand type(s) for -: 'TensorConstant' and 'Function'

, если я изменю «testfunc» в:

def testfunc(self,t,y,z): 
    t2 = theano.tensor.dscalar('t2') 
    y2 = theano.tensor.dscalar('y2') 
    z2 = theano.tensor.dscalar('z2') 
    val = t2 + y2 * self.observed_x + z2 * self.observed_x**2 
    f = theano.function([t2,y2,z2],val) 
    fval = f(t,y,z,self.observed_x) 
    return fval 

код не в строке 'MyModel =' с ошибкой TypeError: ('Bad input argument to theano function with name "/Users/steph/work/profiles/GUI/pymc3/theanotest170409.py:32" at index 0(0-based)', 'Expected an array-like object, but found a Variable: maybe you are trying to call a function on a (possibly shared) variable instead of a numeric array?')

если я вернусь к оригинальному «testfunc», но изменить последний «с моделью» линии с:

with model: 
    fval = test.testfunc(t,y,z) 
    obs = pymc3.Normal('obs',mu=fval,sd=0.1,observed=test.observations) 

погрешность такой же, как и первый.

Я представил здесь только 3 попытки, но я хотел бы подчеркнуть, что я пробовал много разных комбинаций, проще и проще до тех пор, пока они не будут в течение нескольких часов. У меня есть чувство, что pymc3 показывает огромную смену духа по сравнению с pymc2, что я не получил и плохо документирован ...

ответ

0

я, наконец, сходился к успешной коде ниже:

import numpy as np 
import theano 
from scipy.interpolate import interp1d 
import pymc3 as pm3 
theano.config.compute_test_value = 'ignore' 
theano.config.on_unused_input = 'ignore' 

class cprofile: 
    observations = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1]) 
    x = np.arange(0,18,0.5) 
    observed_x = np.array([0.3,1.4,3.1,5,6.8,9,13.4,17.1])  

    def doMAP(self): 
     model = pm3.Model() 
     with model: 
      t = pm3.Uniform("t",0,5) 
      y = pm3.Uniform("y",0,5) 
      z = pm3.Uniform("z",0,5) 
      obs=pm3.Normal('obs', 
       mu=FunctionIWantToFit(self)(t,y,z), 
       sd=0.1,observed=self.observations) 
      start = pm3.find_MAP() 
      print('start: ',start) 

class FunctionIWantToFit(theano.gof.Op): 
    itypes=[theano.tensor.dscalar, 
      theano.tensor.dscalar, 
      theano.tensor.dscalar] 
    otypes=[theano.tensor.dvector] 

    def __init__(self, cp): 
     self.cp = cp # note cp is an instance of the 'cprofile' class 

    def perform(self,node, inputs, outputs): 
     t, y, z = inputs[0], inputs[1], inputs[2] 

     xxx = self.cp.x 
     temp = t+y*xxx+z*xxx**2 
     interpolated_concentration = interp1d(xxx,temp) 
     outputs[0][0] = interpolated_concentration(self.cp.observed_x) 

testcp=cprofile() 
testcp.doMAP() 

благодаря ответу Дарио, потому что я был слишком медленным, чтобы понять первый ответ сам. Я получаю это ретроспективно, но я сильно думаю, что документ pymc3 болезненно неясен. Он должен содержать очень простые и наглядные примеры.

Однако я не преуспел в том, чтобы делать что-либо, что работает после комментария Криса. Может ли кто-нибудь объяснить и/или привести пример?

Еще одна вещь: я не знаю, эффективен ли мой пример выше или может быть упрощен. В частности, это создает впечатление, что экземпляр testcp дважды копируется в память. Больше комментариев и ответов можно продолжить.

1

theano.compile.ops.as_op - это всего лишь короткая рука для определения простых Theano Ops. Если вы хотите кодировать более привлекательные, лучше определить его в отдельном классе. Объекты этого класса могли бы, конечно, ссылаться на экземпляр вашего cprofile, если это действительно необходимо.

http://deeplearning.net/software/theano/extending/extending_theano.html

+0

Благодарим вас за ответ. Я был в отъезде и попробую это сейчас. –

+0

извините, но мне остается неясным. Я пробовал много комбинаций самодельного anano op с pymc3, но ничего не работает, и документы сильно размыты ... Не могли бы вы привести пример кода? –

+0

Если вы пишете свою собственную функцию интерполяции с помощью Theano, вам не нужно использовать op. Интерполяция 1D должна быть простой. –

3

Хорошо, давайте делать это по частям. Сначала я объясню полученные сообщения об ошибках, а затем расскажу, как я буду продолжать.

По первому вопросу, прямая причина, по которой вы получаете жалобу на недостающие параметры, состоит в том, что ваша функция, определенная внутри класса, принимает в качестве входных данных (self, t, y, z), в то время как вы объявив его в op decorator как имеющее только три входа (t, y, z). Вам нужно будет объявить входные данные как четыре в вашем декораторе, чтобы учесть сам экземпляр класса.

В «добавленном в апреле 9, 2017:» первый код не будет работать, потому что результат test.testfunc (t, y, z) является самой функцией anano. pymc3.Deterministic ожидает, что он выведет переменные anano (или переменные python). Вместо этого сделайте непосредственно test.testfun вывод val = t2 + y2 * self.observed_x + z2 * self.observed_x ** 2.

Затем, если «меняет« testfunc »на:», вы получаете эту ошибку из-за способа, которым pymc3 пытается работать с функциями anano. Короче говоря, проблема в том, что, когда pymc3 использует эту функцию, он отправит ей переменные anano, а fval ожидает числовые переменные (массивы numpy или другие). Как и в предыдущем абзаце, вам просто нужно выводить значение val напрямую: нет необходимости компилировать любую функцию anano для этого.

Что касается продолжения, я бы попытался объявить экземпляр класса в качестве входного сигнала для декоратора theano. К сожалению, я не могу найти документацию о том, как это сделать, и это может быть фактически невозможно (см., Например, this old post).

Затем я попытался передать все функции, необходимые в качестве входных данных, и определить их вне класса. Это может быть довольно громоздким, и если в качестве ввода нужны методы, то возникают дополнительные проблемы.

Другой способ сделать это - создать дочерний класс theano.gof.Op, метод init принимает ваш класс (или скорее его экземпляр) в качестве входных данных, а затем определяет ваш метод perform(). Это будет выглядеть примерно так:

class myOp(theano.gof.Op): 
    """ These are the inputs/outputs you used in your as_op 
    decorator. 
    """ 
    itypes=[tt.dscalar,tt.dscalar,tt.dscalar] 
    otypes=[tt.dvector] 
    def __init__(self, myclass): 
     """ myclass would be the class you had from before, which 
     you called cprofile in your first block of code.""" 
     self.myclass = myclass 
    def perform(self,node, inputs, outputs): 
     """ Here you define your operations, but instead of 
     calling everyting from that class with self.methods(), you 
     just do self.myclass.methods(). 

     Here, 'inputs' is a list with the three inputs you declared 
     so you need to unpack them. 'outputs' is something similar, so 
     the function doesn't actually return anything, but saves all 
     to outputs. 'node' is magic juice that keeps the world 
     spinning around; you need not do anything with it, but always 
     include it. 
     """ 
     t, y, z = inputs[0][0], inputs[0][1], inputs[0][2] 
     outputs[0][0] = t+y*self.myclass.x+z*self.myclass.x**2 
myop = myOp(myclass) 

После того, как вы сделали это, вы можете использовать myop в качестве Op для остальной части кода. Обратите внимание, что некоторые части отсутствуют. Вы можете проверить my example для более подробной информации.

Что касается the example, вам не нужно определять метод grad(). Из-за этого вы можете выполнять все операции в функции perform() в чистом python, если это помогает.

В качестве альтернативы, и я говорю это с ухмылкой на моем лице, если у вас есть доступ к определению используемого класса, вы также можете сделать его наследованием от theano.gof.Op, создать функцию perform() метод (as in my other example, где вы оставили сообщение) и попытайтесь использовать его так. Это может создать конфликты с тем, что вы делаете с этим классом, и, вероятно, довольно сложно правильно, но может быть интересно попробовать.