16
16
import sys
17
17
sys .setrecursionlimit (10000 )
18
18
19
- __all__ = ['sample' , 'iter_sample' , 'sample_ppc' ]
19
+ __all__ = ['sample' , 'iter_sample' , 'sample_ppc' , 'init_nuts' ]
20
20
21
21
22
22
def assign_step_methods (model , step = None , methods = (NUTS , HamiltonianMC , Metropolis ,
@@ -81,8 +81,9 @@ def assign_step_methods(model, step=None, methods=(NUTS, HamiltonianMC, Metropol
81
81
return steps
82
82
83
83
84
- def sample (draws , step = None , start = None , trace = None , chain = 0 , njobs = 1 , tune = None ,
85
- progressbar = True , model = None , random_seed = - 1 ):
84
+ def sample (draws , step = None , init = 'advi' , n_init = 500000 , start = None ,
85
+ trace = None , chain = 0 , njobs = 1 , tune = None , progressbar = True ,
86
+ model = None , random_seed = - 1 ):
86
87
"""
87
88
Draw a number of samples using the given step method.
88
89
Multiple step methods supported via compound step method
@@ -97,6 +98,15 @@ def sample(draws, step=None, start=None, trace=None, chain=0, njobs=1, tune=None
97
98
A step function or collection of functions. If no step methods are
98
99
specified, or are partially specified, they will be assigned
99
100
automatically (defaults to None).
101
+ init : str {'advi', 'advi_map', 'map', 'nuts'}
102
+ Initialization method to use.
103
+ * advi : Run ADVI to estimate posterior mean and diagonal covariance matrix.
104
+ * advi_map: Initialize ADVI with MAP and use MAP as starting point.
105
+ * map : Use the MAP as starting point.
106
+ * nuts : Run NUTS and estimate posterior mean and covariance matrix.
107
+ n_init : int
108
+ Number of iterations of initializer
109
+ If 'advi', number of iterations, if 'nuts', number of draws.
100
110
start : dict
101
111
Starting point in parameter space (or partial point)
102
112
Defaults to trace.point(-1)) if there is a trace provided and
@@ -132,7 +142,14 @@ def sample(draws, step=None, start=None, trace=None, chain=0, njobs=1, tune=None
132
142
"""
133
143
model = modelcontext (model )
134
144
135
- step = assign_step_methods (model , step )
145
+ if step is None and init is not None and pm .model .all_continuous (model .vars ):
146
+ # By default, use NUTS sampler
147
+ pm ._log .info ('Auto-assigning NUTS sampler...' )
148
+ start_ , step = init_nuts (init = init , n_init = n_init , model = model )
149
+ if start is None :
150
+ start = start_
151
+ else :
152
+ step = assign_step_methods (model , step )
136
153
137
154
if njobs is None :
138
155
import multiprocessing as mp
@@ -373,3 +390,63 @@ def sample_ppc(trace, samples=None, model=None, vars=None, size=None, random_see
373
390
size = size ))
374
391
375
392
return {k : np .asarray (v ) for k , v in ppc .items ()}
393
+
394
+
395
+ def init_nuts (init = 'advi' , n_init = 500000 , model = None ):
396
+ """Initialize and sample from posterior of a continuous model.
397
+
398
+ This is a convenience function. NUTS convergence and sampling speed is extremely
399
+ dependent on the choice of mass/scaling matrix. In our experience, using ADVI
400
+ to estimate a diagonal covariance matrix and using this as the scaling matrix
401
+ produces robust results over a wide class of continuous models.
402
+
403
+ Parameters
404
+ ----------
405
+ init : str {'advi', 'advi_map', 'map', 'nuts'}
406
+ Initialization method to use.
407
+ * advi : Run ADVI to estimate posterior mean and diagonal covariance matrix.
408
+ * advi_map: Initialize ADVI with MAP and use MAP as starting point.
409
+ * map : Use the MAP as starting point.
410
+ * nuts : Run NUTS and estimate posterior mean and covariance matrix.
411
+ n_init : int
412
+ Number of iterations of initializer
413
+ If 'advi', number of iterations, if 'metropolis', number of draws.
414
+ model : Model (optional if in `with` context)
415
+
416
+ Returns
417
+ -------
418
+ start, nuts_sampler
419
+
420
+ start : pymc3.model.Point
421
+ Starting point for sampler
422
+ nuts_sampler : pymc3.step_methods.NUTS
423
+ Instantiated and initialized NUTS sampler object
424
+ """
425
+
426
+ model = pm .modelcontext (model )
427
+
428
+ pm ._log .info ('Initializing NUTS using {}...' .format (init ))
429
+
430
+ if init == 'advi' :
431
+ v_params = pm .variational .advi (n = n_init )
432
+ start = pm .variational .sample_vp (v_params , 1 )[0 ]
433
+ cov = np .power (model .dict_to_array (v_params .stds ), 2 )
434
+ elif init == 'advi_map' :
435
+ start = pm .find_MAP ()
436
+ v_params = pm .variational .advi (n = n_init , start = start )
437
+ cov = np .power (model .dict_to_array (v_params .stds ), 2 )
438
+ elif init == 'map' :
439
+ start = pm .find_MAP ()
440
+ cov = pm .find_hessian (point = start )
441
+
442
+ elif init == 'nuts' :
443
+ init_trace = pm .sample (step = pm .NUTS (), draws = n_init )
444
+ cov = pm .trace_cov (init_trace [n_init // 2 :])
445
+
446
+ start = {varname : np .mean (init_trace [varname ]) for varname in init_trace .varnames }
447
+ else :
448
+ raise NotImplemented ('Initializer {} is not supported.' .format (init ))
449
+
450
+ step = pm .NUTS (scaling = cov , is_cov = True )
451
+
452
+ return start , step
0 commit comments