Skip to content

Commit fdef760

Browse files
aloctavodiaColCarroll
authored andcommitted
SMC: estimate marginal likelihood (#2563)
* SMC: estimate marginal likelihood * add example marginal likelihood computation * fix typos
1 parent bdd8ff5 commit fdef760

File tree

5 files changed

+602
-51
lines changed

5 files changed

+602
-51
lines changed

docs/source/examples.rst

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Howto
1313
notebooks/posterior_predictive.ipynb
1414
notebooks/model_comparison.ipynb
1515
notebooks/model_averaging.ipynb
16+
notebooks/Bayes_factor.ipynb
1617
notebooks/howto_debugging.ipynb
1718
notebooks/PyMC3_tips_and_heuristic.ipynb
1819
notebooks/LKJ.ipynb

docs/source/notebooks/Bayes_factor.ipynb

+578
Large diffs are not rendered by default.

docs/source/notebooks/GLM-model-selection.ipynb

+3-39
Original file line numberDiff line numberDiff line change
@@ -1452,45 +1452,9 @@
14521452
"source": [
14531453
"## Final remarks and tips\n",
14541454
"\n",
1455-
"It is important to keep in mind that, with more data point, the real underlying model (one that we used to generate the data) should outperforms other models. \n",
1455+
"It is important to keep in mind that, with more data points, the real underlying model (one that we used to generate the data) should outperforms other models. \n",
14561456
"\n",
1457-
"In general, PSIS-LOO is recommanded. To quote from [avehtari's comment](https://github.com/pymc-devs/pymc3/issues/938#issuecomment-313425552): \"I also recommend using PSIS-LOO instead of WAIC, because it's more reliable and has better diagnostics as discussed in http://link.springer.com/article/10.1007/s11222-016-9696-4 (preprint https://arxiv.org/abs/1507.04544), but if you insist to have one information criterion then leave WAIC.\""
1458-
]
1459-
},
1460-
{
1461-
"cell_type": "markdown",
1462-
"metadata": {},
1463-
"source": [
1464-
"---\n",
1465-
"\n",
1466-
"---"
1467-
]
1468-
},
1469-
{
1470-
"cell_type": "markdown",
1471-
"metadata": {},
1472-
"source": [
1473-
"## Bayes Factor"
1474-
]
1475-
},
1476-
{
1477-
"cell_type": "markdown",
1478-
"metadata": {},
1479-
"source": [
1480-
"Following text lifted directly from [JakeVDP blogpost](https://jakevdp.github.io/blog/2015/08/07/frequentism-and-bayesianism-5-model-selection/)\n",
1481-
"\n",
1482-
"The Bayesian approach proceeds very differently. Recall that the Bayesian model involves computing the odds ratio between two models:\n",
1483-
"\n",
1484-
"$$O_{21}=\\frac{P(M_{2} \\;|\\; D)}{P(M_{1} \\;|\\; D)}=\\frac{P(D \\;|\\; M_{2})}{P(D \\;|\\; M_{1})}\\frac{P(M_{2})}{P(M_{1})}$$\n",
1485-
"\n",
1486-
"Here the ratio $\\frac{P(M2)}{P(M1)}$ is the prior odds ratio, and is often assumed to be equal to 1 if no compelling prior evidence favors one model over another. The ratio $\\frac{P(D \\;|\\; M2)}{P(D \\;|\\; M1)}$ is the **Bayes factor**, and is the key to Bayesian model selection.\n",
1487-
"\n",
1488-
"\n",
1489-
"The Bayes factor can be computed by evaluating the integral over the parameter likelihood:\n",
1490-
"\n",
1491-
"$$P(D \\;|\\; M)=\\int_{\\Omega}P(D \\;|\\; \\theta,M) \\; P(\\theta \\;|\\; M) \\;d\\theta$$\n",
1492-
"\n",
1493-
"This integral is over the entire parameter space of the model, and thus can be extremely computationally intensive, especially as the dimension of the model grows beyond a few. "
1457+
"In general, PSIS-LOO is recommended. To quote from [avehtari's comment](https://github.com/pymc-devs/pymc3/issues/938#issuecomment-313425552): \"I also recommend using PSIS-LOO instead of WAIC, because it's more reliable and has better diagnostics as discussed in http://link.springer.com/article/10.1007/s11222-016-9696-4 (preprint https://arxiv.org/abs/1507.04544), but if you insist to have one information criterion then leave WAIC\". Alternatively Watanabe [says](http://watanabe-www.math.dis.titech.ac.jp/users/swatanab/index.html) \"WAIC is a better approximator of the generalization error than the pareto smoothing importance sampling cross validation. The Pareto smoothing cross validation may be the better approximator of the cross validation than WAIC, however, it is not of the generalization error\"."
14941458
]
14951459
},
14961460
{
@@ -1518,7 +1482,7 @@
15181482
"name": "python",
15191483
"nbconvert_exporter": "python",
15201484
"pygments_lexer": "ipython3",
1521-
"version": "3.5.2"
1485+
"version": "3.6.1"
15221486
},
15231487
"widgets": {
15241488
"state": {

pymc3/step_methods/smc.py

+16-10
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ def __init__(self, vars=None, out_vars=None, n_chains=100, scaling=1., covarianc
165165
self.accepted = 0
166166

167167
self.beta = 0
168+
self.sjs = 1
168169
self.stage = 0
169170
self.chain_index = 0
170171
self.resampling_indexes = np.arange(n_chains)
@@ -276,23 +277,25 @@ def calc_beta(self):
276277
tempering parameter of the current stage
277278
weights : :class:`numpy.ndarray`
278279
Importance weights (floats)
280+
sj : float
281+
Mean of unnormalized weights
279282
"""
280-
low_beta = self.beta
283+
low_beta = old_beta = self.beta
281284
up_beta = 2.
282-
old_beta = self.beta
283285

284286
while up_beta - low_beta > 1e-6:
285287
current_beta = (low_beta + up_beta) / 2.
286-
temp = np.exp((current_beta - self.beta) * (self.likelihoods - self.likelihoods.max()))
287-
cov_temp = np.std(temp) / np.mean(temp)
288+
weights_un = np.exp((current_beta - self.beta) *
289+
(self.likelihoods - self.likelihoods.max()))
290+
sj = np.mean(weights_un)
291+
cov_temp = np.std(weights_un) / sj
288292
if cov_temp > self.coef_variation:
289293
up_beta = current_beta
290294
else:
291295
low_beta = current_beta
292296

293-
beta = current_beta
294-
weights = temp / np.sum(temp)
295-
return beta, old_beta, weights
297+
weights = weights_un / np.sum(weights_un)
298+
return current_beta, old_beta, weights, sj
296299

297300
def calc_covariance(self):
298301
"""Calculate trace covariance matrix based on importance weights.
@@ -551,7 +554,8 @@ def sample_smc(n_steps, n_chains=100, step=None, start=None, homepath=None, stag
551554

552555
step.population, step.array_population, step.likelihoods = step.select_end_points(
553556
mtrace)
554-
step.beta, step.old_beta, step.weights = step.calc_beta()
557+
step.beta, step.old_beta, step.weights, sj = step.calc_beta()
558+
step.sjs *= sj
555559

556560
if step.beta > 1.:
557561
pm._log.info('Beta > 1.: %f' % step.beta)
@@ -576,8 +580,8 @@ def sample_smc(n_steps, n_chains=100, step=None, start=None, homepath=None, stag
576580
pm._log.info('Sample final stage')
577581
step.stage = -1
578582
chains = stage_handler.clean_directory(step.stage, chains, rm_flag)
579-
temp = np.exp((1 - step.old_beta) * (step.likelihoods - step.likelihoods.max()))
580-
step.weights = temp / np.sum(temp)
583+
weights_un = np.exp((1 - step.old_beta) * (step.likelihoods - step.likelihoods.max()))
584+
step.weights = weights_un / np.sum(weights_un)
581585
step.covariance = step.calc_covariance()
582586
step.proposal_dist = choose_proposal(step.proposal_name, scale=step.covariance)
583587
step.resampling_indexes = step.resample()
@@ -590,6 +594,8 @@ def sample_smc(n_steps, n_chains=100, step=None, start=None, homepath=None, stag
590594
_iter_parallel_chains(**sample_args)
591595

592596
stage_handler.dump_atmip_params(step)
597+
598+
model.marginal_likelihood = step.sjs
593599
return stage_handler.create_result_trace(step.stage,
594600
idxs=range(n_steps),
595601
step=step,

pymc3/tests/test_smc.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def setup_class(self):
1818
super(TestSMC, self).setup_class()
1919
self.test_folder = mkdtemp(prefix='ATMIP_TEST')
2020

21-
self.n_chains = 300
22-
self.n_steps = 100
21+
self.n_chains = 1000
22+
self.n_steps = 10
2323
self.tune_interval = 25
2424

2525
n = 4
@@ -76,6 +76,8 @@ def test_sample_n_core(self, n_jobs, stage):
7676
x = mtrace.get_values('X')
7777
mu1d = np.abs(x).mean(axis=0)
7878
np.testing.assert_allclose(self.muref, mu1d, rtol=0., atol=0.03)
79+
# Scenario IV Ching, J. & Chen, Y. 2007
80+
assert np.round(np.log(self.ATMIP_test.marginal_likelihood)) == -11.0
7981

8082
def test_stage_handler(self):
8183
stage_number = -1

0 commit comments

Comments
 (0)