Skip to content

Commit 9d75e90

Browse files
author
Chris Fonnesbeck
committed
Merge pull request #214 from pymc-devs/metropolis
Add self-tuning to metropolis step method.
2 parents 328522c + c8a9c4f commit 9d75e90

20 files changed

+272
-172
lines changed

examples/lasso_block_update.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
# <markdowncell>
55

66
# Sometimes, it is very useful to update a set of parameters together. For example, variables that are highly correlated are often good to update together. In PyMC 3 block updating is simple, as example will demonstrate.
7-
#
7+
#
88
# Here we have a LASSO regression model where the two coefficients are strongly correlated. Normally, we would define the coefficient parameters as a single random variable, but here we define them separately to show how to do block updates.
9-
#
9+
#
1010
# First we generate some fake data.
1111

1212
# <codecell>
1313
from matplotlib.pylab import *
14-
from pymc import *
15-
import numpy as np
14+
from pymc import *
15+
import numpy as np
1616

1717
d = np.random.normal(size=(3, 30))
1818
d1 = d[0] + 4
@@ -29,24 +29,24 @@
2929
s = Exponential('s', 1)
3030
m1 = Laplace('m1', 0, 100)
3131
m2 = Laplace('m2', 0, 100)
32-
32+
3333
p = d1*m1 + d2*m2
34-
35-
y = Normal('y', p, s**-2, observed = yd)
34+
35+
y = Normal('y', p, s**-2, observed = yd)
3636

3737
# <markdowncell>
3838

39-
# For most samplers, including Metropolis and HamiltonianMC, simply pass a list of variables to sample as a block. This works with both scalar and array parameters.
39+
# For most samplers, including Metropolis and HamiltonianMC, simply pass a list of variables to sample as a block. This works with both scalar and array parameters.
4040

4141
# <codecell>
4242

43-
with model:
43+
with model:
4444
start = find_MAP()
45-
46-
step1 = Metropolis([m1, m2], approx_hess(start, [m1,m2]))
47-
48-
step2 = Metropolis([s], approx_hess(start, [s]))
49-
45+
46+
step1 = Metropolis([m1, m2])
47+
48+
step2 = Metropolis([s], laplace_proposal)
49+
5050
trace = sample(5000, [step1, step2], start)
5151

5252
# <codecell>

pymc/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
from core import *
1+
from core import *
22
from distributions import *
3-
from math import *
3+
from math import *
44

55
from trace import *
6-
from sample import *
6+
from sample import *
77
from step_methods import *
88
from tuning import *
99

10-
from debug import *
10+
from debug import *
1111

1212
from plots import *
13+
14+
from .tests import test

pymc/model.py

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
1-
'''
2-
Created on Mar 7, 2011
3-
4-
@author: johnsalvatier
5-
'''
61
from point import *
72
from types import *
83

94
from theano import theano, tensor as t, function
105
from theano.gof.graph import inputs
116

12-
import numpy as np
7+
import numpy as np
138
from functools import wraps
149

15-
__all__ = ['Model', 'compilef', 'gradient', 'hessian', 'modelcontext', 'Point']
10+
__all__ = ['Model', 'compilef', 'gradient', 'hessian', 'modelcontext', 'Point']
1611

1712

1813

19-
class Context(object):
20-
def __enter__(self):
14+
class Context(object):
15+
def __enter__(self):
2116
type(self).get_contexts().append(self)
2217
return self
2318

@@ -28,7 +23,7 @@ def __exit__(self, typ, value, traceback):
2823
def get_contexts(cls):
2924
if not hasattr(cls, "contexts"):
3025
cls.contexts = []
31-
26+
3227
return cls.contexts
3328

3429
@classmethod
@@ -38,45 +33,45 @@ def get_context(cls):
3833
except IndexError:
3934
raise TypeError("No context on context stack")
4035

41-
def modelcontext(model):
36+
def modelcontext(model):
4237
if model is None:
43-
return Model.get_context()
38+
return Model.get_context()
4439
return model
4540

4641
class Model(Context):
4742
"""
48-
Base class for encapsulation of the variables and
43+
Base class for encapsulation of the variables and
4944
likelihood factors of a model.
5045
"""
5146

