-
Notifications
You must be signed in to change notification settings - Fork 761
/
monte_carlo.py
152 lines (123 loc) · 5.18 KB
/
monte_carlo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
import six
import tensorflow as tf
from edward.inferences.inference import Inference
from edward.models import Empirical, RandomVariable
from edward.util import get_session
class MonteCarlo(Inference):
"""Base class for Monte Carlo inference methods.
"""
def __init__(self, latent_vars=None, data=None):
"""Initialization.
Parameters
----------
latent_vars : list or dict, optional
Collection of random variables (of type ``RandomVariable`` or
``tf.Tensor``) to perform inference on. If list, each random
variable will be approximated using a ``Empirical`` random
variable that is defined internally (with unconstrained
support). If dictionary, each value in the dictionary must be a
``Empirical`` random variable.
data : dict, optional
Data dictionary which binds observed variables (of type
``RandomVariable`` or ``tf.Tensor``) to their realizations (of
type ``tf.Tensor``). It can also bind placeholders (of type
``tf.Tensor``) used in the model to their realizations.
Examples
--------
Most explicitly, ``MonteCarlo`` is specified via a dictionary:
>>> qpi = Empirical(params=tf.Variable(tf.zeros([T, K-1])))
>>> qmu = Empirical(params=tf.Variable(tf.zeros([T, K*D])))
>>> qsigma = Empirical(params=tf.Variable(tf.zeros([T, K*D])))
>>> ed.MonteCarlo({pi: qpi, mu: qmu, sigma: qsigma}, data)
The inferred posterior is comprised of ``Empirical`` random
variables with ``T`` samples. We also automate the specification
of ``Empirical`` random variables. One can pass in a list of
latent variables instead:
>>> ed.MonteCarlo([beta], data)
>>> ed.MonteCarlo([pi, mu, sigma], data)
It defaults to ``Empirical`` random variables with 10,000 samples for
each dimension.
Notes
-----
The number of Monte Carlo iterations is set according to the
minimum of all ``Empirical`` sizes.
Initialization is assumed from ``params[0, :]``. This generalizes
initializing randomly and initializing from user input. Updates
are along this outer dimension, where iteration t updates
``params[t, :]`` in each ``Empirical`` random variable.
No warm-up is implemented. Users must run MCMC for a long period
of time, then manually burn in the Empirical random variable.
"""
if isinstance(latent_vars, list):
with tf.variable_scope("posterior"):
latent_vars = {rv: Empirical(params=tf.Variable(
tf.zeros([1e4] + rv.get_batch_shape().as_list())))
for rv in latent_vars}
elif isinstance(latent_vars, dict):
for qz in six.itervalues(latent_vars):
if not isinstance(qz, Empirical):
raise TypeError("Posterior approximation must consist of only "
"Empirical random variables.")
super(MonteCarlo, self).__init__(latent_vars, data)
def initialize(self, *args, **kwargs):
kwargs['n_iter'] = np.amin([qz.n for
qz in six.itervalues(self.latent_vars)])
super(MonteCarlo, self).initialize(*args, **kwargs)
self.n_accept = tf.Variable(0, trainable=False)
self.n_accept_over_t = self.n_accept / self.t
self.train = self.build_update()
def update(self, feed_dict=None):
"""Run one iteration of sampling for Monte Carlo.
Parameters
----------
feed_dict : dict, optional
Feed dictionary for a TensorFlow session run. It is used to feed
placeholders that are not fed during initialization.
Returns
-------
dict
Dictionary of algorithm-specific information. In this case, the
acceptance rate of samples since (and including) this iteration.
Notes
-----
We run the increment of ``t`` separately from other ops. Whether the
others op run with the ``t`` before incrementing or after incrementing
depends on which is run faster in the TensorFlow graph. Running it
separately forces a consistent behavior.
"""
if feed_dict is None:
feed_dict = {}
for key, value in six.iteritems(self.data):
if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type:
feed_dict[key] = value
sess = get_session()
_, accept_rate = sess.run([self.train, self.n_accept_over_t], feed_dict)
t = sess.run(self.increment_t)
if self.debug:
sess.run(self.op_check)
if self.logging and self.n_print != 0:
if t == 1 or t % self.n_print == 0:
summary = sess.run(self.summarize, feed_dict)
self.train_writer.add_summary(summary, t)
return {'t': t, 'accept_rate': accept_rate}
def print_progress(self, info_dict):
"""Print progress to output.
"""
if self.n_print != 0:
t = info_dict['t']
if t == 1 or t % self.n_print == 0:
self.progbar.update(t, {'Acceptance Rate': info_dict['accept_rate']})
def build_update(self):
"""Build update, which returns an assign op for parameters in
the Empirical random variables.
Any derived class of ``MonteCarlo`` **must** implement
this method.
Raises
------
NotImplementedError
"""
raise NotImplementedError()