Mercurial > pylearn
annotate pylearn/sampling/mcmc.py @ 1447:fbe470217937
Use .get_value() and .set_value() of shared instead of the .value property
author | Pascal Lamblin <lamblinp@iro.umontreal.ca> |
---|---|
date | Wed, 16 Mar 2011 20:20:02 -0400 |
parents | 492473059b37 |
children |
rev | line source |
---|---|
955
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
1 """Metropolis-Hastings Monte Carlo |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
2 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
3 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
4 See also Hamiltonian Monte Carlo (aka Hybrid Monte Carlo) in the hmc.py file. |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
5 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
6 """ |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
7 import sys |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
8 import logging |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
9 import numpy as np |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
10 from theano import function, shared |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
11 from theano import tensor as TT |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
12 import theano.sparse #installs the sparse shared var handler |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
13 from theano.printing import Print |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
14 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
15 #TODO: |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
16 # Consider how to refactor this code so that the key functions /functionality are provided as |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
17 # theano expressions?? |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
18 # It could be limiting that the implementation requires that the position be a list of shared |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
19 # variables, and basically uses procedural logic on top of that. |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
20 # |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
21 # Theano can only do function optimization on one function at a time, so until these are |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
22 # written as update expressions, they must be compiled as their own functions and cannot be |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
23 # rolled into things the user is doing anyway. |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
24 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
25 class MCMC_sampler(object): |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
26 """Generic Metropolis-Hastings Monte-Carlo sampler |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
27 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
28 The `draw` function advances the markov chain and returns the current sample by calling |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
29 `simulate` and `get_position` in sequence. |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
30 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
31 The stepsize is updated dynamically to |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
32 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
33 """ |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
34 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
35 # TODO: anyone has better guesses for these constants?? |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
36 stepsize_dec = 0.98 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
37 stepsize_min = 0.001 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
38 stepsize_max = 0.25 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
39 stepsize_inc = 1.02 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
40 target_acceptance_rate=0.7 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
41 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
42 avg_acceptance_slowness = 0.9 # used in geometric avg. 1.0 would be not moving at all |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
43 n_steps=1 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
44 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
45 def __init__(self, positions, energy_fn, |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
46 initial_stepsize=0.01, |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
47 seed=12345): |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
48 """ |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
49 :param positions: list of shared ndarray variables. |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
50 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
51 :param energy: |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
52 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
53 callable such that energy_fn(positions) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
54 returns theano vector of energies. |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
55 The len of this vector is the batchsize. |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
56 """ |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
57 |
1447
fbe470217937
Use .get_value() and .set_value() of shared instead of the .value property
Pascal Lamblin <lamblinp@iro.umontreal.ca>
parents:
955
diff
changeset
|
58 batchsize = positions[0].get_value(borrow=True).shape[0] |
955
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
59 self.s_rng = TT.shared_randomstreams.RandomStreams(seed) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
60 self.positions = positions |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
61 self.prev_energy = shared(np.zeros(batchsize) + float('inf')) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
62 self.avg_acceptance_rate = 0.5 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
63 self.stepsize = initial_stepsize |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
64 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
65 s_stepsize = TT.scalar('stepsize') |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
66 |
1447
fbe470217937
Use .get_value() and .set_value() of shared instead of the .value property
Pascal Lamblin <lamblinp@iro.umontreal.ca>
parents:
955
diff
changeset
|
67 new_positions = [p + s_stepsize * self.s_rng.normal(size=p.get_value(borrow=True).shape) |
955
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
68 for p in self.positions] |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
69 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
70 # accept-reject according to Metropolis-Hastings |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
71 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
72 energy = energy_fn(new_positions) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
73 accept = TT.exp(self.prev_energy - energy) - self.s_rng.uniform(size=(batchsize,)) >= 0 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
74 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
75 self.accept_reject_positions = function([s_stepsize], accept.mean(), |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
76 updates=[(self.prev_energy, TT.switch(accept, energy, self.prev_energy))] + [ |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
77 (q, TT.switch(accept.dimshuffle(0, *(('x',)*(q.ndim-1))), new_q, q)) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
78 for q, new_q in zip(self.positions, new_positions)]) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
79 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
80 def simulate(self, n_steps=None): |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
81 if n_steps is None: |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
82 n_steps = self.n_steps |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
83 for ss in range(n_steps): |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
84 acceptance_rate = self.accept_reject_positions(self.stepsize) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
85 self.avg_acceptance_rate = self.avg_acceptance_slowness * self.avg_acceptance_rate \ |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
86 + (1.0 - self.avg_acceptance_slowness) * acceptance_rate |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
87 if self.avg_acceptance_rate < self.target_acceptance_rate: |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
88 self.stepsize = max(self.stepsize*self.stepsize_dec,self.stepsize_min) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
89 else: |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
90 self.stepsize = min(self.stepsize*self.stepsize_inc,self.stepsize_max) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
91 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
92 def get_position(self): |
1447
fbe470217937
Use .get_value() and .set_value() of shared instead of the .value property
Pascal Lamblin <lamblinp@iro.umontreal.ca>
parents:
955
diff
changeset
|
93 return [q.get_value(borrow=True) for q in self.positions] |
955
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
94 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
95 def draw(self, n_steps=None): |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
96 """Return the current sample in the Markov chain as a list of numpy arrays |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
97 |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
98 The size of the arrays will match the size of the `initial_position` argument to |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
99 __init__. |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
100 """ |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
101 self.simulate(n_steps=n_steps) |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
102 return self.get_position() |
492473059b37
Adding sampling module
James Bergstra <bergstrj@iro.umontreal.ca>
parents:
diff
changeset
|
103 |