Skip to content

Commit dee6575

Browse files
michaelosthegeJunpeng Lao
authored and
Junpeng Lao
committed
Supporting Population Samplers (implemented DE-MCMC) (#2735)
* catching other error types that may occur when gradients are not available * added a benchmark example for correlated dimensions * marking Metropolis as COMPATIBLE for all types, added line breaks (code style) * specifying single job to force the sample_many function * modified sampling procedure to interate chains in parallel (instead of sequentially) * updated description * indexing samplers by chain number instead of chain id * print transposes result table * created PopulationArrayStepShared base class that allows the individual sampler to be aware of other chains * modified sampling loop to account for the PopulationArrayStepShared sampler types * added the DEMetropolis sampler * raisig an error when the population is too small * verbose debug logging * removed debug print * forcing CompoundStep type * formatting * setting DEMetropolis as a blocked step method * measuring the runtime, example with both 2D z-variable and two 1D x,y variables * changed the initialization order such that variable transforms are applied * fixed a bug caused by start=None * fixes a bug in computing lambda * using a Uniform proposal with low initial scale * renamed local variable * logging the crossover and scaling * fixed a bug that caused step methods to not be copied * smarter multiprocessing * automatic multiprocessing decision, reporting relative sampling rates * print format * inheriting PopulationArraySharedStep from ArrayStepShared, using a bij attribute because DEMetropolis will use it * printing the number of effective samples per variable * docstrings and comments * falling back to sequential sampling if no population samplers are used * removed debugging stats logging * fixed nested if else * updated print statement * fixed a bug related to bijection updating * docstring and comments * refactoring for better clarity and less diff * code style * removed unused import * fixed a bug where Slice was preferred on multidimensional variables * printing the stepper hierarchy, fixed a variable name, handling non-CompoundStep methods * fixed a bug where DEMetropolis assigned itself to discrete vars, fixed a bug that happened when only one var was assigned * improved code style, including Slice in comparison * including DEMetropolis in existing tests, added test case for PopulationSamplers * fixes python 2.7 compatibility * Using multiprocessing to parallelize iteration of chain populations (initial) * added references * added a warning that DEMetropolis is experimental * forgotten space * modified the PopulationStepper to automatically use parallelization or fallback to sequential mode * avoiding a reimport, disabled chain parallelization by default * increased nchains * resolving conflicts * resolving conflicts * included DEMetropolis in new features
1 parent a825f96 commit dee6575

File tree

8 files changed

+608
-18
lines changed

8 files changed

+608
-18
lines changed

Diff for: RELEASE-NOTES.md

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
- Fixed `sample_ppc` and `sample_ppc_w` to iterate all chains(#2633)
2424
- Add Bayesian R2 score (for GLMs) `stats.r2_score` (#2696) and test (#2729).
2525

26+
### New Features
27+
- Michael Osthege added support for population-samplers and implemented differential evolution metropolis (`DEMetropolis`). For models with correlated dimensions that can not use gradient-based samplers, the `DEMetropolis` sampler can give higher effective sampling rates. (also see [PR#2735](https://github.com/pymc-devs/pymc3/pull/2735))
28+
2629

2730
## PyMC3 3.2 (October 10, 2017)
2831

Diff for: pymc3/examples/samplers_mvnormal.py

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""
2+
Comparing different samplers on a correlated bivariate normal distribution.
3+
4+
This example will sample a bivariate normal with Metropolis, NUTS and DEMetropolis
5+
at two correlations (0, 0.9) and print out the effective sample sizes, runtime and
6+
normalized effective sampling rates.
7+
"""
8+
9+
10+
import numpy as np
11+
import time
12+
import pandas as pd
13+
import pymc3 as pm
14+
import theano.tensor as tt
15+
16+
# with this flag one can switch between defining the bivariate normal as
17+
# either a 2D MvNormal (USE_XY = False) split up the two dimensions into
18+
# two variables 'x' and 'y'. The latter is recommended because it highlights
19+
# different behaviour with respect to blocking.
20+
USE_XY = True
21+
22+
def run(steppers, p):
23+
steppers = set(steppers)
24+
traces = {}
25+
effn = {}
26+
runtimes = {}
27+
28+
with pm.Model() as model:
29+
if USE_XY:
30+
x = pm.Flat('x')
31+
y = pm.Flat('y')
32+
mu = np.array([0.,0.])
33+
cov = np.array([[1.,p],[p,1.]])
34+
z = pm.MvNormal.dist(mu=mu, cov=cov, shape=(2,)).logp(tt.stack([x,y]))
35+
pot = pm.Potential('logp_xy', z)
36+
start = {'x': 0, 'y': 0}
37+
else:
38+
mu = np.array([0.,0.])
39+
cov = np.array([[1.,p],[p,1.]])
40+
z = pm.MvNormal('z', mu=mu, cov=cov, shape=(2,))
41+
start={'z': [0, 0]}
42+
43+
for step_cls in steppers:
44+
name = step_cls.__name__
45+
t_start = time.time()
46+
mt = pm.sample(
47+
draws=10000,
48+
chains=16, parallelize=False,
49+
step=step_cls(),
50+
start=start
51+
)
52+
runtimes[name] = time.time() - t_start
53+
print('{} samples across {} chains'.format(len(mt) * mt.nchains, mt.nchains))
54+
traces[name] = mt
55+
en = pm.diagnostics.effective_n(mt)
56+
print('effective: {}\r\n'.format(en))
57+
if USE_XY:
58+
effn[name] = np.mean(en['x']) / len(mt) / mt.nchains
59+
else:
60+
effn[name] = np.mean(en['z']) / len(mt) / mt.nchains
61+
return traces, effn, runtimes
62+
63+
64+
if __name__ == '__main__':
65+
methods = [
66+
pm.Metropolis,
67+
pm.Slice,
68+
pm.NUTS,
69+
pm.DEMetropolis
70+
]
71+
names = [c.__name__ for c in methods]
72+
73+
df_base = pd.DataFrame(columns=['p'] + names)
74+
df_base['p'] = [.0,.9]
75+
df_base = df_base.set_index('p')
76+
77+
df_effectiven = df_base.copy()
78+
df_runtime = df_base.copy()
79+
df_performance = df_base.copy()
80+
81+
for p in df_effectiven.index:
82+
trace, rate, runtime = run(methods, p)
83+
for name in names:
84+
df_effectiven.set_value(p, name, rate[name])
85+
df_runtime.set_value(p, name, runtime[name])
86+
df_performance.set_value(p, name, rate[name] / runtime[name])
87+
88+
print('\r\nEffective sample size [0...1]')
89+
print(df_effectiven.T.to_string(float_format='{:.3f}'.format))
90+
91+
print('\r\nRuntime [s]')
92+
print(df_runtime.T.to_string(float_format='{:.1f}'.format))
93+
94+
if 'NUTS' in names:
95+
print('\r\nNormalized effective sampling rate [0...1]')
96+
df_performance = df_performance.T / df_performance.loc[0]['NUTS']
97+
else:
98+
print('\r\nNormalized effective sampling rate [1/s]')
99+
df_performance = df_performance.T
100+
print(df_performance.to_string(float_format='{:.3f}'.format))

0 commit comments

Comments
 (0)