5247
def __init__(self):
5348
self.vars = []
5449
self.factors = []
55-
50+
5651
@property
5752
def logp(model):
5853
"""
5954
log-probability of the model
60-
55+
6156
Parameters
6257
----------
63-
64-
model : Model
58+
59+
model : Model
6560
6661
Returns
6762
-------
6863
6964
logp : Theano scalar
70-
65+
7166
"""
7267
return t.add(*map(t.sum, model.factors))
7368

7469
@property
75-
def logpc(model):
70+
def logpc(model):
7671
"""Compiled log probability density function"""
7772
return compilef(model.logp)
7873

79-
def dlogpc(model, vars = None):
74+
def dlogpc(model, vars = None):
8075
"""Compiled log probability density gradient function"""
8176
return compilef(gradient(model.logp, vars))
8277

@@ -92,7 +87,7 @@ def test_point(self):
9287
@property
9388
def cont_vars(model):
9489
"""All the continuous variables in the model"""
95-
return typefilter(model.vars, continuous_types)
90+
return typefilter(model.vars, continuous_types)
9691

9792
"""
9893
these functions add random variables
@@ -108,8 +103,8 @@ def Var(model, name, dist):
108103
model.factors.append(dist.logp(var))
109104
return var
110105

111-
def TransformedVar(model, name, dist, trans):
112-
tvar = model.Var(trans.name + '_' + name, trans.apply(dist))
106+
def TransformedVar(model, name, dist, trans):
107+
tvar = model.Var(trans.name + '_' + name, trans.apply(dist))
113108

114109
return named(trans.backward(tvar),name), tvar
115110

@@ -118,26 +113,26 @@ def AddPotential(model, potential):
118113

119114

120115
def Point(*args,**kwargs):
121-
"""
122-
Build a point. Uses same args as dict() does.
123-
Filters out variables not in the model. All keys are strings.
116+
"""
117+
Build a point. Uses same args as dict() does.
118+
Filters out variables not in the model. All keys are strings.
124119
125120
Parameters
126121
----------
127-
*args, **kwargs
122+
*args, **kwargs
128123
arguments to build a dict
129124
"""
130125
if 'model' in kwargs :
131126
model = kwargs['model']
132127
del kwargs['model']
133-
else:
128+
else:
134129
model = Model.get_context()
135130

136131
d = dict(*args, **kwargs)
137132
varnames = map(str, model.vars)
138-
return dict((str(k),np.array(v))
139-
for (k,v) in d.iteritems()
140-
if str(k) in varnames)
133+
return dict((str(k),np.array(v))
134+
for (k,v) in d.iteritems()
135+
if str(k) in varnames)
141136

142137

143138
def compilef(outs, mode = None):
@@ -148,42 +143,42 @@ def compilef(outs, mode = None):
148143
----------
149144
outs : Theano variable or iterable of Theano variables
150145
mode : Theano compilation mode
151-
146+
152147
Returns
153148
-------
154149
Compiled Theano function
155150
"""
156151
return PointFunc(
157-
function(inputvars(outs), outs,
158-
allow_input_downcast = True,
152+
function(inputvars(outs), outs,
153+
allow_input_downcast = True,
159154
on_unused_input = 'ignore',
160155
mode = mode)
161156
)
162157

163158
def named(var, name):
164-
var.name = name
159+
var.name = name
165160
return var
166161

167162
def as_iterargs(data):
168-
if isinstance(data, tuple):
163+
if isinstance(data, tuple):
169164
return data
170165
if hasattr(data, 'columns'): #data frames
171-
return [np.asarray(data[c]) for c in data.columns]
166+
return [np.asarray(data[c]) for c in data.columns]
172167
else:
173168
return [data]
174169

175-
def makeiter(a):
170+
def makeiter(a):
176171
if isinstance(a, (tuple, list)):
177172
return a
178173
else :
179174
return [a]
180175

181-
def inputvars(a):
176+
def inputvars(a):
182177
return [v for v in inputs(makeiter(a)) if isinstance(v, t.TensorVariable)]
183178

184179
"""
185-
Theano derivative functions
186-
"""
180+
Theano derivative functions
181+
"""
187182

