tutorial example pymc3

example - ¿Cómo ajustar un método que pertenece a una instancia con pymc3?



pymc3 example (3)

No pude ajustar un método que pertenece a una instancia de una clase, como una función determinista, con PyMc3. ¿Me puede mostrar cómo hacer eso?

Para simplificar, mi caso se resume a continuación con un ejemplo simple. En realidad, mi restricción es que todo se hace a través de una GUI y acciones como ''find_MAP'' deberían estar dentro de los métodos vinculados a los botones pyqt.

Quiero ajustar la función ''FunctionIWantToFit'' sobre los puntos de datos. Problema, el siguiente código:

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()

da el siguiente error:

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''

Que pasa

Observación 1: Recibo sistemáticamente un mensaje de error sobre el último parámetro de ''FunctionIWantToFit''. aquí es ''z'' pero si elimino z de la firma, el mensaje de error se refiere a ''y'' (idéntico excepto por el nombre de la variable). Si agrego una cuarta variable ''w'' en la firma, el mensaje de error se refiere a ''w'' (idéntico excepto por el nombre de la variable).

rk2: parece que me perdí algo muy básico en ''theano'' o ''pymc3'', porque cuando puse ''FunctionIWantToFit'' fuera de la clase, funciona. Ver el siguiente ejemplo.

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()

da:

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)}

Excepto que no sé cómo hacerlo sin grandes modificaciones en varios módulos, ya que el verdadero ''FunctionIWantToFit'' depende de un montón de variables y métodos que pertenecen a esta instancia del perfil de clase.

De hecho, ni siquiera estoy seguro de saber cómo hacerlo, ya que ''FunctionIWantToFit'' debería tener objetos en argumentos (que actualmente uso a través de self ) y no estoy seguro de cómo hacerlo con el decorador theano.

Así que preferiría evitar esta solución ... a menos que sea necesario. entonces necesito explicaciones sobre cómo implementarlo ...

agregado el 9 de abril de 2017:

Incluso sin la pregunta de interpolación, no funciona porque debo haber pasado por alto algo obvio con theano y / o pymc3. Por favor, ¿puedes explicar el problema? Solo quiero comparar modelo y datos. Primero, es una pena estar pegado a pymc2. ; segundo, estoy seguro de que no soy el único con un problema tan básico.

Por ejemplo, consideremos variaciones alrededor de este código muy básico:

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)

este código falla en la última línea con el mensaje de error: TypeError: unsupported operand type(s) for -: ''TensorConstant'' and ''Function''

si cambio ''testfunc'' en:

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

el código falla en la línea ''MyModel ='' con error 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?'')

si vuelvo al ''testfunc'' original pero cambio las últimas líneas ''con modelo'' con:

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

El error es el mismo que el primero.

Presente aquí solo 3 intentos, pero me gustaría subrayar que probé muchas combinaciones, más y más simples hasta estas, durante horas. Tengo la sensación de que pymc3 muestra un gran cambio de espíritu, en comparación con pymc2, que no obtuve y está mal documentado ...


Finalmente convergí hacia el código exitoso a continuación:

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()

gracias a la respuesta de Dario porque fui demasiado lento para entender la primera respuesta por mí mismo. Lo entiendo retrospectivamente, pero creo firmemente que el documento pymc3 no es muy claro. Debe contener ejemplos muy simples e ilustrativos.

Sin embargo, no logré hacer nada que funcione después del comentario de Chris. ¿Alguien podría explicar y / o dar un ejemplo?

Una cosa más: no sé si mi ejemplo anterior es eficiente o podría simplificarse. En particular, me da la impresión de que la instancia ''testcp'' se copia dos veces en la memoria. Más comentarios / respuestas son bienvenidos para ir más allá.


Ok, hagamos esto por partes. Primero explicaré los mensajes de error que recibió y luego le diré cómo procedería.

En la primera pregunta, la razón directa por la que recibe una queja sobre los parámetros faltantes es porque su función, definida dentro de la clase, toma como entrada (self, t, y, z), mientras la declara en el El decorador operativo tiene solo tres entradas (t, y, z). Tendría que declarar las entradas como cuatro en su decorador para dar cuenta de la instancia de clase en sí.

En "agregado el 9 de abril de 2017:", el primer código no funcionará porque la salida de test.testfunc (t, y, z) es una función theano en sí misma. pymc3.Deterministic espera que genere las variables theano (o variables de python). En su lugar, realice la salida test.testfun val = t2 + y2 * self.observed_x + z2 * self.observed_x ** 2 directamente.

Luego, en "si cambio ''testfunc'' a:", obtienes ese error debido a la forma en que pymc3 intenta trabajar con las funciones de theano. En pocas palabras, el problema es que cuando pymc3 hace uso de esta función, le enviará las variables de nano, mientras que fval espera variables numéricas (matrices numpy u otras). Como en el párrafo anterior, solo necesita generar val directamente: no es necesario compilar ninguna función theano para esto.

En cuanto a cómo procedería, trataría de declarar la instancia de clase como entrada al decorador de theano. Desafortunadamente, no puedo encontrar ninguna documentación sobre cómo hacer esto y en realidad podría ser imposible (vea esta publicación anterior , por ejemplo).

Luego, trataría de pasar todo lo que la función necesita como entradas y definirlo fuera de la clase. Esto podría ser bastante engorroso y si necesita métodos como entrada, entonces se encuentra con problemas adicionales.

Otra forma de hacerlo es crear una clase secundaria de theano.gof.Op cuyo método init tome su clase (o más bien una instancia de ella) como entrada y luego defina su método perform (). Esto se vería así:

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)

Una vez que haya hecho esto, puede usar myop como su Op para el resto de su código. Tenga en cuenta que faltan algunas partes. Puedes consultar mi ejemplo para más detalles.

En cuanto al ejemplo , no necesita definir el método grad (). Debido a esto, puede hacer todas las operaciones en perform () en Python puro, si eso ayuda.

Alternativamente, y digo esto con una sonrisa en mi rostro, si tienes acceso a la definición de la clase que estás usando, también puedes hacer que herede de theano.gof.Op, crea el método perform () ( como en mi otro ejemplo , donde dejaste un mensaje) e intenta usarlo así. Podría crear conflictos con cualquier otra cosa que esté haciendo con esa clase y probablemente sea bastante difícil hacerlo bien, pero puede ser divertido intentarlo.


theano.compile.ops.as_op es solo una abreviatura para definir simples Theano Ops. Si desea codificar los más involucrados, es mejor definirlo en una clase separada. Los objetos de esta clase podrían, por supuesto, tomar una referencia a una instancia de su perfil c si es realmente necesario.

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