Repository: lisa-lab/pylearn2

Issue:
AIS hasn't been run for some cases
AIS.fX has no self argument, which suggests that that AIS has never been run in a mode that results in fX being called. We should fix fX and make sure whatever code path is meant to call it is working.



Discussion:
To be clear, we are referring to [this](https://github.com/lisa-lab/pylearn2/blob/master/pylearn2/rbm_tools.py) AIS implementation. We should also move the file to a better location and deprecate the current location.

I just had a look and it appears `AIS.fX` **is** called when the `dflt_betas` are initialized.

However, `test_ais.test_ais()` only calls `test_ais.ais_data`. It is explained in a comment that `test_ais.ais_nodata()` can be off but that this is expected. Maybe we should change this method so that it makes sure the AIS estimate is in the right ballpark (e.g. under 1% error) instead of spot-on?


Resolving patch:
diff --git a/pylearn2/rbm_tools.py b/pylearn2/rbm_tools.py
--- a/pylearn2/rbm_tools.py
+++ b/pylearn2/rbm_tools.py
@@ -155,7 +155,7 @@ def rbm_ais(rbm_params, n_runs, visbias_a=None, data=None,
     """
     (weights, visbias, hidbias) = rbm_params
 
-    rng = make_np_rng(rng, seed, ['random_sample','rand'])
+    rng = make_np_rng(rng, seed, ['random_sample', 'rand'])
 
     if data is None:
         if visbias_a is None:
@@ -330,7 +330,7 @@ def rbm_ais_gibbs_for_v(rbmA_params, rbmB_params, beta, v_sample, seed=23098):
 
     # equation 15 (Salakhutdinov & Murray 2008)
     ph_a = nnet.sigmoid((1 - beta) * (tensor.dot(v_sample, weights_a) +
-                                    hidbias_a))
+                        hidbias_a))
     ha_sample = theano_rng.binomial(size=(v_sample.shape[0], len(hidbias_a)),
                                     n=1, p=ph_a, dtype=config.floatX)
 
@@ -341,7 +341,7 @@ def rbm_ais_gibbs_for_v(rbmA_params, rbmB_params, beta, v_sample, seed=23098):
 
     # equation 17 (Salakhutdinov & Murray 2008)
     pv_act = (1 - beta) * (tensor.dot(ha_sample, weights_a.T) + visbias_a) + \
-                beta * (tensor.dot(hb_sample, weights_b.T) + visbias_b)
+             beta * (tensor.dot(hb_sample, weights_b.T) + visbias_b)
     pv = nnet.sigmoid(pv_act)
     new_v_sample = theano_rng.binomial(
         size=(v_sample.shape[0], len(visbias_b)),
@@ -371,13 +371,13 @@ class AIS(object):
                   fe_{k-1}(v_{k-1}) - fe_{k}(v_{k-1})
     """
 
-    def fX(a):
-        return numpy.asarray(a, dtype=config.floatX)
-
     # default configuration for interpolating distributions
-    dflt_beta = numpy.hstack((fX(numpy.linspace(0, 0.5, 1e3)),
-                              fX(numpy.linspace(0.5, 0.9, 1e4)),
-                              fX(numpy.linspace(0.9, 1.0, 1e4))))
+    dflt_beta = numpy.hstack((numpy.asarray(numpy.linspace(0, 0.5, 1e3),
+                                            dtype=config.floatX),
+                              numpy.asarray(numpy.linspace(0.5, 0.9, 1e4),
+                                            dtype=config.floatX),
+                              numpy.asarray(numpy.linspace(0.9, 1.0, 1e4),
+                                            dtype=config.floatX)))
 
     def __init__(self, sample_fn, free_energy_fn, v_sample0, n_runs,
                  log_int=500):
@@ -418,8 +418,8 @@ def __init__(self, sample_fn, free_energy_fn, v_sample0, n_runs,
         # utility function for safely computing log-mean of the ais weights
         ais_w = tensor.vector()
         dlogz = (
-            tensor.log(tensor.mean(tensor.exp(ais_w - tensor.max(ais_w)))) \
-                + tensor.max(ais_w)
+            tensor.log(tensor.mean(tensor.exp(ais_w - tensor.max(ais_w))))
+            + tensor.max(ais_w)
         )
         self.log_mean = theano.function([ais_w], dlogz,
                                         allow_input_downcast=False)
@@ -442,7 +442,7 @@ def set_betas(self, betas=None, key_betas=None):
         self.key_betas = None if key_betas is None else numpy.sort(key_betas)
 
         betas = numpy.array(betas, dtype=config.floatX) \
-                if betas is not None else self.dflt_beta
+            if betas is not None else self.dflt_beta
         # insert key temperatures within
         if key_betas is not None:
             betas = numpy.hstack((betas, key_betas))
@@ -457,7 +457,7 @@ def run(self, n_steps=1):
         .. math::
 
             log_w^i += fe_{k-1}(v_{k-1}) - fe_{k}(v_{k-1})
-        
+
         recursively for all temperatures.
 
         Parameters
@@ -480,8 +480,9 @@ def run(self, n_steps=1):
         for i in range(len(self.betas) - 1):
             bp, bp1 = self.betas[i], self.betas[i + 1]
             # log-ratio of (free) energies for two nearby temperatures
-            self.log_ais_w += self.free_energy_fn(bp,  state) - \
-                              self.free_energy_fn(bp1, state)
+            self.log_ais_w += \
+                self.free_energy_fn(bp, state) - \
+                self.free_energy_fn(bp1, state)
             # log standard deviation of AIS weights (kind of deprecated)
             if (i + 1) % self.log_int == 0:
                 m = numpy.max(self.log_ais_w)


Test patch:
diff --git a/pylearn2/tests/rbm/test_ais.py b/pylearn2/tests/rbm/test_ais.py
--- a/pylearn2/tests/rbm/test_ais.py
+++ b/pylearn2/tests/rbm/test_ais.py
@@ -13,6 +13,7 @@
 from pylearn2 import rbm_tools
 from pylearn2.models import rbm
 
+
 def load_rbm_params(fname):
     mnistvh = io.loadmat(fname)
     rbm_params = [numpy.asarray(mnistvh['vishid'], dtype=config.floatX),
@@ -20,6 +21,7 @@ def load_rbm_params(fname):
                   numpy.asarray(mnistvh['hidbiases'][0], dtype=config.floatX)]
     return rbm_params
 
+
 def compute_logz(rbm_params):
     (nvis, nhid) = rbm_params[0].shape
 
@@ -34,13 +36,15 @@ def compute_logz(rbm_params):
 
     return rbm_tools.compute_log_z(model, free_energy_fn)
 
+
 def ais_nodata(fname, do_exact=True):
 
     rbm_params = load_rbm_params(fname)
 
     # ais estimate using tempered models as intermediate distributions
     t1 = time.time()
-    (logz, log_var_dz), aisobj = rbm_tools.rbm_ais(rbm_params, n_runs=100, seed=123)
+    (logz, log_var_dz), aisobj = \
+        rbm_tools.rbm_ais(rbm_params, n_runs=100, seed=123)
     print 'AIS logZ         : %f' % logz
     print '    log_variance : %f' % log_var_dz
     print 'Elapsed time: ', time.time() - t1
@@ -48,7 +52,9 @@ def ais_nodata(fname, do_exact=True):
     if do_exact:
         exact_logz = compute_logz(rbm_params)
         print 'Exact logZ = %f' % exact_logz
-        numpy.testing.assert_almost_equal(exact_logz, logz, decimal=0)
+        # accept less than 1% error
+        assert abs(exact_logz - logz) < 0.01*exact_logz
+
 
 def ais_data(fname, do_exact=True):
 
@@ -61,7 +67,8 @@ def ais_data(fname, do_exact=True):
 
     # run ais using B=0 model with ML visible biases
     t1 = time.time()
-    (logz, log_var_dz), aisobj = rbm_tools.rbm_ais(rbm_params, n_runs=100, seed=123, data=data)
+    (logz, log_var_dz), aisobj = \
+        rbm_tools.rbm_ais(rbm_params, n_runs=100, seed=123, data=data)
     print 'AIS logZ         : %f' % logz
     print '    log_variance : %f' % log_var_dz
     print 'Elapsed time: ', time.time() - t1
@@ -71,10 +78,10 @@ def ais_data(fname, do_exact=True):
         print 'Exact logZ = %f' % exact_logz
         numpy.testing.assert_almost_equal(exact_logz, logz, decimal=0)
 
+
 def test_ais():
 
     ais_data('mnistvh.mat', do_exact=True)
 
     # Estimate can be off when using the wrong base-rate model.
-    # The test below reports log Z = 213.804 instead of 215.138
-    #ais_nodata('mnistvh.mat', do_exact=True)
+    ais_nodata('mnistvh.mat', do_exact=True)