188183
def cont_inputs(f):
189184
return typefilter(inputvars(f), continuous_types)
@@ -193,7 +188,7 @@ def gradient1(f, v):
193188
return t.flatten(t.grad(f, v, disconnected_inputs='warn'))
194189

195190
def gradient(f, vars = None):
196-
if not vars:
191+
if not vars:
197192
vars = cont_inputs(f)
198193

199194
return t.concatenate([gradient1(f, v) for v in vars], axis = 0)
@@ -202,14 +197,14 @@ def jacobian1(f, v):
202197
"""jacobian of f wrt v"""
203198
f = t.flatten(f)
204199
idx = t.arange(f.shape[0])
205-
206-
def grad_i(i):
200+
201+
def grad_i(i):
207202
return gradient1(f[i], v)
208203

209204
return theano.map(grad_i, idx)[0]
210205

211206
def jacobian(f, vars = None):
212-
if not vars:
207+
if not vars:
213208
vars = cont_inputs(f)
214209

215210
return t.concatenate([jacobian1(f, v) for v in vars], axis = 1)
@@ -218,6 +213,6 @@ def hessian(f, vars = None):
218213
return -jacobian(gradient(f, vars), vars)
219214

220215

221-
#theano stuff
216+
#theano stuff
222217
theano.config.warn.sum_div_dimshuffle_bug = False
223218
theano.config.compute_test_value = 'raise'

pymc/sample.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,41 @@
88

99
__all__ = ['sample', 'psample']
1010

11-
def sample(draws, step, start = None, trace = None, track_progress = True, model = None):
11+
def sample(draws, step, start=None, trace=None, track_progress=True, model=None):
1212
"""
13-
Draw a number of samples using the given step method.
14-
Multiple step methods supported via compound step method
13+
Draw a number of samples using the given step method.
14+
Multiple step methods supported via compound step method
1515
returns the amount of time taken.
16-
16+
1717
Parameters
1818
----------
19-
19+
2020
model : Model (optional if in `with` context)
21-
draws : int
21+
draws : int
2222
The number of samples to draw
2323
step : function
2424
A step function
25-
start : dict
25+
start : dict
2626
Starting point in parameter space (Defaults to trace.point(-1))
2727
trace : NpTrace
2828
A trace of past values (defaults to None)
2929
track : list of vars
3030
The variables to follow
31-
31+
3232
Examples
3333
--------
34-
34+
3535
>>> an example
36-
36+
3737
"""
3838
model = modelcontext(model)
3939
draws = int(draws)
40-
if start is None:
40+
if start is None:
4141
start = trace[-1]
4242
point = Point(start, model = model)
4343

4444
if not hasattr(trace, 'record'):
45-
if trace is None:
45+
if trace is None:
4646
trace = model.vars
4747
trace = NpTrace(list(trace))
4848

@@ -65,8 +65,8 @@ def sample(draws, step, start = None, trace = None, track_progress = True, model
6565
def argsample(args):
6666
""" defined at top level so it can be pickled"""
6767
return sample(*args)
68-
69-
def psample(draws, step, start, mtrace = None, track = None, model = None, threads = None):
68+
69+
def psample(draws, step, start, mtrace=None, track=None, model=None, threads=None):
7070
"""draw a number of samples using the given step method. Multiple step methods supported via compound step method
7171
returns the amount of time taken"""
7272

@@ -78,7 +78,7 @@ def psample(draws, step, start, mtrace = None, track = None, model = None, threa
7878
if isinstance(start, dict) :
7979
start = threads * [start]
8080

81-
if track is None:
81+
if track is None:
8282
track = model.vars
8383

8484
if not mtrace:
@@ -87,7 +87,7 @@ def psample(draws, step, start, mtrace = None, track = None, model = None, threa
8787
p = mp.Pool(threads)
8888

8989
argset = zip([draws]*threads, [step]*threads, start, mtrace.traces, [False]*threads, [model] *threads)
90-
90+
9191
traces = p.map(argsample, argset)
92-
92+
9393
return MultiTrace(traces)

0 commit comments

Comments
 (0)