I failed to fit a method belonging to an instance of a class, as a Deterministic function, with PyMc3. Can you show me how to do that ?
For simplicity, my case is summarised below with a simple example. In reality, my constraint is that everything is made through a GUI and actions like ‘find_MAP’ should be inside methods linked to pyqt buttons.
I want to fit the function ‘FunctionIWantToFit’ over the data points. Problem, the following code:
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()
gives the following error:
Traceback (most recent call last): File "", line 1, in 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 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'
What’s wrong ?
remark 1: I systematically get an error message concerning the last parameter of ‘FunctionIWantToFit’. here it’s ‘z’ but if I remove z from the signature, the error message concerns ‘y’ (identical except from the name of the variable). if I add a 4th variable ‘w’ in the signature, the error message concerns ‘w’ (identical except from the name of the variable).
rk2: it looks like I missed something very basic in ‘theano’ or ‘pymc3’, because when I put ‘FunctionIWantToFit’ outside the class, it works. See the following example.
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()
gives:
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)}
Except that I don’t know how to do that without big modifications in several modules, since the real ‘FunctionIWantToFit’ depends on a bunch of variables and methods that belong to this instance of the class profile.
In fact I 'm not even sure I know how to do that since ‘FunctionIWantToFit’ should then have objects in arguments (that I currently use via self
) and I'm not sure how to do that with the theano decorator.
So I would prefer to avoid this solution... unless necessary. then I need explanations on how to implement it...
added on april 9, 2017:
Even without the interpolation question, it doesn't work because I must have missed something obvious with theano and/or pymc3. Please can you explain the problem ? I just want to compare model and data. First, it's such a shame being stuck to pymc2. ; second, I'm sure I'm not the only one with such a basic problem.
For example, let's consider variations around this very basic code:
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)
this code fails at the last line with the error message: TypeError: unsupported operand type(s) for -: 'TensorConstant' and 'Function'
if I change 'testfunc' into:
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
the code fails at the 'MyModel =' line with 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?')
if I go back to the original 'testfunc' but change the last 'with model' lines with:
with model: fval = test.testfunc(t,y,z) obs = pymc3.Normal('obs',mu=fval,sd=0.1,observed=test.observations)
the error is the same as the first one.
I presented here only 3 tries but I would like to underline that I tried many many combinations, simpler and simpler until these ones, during hours. I have the feeling pymc3 shows a huge change of spirit, compared to pymc2, that I didn't get and is poorly documented...