Repository: pyro-ppl/pyro
Pull request: Resolve changes in tensor/variable API with PyTorch master
This resolves a few minor issues so as to keep in sync with PyTorch master.
 - `tensor[0]` returns a `Variable` scalar instead of a python numeric type. We need to use `.item()` to convert a PyTorch scalar into a python numeric type.
 - `.data` returns an instance of type `Variable` and not `torch.Tensor`. Instead of using `.data.cpu().numpy()`, we are now using `.detach().cpu().numpy()`.

NOTE: Tests will fail until we update the PyTorch wheels for travis. The core changes are really small, so would suggest reviewing only once the tests pass.

Only partially resolves #815. We will need more renaming changes later.

Problem statement:
Support PyTorch after Variable+Tensor were merged
Pyro is behind PyTorch master and needs to be updated now that `Tensor` and `Variable` were merged. This is logically orthogonal to the rest of our refactoring, although there will be lots of superficial merge conflicts.

Hints from issue discussion:

Commit message: detach and call numpy

Patch to review:
diff --git a/examples/air/viz.py b/examples/air/viz.py
index 21fecf5877..16b5ebd9b4 100644
--- a/examples/air/viz.py
+++ b/examples/air/viz.py
@@ -35,7 +35,7 @@ def draw_one(imgarr, z_arr):
     # Note that this clipping makes the visualisation somewhat
     # misleading, as it incorrectly suggests objects occlude one
     # another.
-    clipped = np.clip(imgarr.data.cpu().numpy(), 0, 1)
+    clipped = np.clip(imgarr.detach().cpu().numpy(), 0, 1)
     img = arr2img(clipped).convert('RGB')
     draw = ImageDraw.Draw(img)
     for k, z in enumerate(z_arr):
diff --git a/examples/contrib/named/mixture.py b/examples/contrib/named/mixture.py
index df51b76934..8e0912414a 100644
--- a/examples/contrib/named/mixture.py
+++ b/examples/contrib/named/mixture.py
@@ -65,7 +65,7 @@ def main(args):
 
     print('Parameters:')
     for name in sorted(pyro.get_param_store().get_all_param_names()):
-        print('{} = {}'.format(name, pyro.param(name).data.cpu().numpy()))
+        print('{} = {}'.format(name, pyro.param(name).detach().cpu().numpy()))
 
 
 if __name__ == '__main__':
diff --git a/examples/contrib/named/tree_data.py b/examples/contrib/named/tree_data.py
index 85fc66dced..99847b84f9 100644
--- a/examples/contrib/named/tree_data.py
+++ b/examples/contrib/named/tree_data.py
@@ -96,7 +96,7 @@ def main(args):
 
     print('Parameters:')
     for name in sorted(pyro.get_param_store().get_all_param_names()):
-        print('{} = {}'.format(name, pyro.param(name).data.cpu().numpy()))
+        print('{} = {}'.format(name, pyro.param(name).detach().cpu().numpy()))
 
 
 if __name__ == '__main__':
diff --git a/examples/inclined_plane.py b/examples/inclined_plane.py
index a27ed620c4..7e311cfc84 100644
--- a/examples/inclined_plane.py
+++ b/examples/inclined_plane.py
@@ -38,10 +38,10 @@ def simulate(mu, length=2.0, phi=np.pi / 6.0, dt=0.005, noise_sigma=None):
     acceleration = Variable(torch.Tensor([little_g * np.sin(phi)])) - \
         Variable(torch.Tensor([little_g * np.cos(phi)])) * mu
 
-    if acceleration.data[0] <= 0.0:             # the box doesn't slide if the friction is too large
+    if acceleration.item() <= 0.0:             # the box doesn't slide if the friction is too large
         return Variable(torch.Tensor([1.0e5]))  # return a very large time instead of infinity
 
-    while displacement.data[0] < length:  # otherwise slide to the end of the inclined plane
+    while displacement.item() < length:  # otherwise slide to the end of the inclined plane
         displacement += velocity * dt
         velocity += acceleration * dt
         T += dt
@@ -67,7 +67,7 @@ def analytic_T(mu, length=2.0, phi=np.pi / 6.0):
 torch.manual_seed(2)
 observed_data = torch.cat([simulate(Variable(torch.Tensor([mu0])), noise_sigma=time_measurement_sigma)
                            for _ in range(N_obs)])
-observed_mean = np.mean([T.data[0] for T in observed_data])
+observed_mean = np.mean([T.item() for T in observed_data])
 
 
 # define model with uniform prior on mu and gaussian noise on the descent time
@@ -100,8 +100,8 @@ def main(args):
     posterior_std_dev = torch.std(torch.cat(posterior_samples), 0)
 
     # report results
-    inferred_mu = posterior_mean.data[0]
-    inferred_mu_uncertainty = posterior_std_dev.data[0]
+    inferred_mu = posterior_mean.item()
+    inferred_mu_uncertainty = posterior_std_dev.item()
     print("the coefficient of friction inferred by pyro is %.3f +- %.3f" %
           (inferred_mu, inferred_mu_uncertainty))
 
@@ -111,9 +111,9 @@ def main(args):
     # but will be systematically off from the third number
     print("the mean observed descent time in the dataset is: %.4f seconds" % observed_mean)
     print("the (forward) simulated descent time for the inferred (mean) mu is: %.4f seconds" %
-          simulate(posterior_mean).data[0])
+          simulate(posterior_mean).item())
     print(("disregarding measurement noise, elementary calculus gives the descent time\n" +
-           "for the inferred (mean) mu as: %.4f seconds") % analytic_T(posterior_mean.data[0]))
+           "for the inferred (mean) mu as: %.4f seconds") % analytic_T(posterior_mean.item()))
 
     """
     ################## EXERCISE ###################
diff --git a/examples/ss_vae_M2.py b/examples/ss_vae_M2.py
index ef6f1b73fc..95ad32f163 100644
--- a/examples/ss_vae_M2.py
+++ b/examples/ss_vae_M2.py
@@ -276,7 +276,7 @@ def get_accuracy(data_loader, classifier_fn, batch_size):
     for pred, act in zip(predictions, actuals):
         for i in range(pred.size(0)):
             v = torch.sum(pred[i] == act[i])
-            accurate_preds += (v.data[0] == 10)
+            accurate_preds += (v.item() == 10)
 
     # calculate the accuracy between 0 and 1
     accuracy = (accurate_preds * 1.0) / (len(predictions) * batch_size)
diff --git a/examples/utils/vae_plots.py b/examples/utils/vae_plots.py
index 25734d7018..12ee3b9b68 100644
--- a/examples/utils/vae_plots.py
+++ b/examples/utils/vae_plots.py
@@ -80,9 +80,9 @@ def plot_tsne(z_mu, classes, name):
     import matplotlib.pyplot as plt
     from sklearn.manifold import TSNE
     model_tsne = TSNE(n_components=2, random_state=0)
-    z_states = z_mu.data.cpu().numpy()
+    z_states = z_mu.detach().cpu().numpy()
     z_embed = model_tsne.fit_transform(z_states)
-    classes = classes.data.cpu().numpy()
+    classes = classes.detach().cpu().numpy()
     fig666 = plt.figure()
     for ic in range(10):
         ind_vec = np.zeros_like(classes)
diff --git a/examples/vae.py b/examples/vae.py
index 552c8b21be..89e1d57f63 100644
--- a/examples/vae.py
+++ b/examples/vae.py
@@ -191,9 +191,9 @@ def main(args):
                         for index in reco_indices:
                             test_img = x[index, :]
                             reco_img = vae.reconstruct_img(test_img)
-                            vis.image(test_img.contiguous().view(28, 28).data.cpu().numpy(),
+                            vis.image(test_img.contiguous().view(28, 28).detach().cpu().numpy(),
                                       opts={'caption': 'test image'})
-                            vis.image(reco_img.contiguous().view(28, 28).data.cpu().numpy(),
+                            vis.image(reco_img.contiguous().view(28, 28).detach().cpu().numpy(),
                                       opts={'caption': 'reconstructed image'})
 
             # report test diagnostics
diff --git a/examples/vae_comparison.py b/examples/vae_comparison.py
index 86d412cff5..cb1558472f 100644
--- a/examples/vae_comparison.py
+++ b/examples/vae_comparison.py
@@ -134,7 +134,7 @@ def test(self, epoch):
                 n = min(x.size(0), 8)
                 comparison = torch.cat([x[:n],
                                         recon_x.view(self.args.batch_size, 1, 28, 28)[:n]])
-                save_image(comparison.data.cpu(),
+                save_image(comparison.detach().cpu(),
                            os.path.join(OUTPUT_DIR, 'reconstruction_' + str(epoch) + '.png'),
                            nrow=n)
 
@@ -166,7 +166,7 @@ def compute_loss_and_gradient(self, x):
         if self.mode == TRAIN:
             loss.backward()
             self.optimizer.step()
-        return loss.data[0]
+        return loss.item()
 
     def initialize_optimizer(self, lr=1e-3):
         model_params = itertools.chain(self.vae_encoder.parameters(), self.vae_decoder.parameters())
diff --git a/pyro/__init__.py b/pyro/__init__.py
index fb73414fc7..fe9960a824 100644
--- a/pyro/__init__.py
+++ b/pyro/__init__.py
@@ -279,7 +279,9 @@ def irange(name, size, subsample_size=None, subsample=None, use_cuda=None):
             for i in subsample:
                 indep_context.next_context()
                 with indep_context:
-                    yield i
+                    # convert to python numeric type as functions like torch.ones(*args)
+                    # do not work with dim 0 torch.Tensor instances.
+                    yield i.item()
 
 
 def map_data(name, data, fn, batch_size=None, batch_dim=0, use_cuda=None):
diff --git a/pyro/infer/abstract_infer.py b/pyro/infer/abstract_infer.py
index 2eca93bd51..f91aceae4e 100644
--- a/pyro/infer/abstract_infer.py
+++ b/pyro/infer/abstract_infer.py
@@ -74,7 +74,7 @@ def sample(self, *args, **kwargs):
         if sample_shape:
             raise ValueError("Arbitrary `sample_shape` not supported by Histogram class.")
         d, values = self._dist_and_values(*args, **kwargs)
-        ix = d.sample().data[0]
+        ix = d.sample()
         return values[ix]
 
     __call__ = sample
@@ -151,4 +151,4 @@ def __call__(self, *args, **kwargs):
         if not isinstance(logits, torch.autograd.Variable):
             logits = Variable(logits)
         ix = dist.Categorical(logits=logits).sample()
-        return traces[ix.data[0]]
+        return traces[ix]
diff --git a/pyro/infer/mcmc/hmc.py b/pyro/infer/mcmc/hmc.py
index 6c68ae5118..3b605266c1 100644
--- a/pyro/infer/mcmc/hmc.py
+++ b/pyro/infer/mcmc/hmc.py
@@ -99,7 +99,7 @@ def sample(self, trace):
         energy_current = self._energy(z, r)
         delta_energy = energy_proposal - energy_current
         rand = pyro.sample('rand_t='.format(self._t), dist.Uniform(ng_zeros(1), ng_ones(1)))
-        if rand.log().data[0] < -delta_energy.data[0]:
+        if rand.log() < -delta_energy:
             self._accept_cnt += 1
             z = z_new
         self._t += 1
diff --git a/pyro/infer/mcmc/nuts.py b/pyro/infer/mcmc/nuts.py
index 413727067d..51e2ac15f8 100644
--- a/pyro/infer/mcmc/nuts.py
+++ b/pyro/infer/mcmc/nuts.py
@@ -83,7 +83,7 @@ def _build_basetree(self, z, r, z_grads, log_slice, direction):
         z_new, r_new, z_grads, potential_energy = single_step_velocity_verlet(
             z, r, self._potential_energy, step_size, z_grads=z_grads)
         energy = potential_energy + self._kinetic_energy(r_new)
-        dE = (log_slice + energy).data[0]
+        dE = log_slice + energy
 
         # As a part of the slice sampling process (see below), along the trajectory
         #     we eliminate states which p(z, r) < u, or dE < 0.
@@ -131,7 +131,7 @@ def _build_tree(self, z, r, z_grads, log_slice, direction, tree_depth):
             other_half_tree_prob = other_half_tree.size / tree_size
             is_other_half_tree = pyro.sample("is_other_halftree",
                                              dist.Bernoulli(ps=ng_ones(1) * other_half_tree_prob))
-            if int(is_other_half_tree.data[0]) == 1:
+            if int(is_other_half_tree.item()) == 1:
                 z_proposal = other_half_tree.z_proposal
 
         # leaves of the full tree are determined by the direction
@@ -190,7 +190,7 @@ def sample(self, trace):
         for tree_depth in range(self.max_tree_depth + 1):
             direction = pyro.sample("direction_t={}_treedepth={}".format(self._t, tree_depth),
                                     dist.Bernoulli(ps=ng_ones(1) * 0.5))
-            direction = int(direction.data[0])
+            direction = int(direction.item())
             if direction == 1:  # go to the right, start from the right leaf of current tree
                 new_tree = self._build_tree(z_right, r_right, z_right_grads,
                                             log_slice, direction, tree_depth)
@@ -210,7 +210,7 @@ def sample(self, trace):
 
             accepted_prob = pyro.sample("acceptedprob_t={}_treedepth={}".format(self._t, tree_depth),
                                         dist.Uniform(ng_zeros(1), ng_ones(1)))
-            if accepted_prob.data[0] < new_tree.size / tree_size:
+            if accepted_prob < new_tree.size / tree_size:
                 is_accepted = True
                 z = new_tree.z_proposal
 
diff --git a/pyro/infer/svi.py b/pyro/infer/svi.py
index 53aef465d0..9e951bda9e 100644
--- a/pyro/infer/svi.py
+++ b/pyro/infer/svi.py
@@ -54,7 +54,7 @@ def __init__(self,
                 self._loss = copy.copy(loss)
 
                 def new_loss(model, guide, *args, **kwargs):
-                    return self._loss(model, guide, *args, **kwargs).data[0]
+                    return self._loss(model, guide, *args, **kwargs).item()
 
                 self.loss = new_loss
 
diff --git a/pyro/nn/auto_reg_nn.py b/pyro/nn/auto_reg_nn.py
index e9ad7c8cf7..e3ced5c487 100644
--- a/pyro/nn/auto_reg_nn.py
+++ b/pyro/nn/auto_reg_nn.py
@@ -87,7 +87,7 @@ def __init__(self, input_dim, hidden_dim, output_dim_multiplier=1,
 
         for k in range(hidden_dim):
             # fill in mask1
-            m_k = self.mask_encoding[k]
+            m_k = self.mask_encoding[k].item()
             slice_k = torch.cat([torch.ones(m_k), torch.zeros(input_dim - m_k)])
             for j in range(input_dim):
                 self.mask1[k, self.permutation[j]] = slice_k[j]
diff --git a/pyro/poutine/trace.py b/pyro/poutine/trace.py
index ea5dd323e9..b8ab6cd969 100644
--- a/pyro/poutine/trace.py
+++ b/pyro/poutine/trace.py
@@ -12,7 +12,7 @@
 
 def _warn_if_nan(name, value):
     if isinstance(value, Variable):
-        value = value.data[0]
+        value = value.item()
     if is_nan(value):
         warnings.warn("Encountered NAN log_pdf at site '{}'".format(name))
     if is_inf(value) and value > 0:
diff --git a/tests/common.py b/tests/common.py
index 605386615d..e47a38c1ec 100644
--- a/tests/common.py
+++ b/tests/common.py
@@ -124,16 +124,6 @@ def is_iterable(obj):
         return False
 
 
-def _unwrap_variables(x, y):
-    if isinstance(x, Variable) and isinstance(y, Variable):
-        return x.data, y.data
-    elif isinstance(x, Variable) or isinstance(y, Variable):
-        raise AssertionError(
-            "cannot compare {} and {}".format(
-                type(x), type(y)))
-    return x, y
-
-
 def assert_tensors_equal(a, b, prec=1e-5, msg=''):
     assert a.size() == b.size(), msg
     if prec == 0:
@@ -148,7 +138,7 @@ def assert_tensors_equal(a, b, prec=1e-5, msg=''):
         diff[nan_mask] = 0
         if diff.is_signed():
             diff = diff.abs()
-        max_err = diff.max()
+        max_err = diff.max().item()
         assert max_err < prec, msg
 
 
@@ -180,8 +170,6 @@ def _safe_coalesce(t):
 # TODO Split this into assert_equal() and assert_close() or assert_almost_equal().
 # TODO Use atol and rtol instead of prec
 def assert_equal(x, y, prec=1e-5, msg=''):
-    x, y = _unwrap_variables(x, y)
-
     if torch.is_tensor(x) and torch.is_tensor(y):
         assert_equal(x.is_sparse, y.is_sparse, prec, msg)
         if x.is_sparse:
diff --git a/tests/contrib/gp/test_kernels.py b/tests/contrib/gp/test_kernels.py
index bd43bd6ca8..1496461f70 100644
--- a/tests/contrib/gp/test_kernels.py
+++ b/tests/contrib/gp/test_kernels.py
@@ -16,7 +16,7 @@ def test_forward_rbf():
     assert K.dim() == 2
     assert K.size(0) == 2
     assert K.size(1) == 2
-    assert_equal(K.data.sum(), 0.30531)
+    assert_equal(K.sum().item(), 0.30531)
 
 
 def test_Kdiag():
diff --git a/tests/contrib/gp/test_models.py b/tests/contrib/gp/test_models.py
index 8f9d79a3d6..b7e85a2052 100644
--- a/tests/contrib/gp/test_models.py
+++ b/tests/contrib/gp/test_models.py
@@ -26,7 +26,7 @@ def test_forward_gpr():
     assert cov.size(0) == 2
     assert cov.size(1) == 2
     assert_equal(loc, y)
-    assert_equal(cov.data.abs().sum(), 0)
+    assert_equal(cov.abs().sum().item(), 0)
 
 
 def test_forward_sgpr():
@@ -48,7 +48,7 @@ def test_forward_sgpr():
     assert cov.size(0) == 2
     assert cov.size(1) == 2
     assert_equal(loc, y)
-    assert_equal(cov.data.abs().sum(), 0)
+    assert_equal(cov.abs().sum().item(), 0)
 
 
 def test_forward_sgpr_vs_gpr():
diff --git a/tests/distributions/dist_fixture.py b/tests/distributions/dist_fixture.py
index f5cc94c1b0..cbb4d83060 100644
--- a/tests/distributions/dist_fixture.py
+++ b/tests/distributions/dist_fixture.py
@@ -75,7 +75,7 @@ def _convert_logits_to_ps(self, dist_params):
             logits = Variable(torch.Tensor(dist_params.pop('logits')))
             is_multidimensional = self.get_test_distribution_name() != 'Bernoulli'
             ps, _ = get_probs_and_logits(logits=logits, is_multidimensional=is_multidimensional)
-            dist_params['ps'] = list(ps.data.cpu().numpy())
+            dist_params['ps'] = list(ps.detach().cpu().numpy())
         return dist_params
 
     def get_scipy_logpdf(self, idx):
diff --git a/tests/distributions/test_categorical.py b/tests/distributions/test_categorical.py
index 40a57a9c41..b9d3777ae5 100644
--- a/tests/distributions/test_categorical.py
+++ b/tests/distributions/test_categorical.py
@@ -37,16 +37,16 @@ def setUp(self):
         self.support = torch.Tensor([[0, 0], [1, 1], [2, 2]])
 
     def test_log_pdf(self):
-        log_px_torch = dist.Categorical(self.ps).log_prob(self.test_data).sum().data[0]
-        log_px_np = float(sp.multinomial.logpmf(np.array([0, 0, 1]), 1, self.ps.data.cpu().numpy()))
+        log_px_torch = dist.Categorical(self.ps).log_prob(self.test_data).sum().item()
+        log_px_np = float(sp.multinomial.logpmf(np.array([0, 0, 1]), 1, self.ps.detach().cpu().numpy()))
         assert_equal(log_px_torch, log_px_np, prec=1e-4)
 
     def test_mean_and_var(self):
-        torch_samples = [dist.Categorical(self.ps).sample().data.cpu().numpy()
+        torch_samples = [dist.Categorical(self.ps).sample().detach().cpu().numpy()
                          for _ in range(self.n_samples)]
         _, counts = np.unique(torch_samples, return_counts=True)
         computed_mean = float(counts[0]) / self.n_samples
-        assert_equal(computed_mean, self.analytic_mean.data.cpu().numpy()[0], prec=0.05)
+        assert_equal(computed_mean, self.analytic_mean.detach().cpu().numpy()[0], prec=0.05)
 
     def test_support_non_vectorized(self):
         s = dist.Categorical(self.d_ps[0].squeeze(0)).enumerate_support()
diff --git a/tests/distributions/test_delta.py b/tests/distributions/test_delta.py
index 64dfeca1d1..f51cc1101e 100644
--- a/tests/distributions/test_delta.py
+++ b/tests/distributions/test_delta.py
@@ -26,21 +26,21 @@ def setUp(self):
         self.n_samples = 10
 
     def test_log_pdf(self):
-        log_px_torch = dist.Delta(self.v).log_prob(self.test_data).sum().data[0]
-        assert_equal(log_px_torch, 0)
+        log_px_torch = dist.Delta(self.v).log_prob(self.test_data).sum()
+        assert_equal(log_px_torch.item(), 0)
 
     def test_batch_log_prob(self):
         log_px_torch = dist.Delta(self.vs_expanded).log_prob(self.batch_test_data_1).data
-        assert_equal(torch.sum(log_px_torch), 0)
+        assert_equal(log_px_torch.sum().item(), 0)
         log_px_torch = dist.Delta(self.vs_expanded).log_prob(self.batch_test_data_2).data
-        assert_equal(torch.sum(log_px_torch), float('-inf'))
+        assert_equal(log_px_torch.sum().item(), float('-inf'))
 
     def test_batch_log_prob_shape(self):
         assert dist.Delta(self.vs).log_prob(self.batch_test_data_3).size() == (4, 1)
         assert dist.Delta(self.v).log_prob(self.batch_test_data_3).size() == (4, 1)
 
     def test_mean_and_var(self):
-        torch_samples = [dist.Delta(self.v).sample().data.cpu().numpy()
+        torch_samples = [dist.Delta(self.v).sample().detach().cpu().numpy()
                          for _ in range(self.n_samples)]
         torch_mean = np.mean(torch_samples)
         torch_var = np.var(torch_samples)
diff --git a/tests/distributions/test_distributions.py b/tests/distributions/test_distributions.py
index 64ea88e4bd..c88b305561 100644
--- a/tests/distributions/test_distributions.py
+++ b/tests/distributions/test_distributions.py
@@ -10,7 +10,7 @@
 
 
 def _unwrap_variable(x):
-    return x.data.cpu().numpy()
+    return x.detach().cpu().numpy()
 
 
 def _log_prob_shape(dist, x_size=torch.Size()):
diff --git a/tests/distributions/test_iaf.py b/tests/distributions/test_iaf.py
index 2e3c06a0bd..b6e4c17568 100644
--- a/tests/distributions/test_iaf.py
+++ b/tests/distributions/test_iaf.py
@@ -75,7 +75,7 @@ def nonzero(x):
                     epsilon_vector = torch.zeros(1, input_dim)
                     epsilon_vector[0, j] = self.epsilon
                     delta = (arn(x + Variable(epsilon_vector)) - arn(x)) / self.epsilon
-                    jacobian[j, k] = float(delta[0, k + output_index * input_dim].data.cpu().numpy()[0])
+                    jacobian[j, k] = float(delta[0, k + output_index * input_dim])
 
             permutation = arn.get_permutation()
             permuted_jacobian = jacobian.clone()
diff --git a/tests/distributions/test_one_hot_categorical.py b/tests/distributions/test_one_hot_categorical.py
index ca751a3209..b80ba659c8 100644
--- a/tests/distributions/test_one_hot_categorical.py
+++ b/tests/distributions/test_one_hot_categorical.py
@@ -102,4 +102,4 @@ def test_batch_log_dims(dim, ps):
     ps = modify_params_using_dims(ps, dim)
     support = dist.OneHotCategorical(ps).enumerate_support()
     log_prob = dist.OneHotCategorical(ps).log_prob(support)
-    assert_equal(log_prob.data.cpu().numpy(), expected_log_pdf)
+    assert_equal(log_prob.detach().cpu().numpy(), expected_log_pdf)
diff --git a/tests/distributions/test_rejector.py b/tests/distributions/test_rejector.py
index a6dedd43f5..2624891732 100644
--- a/tests/distributions/test_rejector.py
+++ b/tests/distributions/test_rejector.py
@@ -70,12 +70,12 @@ def test_exponential_elbo(rate, factor):
 
     grads = []
     for guide in [guide1, guide2]:
-        grads.append(compute_elbo_grad(model, guide, [rates])[0].data)
+        grads.append(compute_elbo_grad(model, guide, [rates])[0])
     expected, actual = grads
     assert_equal(actual.mean(), expected.mean(), prec=0.05, msg='bad grad for rate')
 
-    actual = compute_elbo_grad(model, guide2, [factors])[0].data
-    assert_equal(actual.mean(), 0.0, prec=0.05, msg='bad grad for factor')
+    actual = compute_elbo_grad(model, guide2, [factors])[0]
+    assert_equal(actual.mean().item(), 0.0, prec=0.05, msg='bad grad for factor')
 
 
 @pytest.mark.parametrize('alpha', [1.0, 2.0, 5.0])
@@ -149,8 +149,8 @@ def test_shape_augmented_beta(alpha, beta):
     z = dist.rsample()
     cost = z.sum()
     (cost + cost.detach() * dist.score_parts(z)[1]).backward()
-    mean_alpha_grad = alphas.grad.data.mean()
-    mean_beta_grad = betas.grad.data.mean()
+    mean_alpha_grad = alphas.grad.data.mean().item()
+    mean_beta_grad = betas.grad.data.mean().item()
     expected_alpha_grad = beta / (alpha + beta) ** 2
     expected_beta_grad = -alpha / (alpha + beta) ** 2
     assert_equal(mean_alpha_grad, expected_alpha_grad, prec=0.01, msg='bad grad for alpha')
diff --git a/tests/distributions/test_tensor_type.py b/tests/distributions/test_tensor_type.py
index 5a7e9d14fb..0a9f4e51b8 100644
--- a/tests/distributions/test_tensor_type.py
+++ b/tests/distributions/test_tensor_type.py
@@ -32,17 +32,17 @@ def beta():
 
 @pytest.fixture()
 def float_test_data(test_data):
-    return Variable(torch.FloatTensor(test_data.data.cpu().numpy()))
+    return Variable(torch.FloatTensor(test_data.detach().cpu().numpy()))
 
 
 @pytest.fixture()
 def float_alpha(alpha):
-    return Variable(torch.FloatTensor(alpha.data.cpu().numpy()))
+    return Variable(torch.FloatTensor(alpha.detach().cpu().numpy()))
 
 
 @pytest.fixture()
 def float_beta(beta):
-    return Variable(torch.FloatTensor(beta.data.cpu().numpy()))
+    return Variable(torch.FloatTensor(beta.detach().cpu().numpy()))
 
 
 def test_double_type(test_data, alpha, beta):
@@ -50,9 +50,9 @@ def test_double_type(test_data, alpha, beta):
     assert isinstance(log_px_torch, torch.DoubleTensor)
     log_px_val = log_px_torch.numpy()
     log_px_np = sp.beta.logpdf(
-        test_data.data.cpu().numpy(),
-        alpha.data.cpu().numpy(),
-        beta.data.cpu().numpy())
+        test_data.detach().cpu().numpy(),
+        alpha.detach().cpu().numpy(),
+        beta.detach().cpu().numpy())
     assert_equal(log_px_val, log_px_np, prec=1e-4)
 
 
@@ -61,9 +61,9 @@ def test_float_type(float_test_data, float_alpha, float_beta, test_data, alpha,
     assert isinstance(log_px_torch, torch.FloatTensor)
     log_px_val = log_px_torch.numpy()
     log_px_np = sp.beta.logpdf(
-        test_data.data.cpu().numpy(),
-        alpha.data.cpu().numpy(),
-        beta.data.cpu().numpy())
+        test_data.detach().cpu().numpy(),
+        alpha.detach().cpu().numpy(),
+        beta.detach().cpu().numpy())
     assert_equal(log_px_val, log_px_np, prec=1e-4)
 
 
diff --git a/tests/distributions/test_transformed_distribution.py b/tests/distributions/test_transformed_distribution.py
index d8f1f0373d..622bad3804 100644
--- a/tests/distributions/test_transformed_distribution.py
+++ b/tests/distributions/test_transformed_distribution.py
@@ -45,7 +45,7 @@ def make_lognormal(kwargs):
 
 
 def unwrap_variable(x):
-    return x.data.cpu().numpy()
+    return x.detach().cpu().numpy()
 
 
 def AffineExp(affine_b, affine_a):
@@ -72,7 +72,7 @@ def test_mean_and_var(lognormal):
     torch_std = torch.std(torch_samples, 0)
     analytic_mean = lognormal.pyro_dist(**dist_params).analytic_mean()
     analytic_std = lognormal.pyro_dist(**dist_params).analytic_var() ** 0.5
-    precision = analytic_mean.max().data[0] * 0.05
+    precision = analytic_mean.max().item() * 0.05
     assert_equal(torch_mean, analytic_mean, prec=precision)
     assert_equal(torch_std, analytic_std, prec=precision)
 
@@ -87,11 +87,11 @@ def test_log_pdf(lognormal):
     normal_dist = dist.Normal(mu_z, sigma_z)
     trans_dist = get_transformed_dist(normal_dist, mu_lognorm, sigma_lognorm)
     test_data = lognormal.get_test_data(0)
-    log_px_torch = trans_dist.log_prob(test_data).sum().data[0]
+    log_px_torch = trans_dist.log_prob(test_data).sum().item()
     log_px_np = sp.lognorm.logpdf(
-        test_data.data.cpu().numpy(),
-        sigma_lognorm.data.cpu().numpy(),
-        scale=np.exp(mu_lognorm.data.cpu().numpy())).sum()
+        test_data.detach().cpu().numpy(),
+        sigma_lognorm.detach().cpu().numpy(),
+        scale=np.exp(mu_lognorm)).sum()
     assert_equal(log_px_torch, log_px_np, prec=1e-4)
 
 
@@ -105,11 +105,11 @@ def test_log_prob(lognormal):
     normal_dist = dist.Normal(mu_z, sigma_z)
     trans_dist = get_transformed_dist(normal_dist, mu_lognorm, sigma_lognorm)
     test_data = lognormal.get_test_data(0)
-    log_px_torch = trans_dist.log_prob(test_data).data.cpu().numpy()
+    log_px_torch = trans_dist.log_prob(test_data).detach().cpu().numpy()
     log_px_np = sp.lognorm.logpdf(
-        test_data.data.cpu().numpy(),
-        sigma_lognorm.data.cpu().numpy(),
-        scale=np.exp(mu_lognorm.data.cpu().numpy()))
+        test_data.detach().cpu().numpy(),
+        sigma_lognorm.detach().cpu().numpy(),
+        scale=np.exp(mu_lognorm.detach().cpu().numpy()))
     assert_equal(log_px_torch, log_px_np, prec=1e-4)
 
 
diff --git a/tests/infer/mcmc/test_hmc.py b/tests/infer/mcmc/test_hmc.py
index 4c7ae29980..26d25a322a 100644
--- a/tests/infer/mcmc/test_hmc.py
+++ b/tests/infer/mcmc/test_hmc.py
@@ -148,14 +148,14 @@ def test_hmc_conjugate_gaussian(fixture,
         logger.info(latent_mu)
         logger.info('Posterior mean (expected) - {}'.format(param_name))
         logger.info(expected_mean)
-        assert_equal(rmse(latent_mu, expected_mean).data[0], 0.0, prec=mean_tol)
+        assert_equal(rmse(latent_mu, expected_mean).item(), 0.0, prec=mean_tol)
 
         # Actual vs expected posterior precisions for the latents
         logger.info('Posterior std (actual) - {}'.format(param_name))
         logger.info(latent_std)
         logger.info('Posterior std (expected) - {}'.format(param_name))
         logger.info(expected_std)
-        assert_equal(rmse(latent_std, expected_std).data[0], 0.0, prec=std_tol)
+        assert_equal(rmse(latent_std, expected_std).item(), 0.0, prec=std_tol)
 
 
 def test_logistic_regression():
@@ -176,4 +176,4 @@ def model(data):
     for trace, _ in mcmc_run._traces(data):
         posterior.append(trace.nodes['beta']['value'])
     posterior_mean = torch.mean(torch.stack(posterior), 0)
-    assert_equal(rmse(true_coefs, posterior_mean).data[0], 0.0, prec=0.05)
+    assert_equal(rmse(true_coefs, posterior_mean).item(), 0.0, prec=0.05)
diff --git a/tests/infer/mcmc/test_mcmc.py b/tests/infer/mcmc/test_mcmc.py
index 58ebc55d73..2c1f6718e8 100644
--- a/tests/infer/mcmc/test_mcmc.py
+++ b/tests/infer/mcmc/test_mcmc.py
@@ -54,7 +54,7 @@ def test_mcmc_interface():
     assert_equal(len(values), 800)
     samples = []
     for _ in range(600):
-        samples.append(values[dist.sample().data[0]])
+        samples.append(values[dist.sample().item()])
     sample_mean = torch.mean(torch.stack(samples), 0)
     sample_std = torch.std(torch.stack(samples), 0)
     assert_equal(sample_mean.data, torch.Tensor([0.0]), prec=0.08)
diff --git a/tests/infer/mcmc/test_nuts.py b/tests/infer/mcmc/test_nuts.py
index 6eaa585c9f..51a87cd417 100644
--- a/tests/infer/mcmc/test_nuts.py
+++ b/tests/infer/mcmc/test_nuts.py
@@ -62,14 +62,14 @@ def test_nuts_conjugate_gaussian(fixture,
         logger.info(latent_mu)
         logger.info('Posterior mean (expected) - {}'.format(param_name))
         logger.info(expected_mean)
-        assert_equal(rmse(latent_mu, expected_mean).data[0], 0.0, prec=mean_tol)
+        assert_equal(rmse(latent_mu, expected_mean).item(), 0.0, prec=mean_tol)
 
         # Actual vs expected posterior precisions for the latents
         logger.info('Posterior std (actual) - {}'.format(param_name))
         logger.info(latent_std)
         logger.info('Posterior std (expected) - {}'.format(param_name))
         logger.info(expected_std)
-        assert_equal(rmse(latent_std, expected_std).data[0], 0.0, prec=std_tol)
+        assert_equal(rmse(latent_std, expected_std).item(), 0.0, prec=std_tol)
 
 
 def test_logistic_regression():
@@ -90,4 +90,4 @@ def model(data):
     for trace, _ in mcmc_run._traces(data):
         posterior.append(trace.nodes['beta']['value'])
     posterior_mean = torch.mean(torch.stack(posterior), 0)
-    assert_equal(rmse(true_coefs, posterior_mean).data[0], 0.0, prec=0.05)
+    assert_equal(rmse(true_coefs, posterior_mean).item(), 0.0, prec=0.05)
diff --git a/tests/infer/test_elbo_mapdata.py b/tests/infer/test_elbo_mapdata.py
index 5c402d6e66..6f2f358cb1 100644
--- a/tests/infer/test_elbo_mapdata.py
+++ b/tests/infer/test_elbo_mapdata.py
@@ -110,7 +110,7 @@ def guide():
                 2.0))
 
         if k % 500 == 0:
-            logger.debug("errors - {}, {}".format(mu_error.data.cpu().numpy()[0], log_sig_error.data.cpu().numpy()[0]))
+            logger.debug("errors - {}, {}".format(mu_error, log_sig_error))
 
-    assert_equal(Variable(torch.zeros(1)), mu_error, prec=0.05)
-    assert_equal(Variable(torch.zeros(1)), log_sig_error, prec=0.06)
+    assert_equal(mu_error.item(), 0, prec=0.05)
+    assert_equal(log_sig_error.item(), 0, prec=0.06)
diff --git a/tests/infer/test_enum.py b/tests/infer/test_enum.py
index 4e4349519e..5e2c3b15c3 100644
--- a/tests/infer/test_enum.py
+++ b/tests/infer/test_enum.py
@@ -34,14 +34,14 @@ def model():
 
     traces = list(iter_discrete_traces(graph_type, 0, model))
 
-    p = pyro.param("p").data
-    ps = pyro.param("ps").data
+    p = pyro.param("p")
+    ps = pyro.param("ps")
     assert len(traces) == 2 * len(ps)
 
     for scale, trace in traces:
-        x = trace.nodes["x"]["value"].data.long().view(-1)[0]
-        y = trace.nodes["y"]["value"].data.long().view(-1)[0]
-        expected_scale = Variable(torch.Tensor([[1 - p[0], p[0]][x] * ps[y]]))
+        x = trace.nodes["x"]["value"].long()
+        y = trace.nodes["y"]["value"].long()
+        expected_scale = [1 - p, p][x] * ps[y]
         assert_equal(scale, expected_scale)
 
 
@@ -91,10 +91,10 @@ def guide():
     guide = config_enumerate(guide, default=enum_discrete)
     Elbo = TraceGraph_ELBO if trace_graph else Trace_ELBO
     elbo = Elbo(max_iarange_nesting=0)
-    loss = elbo.loss(model, guide)
-    assert isinstance(loss, float) and not math.isnan(loss), loss
+    loss = elbo.loss(model, guide).item()
+    assert not math.isnan(loss), loss
     loss = elbo.loss_and_grads(model, guide)
-    assert isinstance(loss, float) and not math.isnan(loss), loss
+    assert not math.isnan(loss), loss
 
 
 # A simple Gaussian mixture model, with no vectorization.
@@ -214,8 +214,8 @@ def guide():
     expected_grad = grad(kl_divergence(dist.Bernoulli(q), dist.Bernoulli(0.25)), [q])[0]
 
     assert_equal(actual_grad, expected_grad, prec=0.1, msg="".join([
-        "\nexpected = {}".format(expected_grad.data.cpu().numpy()),
-        "\n  actual = {}".format(actual_grad.data.cpu().numpy()),
+        "\nexpected = {}".format(expected_grad.detach().cpu().numpy()),
+        "\n  actual = {}".format(actual_grad.detach().cpu().numpy()),
     ]))
 
 
@@ -250,8 +250,8 @@ def guide():
     expected_grad = 2 * grad(kl_divergence(dist.Bernoulli(q), dist.Bernoulli(0.25)), [q])[0]
 
     assert_equal(actual_grad, expected_grad, prec=0.1, msg="".join([
-        "\nexpected = {}".format(expected_grad.data.cpu().numpy()),
-        "\n  actual = {}".format(actual_grad.data.cpu().numpy()),
+        "\nexpected = {}".format(expected_grad.detach().cpu().numpy()),
+        "\n  actual = {}".format(actual_grad.detach().cpu().numpy()),
     ]))
 
 
@@ -289,8 +289,8 @@ def guide():
     expected_grad = grad(kl, [p])[0]
 
     assert_equal(actual_grad, expected_grad, prec=prec, msg="".join([
-        "\nexpected = {}".format(expected_grad.data.cpu().numpy()),
-        "\n  actual = {}".format(actual_grad.data.cpu().numpy()),
+        "\nexpected = {}".format(expected_grad.detach().cpu().numpy()),
+        "\n  actual = {}".format(actual_grad.detach().cpu().numpy()),
     ]))
 
 
@@ -330,6 +330,6 @@ def guide():
 
     for actual_grad, expected_grad in zip(actual_grads, expected_grads):
         assert_equal(actual_grad, expected_grad, prec=0.001, msg="".join([
-            "\nexpected = {}".format(expected_grad.data.cpu().numpy()),
-            "\n  actual = {}".format(actual_grad.data.cpu().numpy()),
+            "\nexpected = {}".format(expected_grad.detach().cpu().numpy()),
+            "\n  actual = {}".format(actual_grad.detach().cpu().numpy()),
         ]))
diff --git a/tests/infer/test_gradient.py b/tests/infer/test_gradient.py
index 17ae5f8d3e..3dc757a647 100644
--- a/tests/infer/test_gradient.py
+++ b/tests/infer/test_gradient.py
@@ -49,7 +49,7 @@ def guide():
                     trace_graph=trace_graph, num_particles=num_particles)
     inference.loss_and_grads(model, guide)
     params = dict(pyro.get_param_store().named_parameters())
-    actual_grads = {name: param.grad.data.cpu().numpy() for name, param in params.items()}
+    actual_grads = {name: param.grad.detach().cpu().numpy() for name, param in params.items()}
 
     expected_grads = {'mu': np.array([0.5, -2.0]), 'sigma': np.array([2.0])}
     for name in sorted(params):
diff --git a/tests/infer/test_inference.py b/tests/infer/test_inference.py
index c65f08bc95..b8d2e620b2 100644
--- a/tests/infer/test_inference.py
+++ b/tests/infer/test_inference.py
@@ -21,11 +21,11 @@
 
 
 def param_mse(name, target):
-    return torch.sum(torch.pow(target - pyro.param(name), 2.0)).data.cpu().numpy()[0]
+    return torch.sum(torch.pow(target - pyro.param(name), 2.0)).item()
 
 
 def param_abs_error(name, target):
-    return torch.sum(torch.abs(target - pyro.param(name))).data.cpu().numpy()[0]
+    return torch.sum(torch.abs(target - pyro.param(name))).item()
 
 
 @pytest.mark.stage("integration", "integration_batch_1")
diff --git a/tests/infer/test_sampling.py b/tests/infer/test_sampling.py
index c0fb3b8794..cc3fa9ecb3 100644
--- a/tests/infer/test_sampling.py
+++ b/tests/infer/test_sampling.py
@@ -80,7 +80,7 @@ def test_complete(self):
 
         tr_latents = set()
         for tr, _ in posterior._traces():
-            tr_latents.add(tuple([tr.nodes[name]["value"].view(-1).data[0]
+            tr_latents.add(tuple([tr.nodes[name]["value"].view(-1).item()
                                   for name in tr.nodes.keys()
                                   if tr.nodes[name]["type"] == "sample" and
                                   not tr.nodes[name]["is_observed"]]))
@@ -94,7 +94,7 @@ def test_marginal(self):
 
         tr_rets = []
         for v in values:
-            tr_rets.append(v.view(-1).data[0])
+            tr_rets.append(v.view(-1).item())
 
         assert len(tr_rets) == 4
         for i in range(4):
@@ -110,8 +110,8 @@ def test_importance_guide(self):
         posterior_samples = [marginal() for i in range(1000)]
         posterior_mean = torch.mean(torch.cat(posterior_samples))
         posterior_stddev = torch.std(torch.cat(posterior_samples), 0)
-        assert_equal(0, torch.norm(posterior_mean - self.mu_mean).data[0], prec=0.01)
-        assert_equal(0, torch.norm(posterior_stddev - self.mu_stddev).data[0], prec=0.1)
+        assert_equal(0, torch.norm(posterior_mean - self.mu_mean).item(), prec=0.01)
+        assert_equal(0, torch.norm(posterior_stddev - self.mu_stddev).item(), prec=0.1)
 
     @pytest.mark.init(rng_seed=0)
     def test_importance_prior(self):
@@ -120,5 +120,5 @@ def test_importance_prior(self):
         posterior_samples = [marginal() for i in range(1000)]
         posterior_mean = torch.mean(torch.cat(posterior_samples))
         posterior_stddev = torch.std(torch.cat(posterior_samples), 0)
-        assert_equal(0, torch.norm(posterior_mean - self.mu_mean).data[0], prec=0.01)
-        assert_equal(0, torch.norm(posterior_stddev - self.mu_stddev).data[0], prec=0.1)
+        assert_equal(0, torch.norm(posterior_mean - self.mu_mean).item(), prec=0.01)
+        assert_equal(0, torch.norm(posterior_stddev - self.mu_stddev).item(), prec=0.1)
diff --git a/tests/integration_tests/test_conjugate_gaussian_models.py b/tests/integration_tests/test_conjugate_gaussian_models.py
index 340104e9ef..f5f3c69197 100644
--- a/tests/integration_tests/test_conjugate_gaussian_models.py
+++ b/tests/integration_tests/test_conjugate_gaussian_models.py
@@ -22,7 +22,7 @@
 
 
 def param_mse(name, target):
-    return torch.sum(torch.pow(target - pyro.param(name), 2.0)).data.cpu().numpy()[0]
+    return torch.sum(torch.pow(target - pyro.param(name), 2.0)).detach().cpu().item()
 
 
 @pytest.mark.stage("integration", "integration_batch_1")
@@ -107,7 +107,7 @@ def do_elbo_test(self, reparameterized, n_steps, lr, prec, difficulty=1.0):
                     (self.N, reparameterized, n_repa_nodes, self.N))
         if self.N < 0:
             def array_to_string(y):
-                return str(map(lambda x: "%.3f" % x.data.cpu().numpy()[0], y))
+                return str(map(lambda x: "%.3f" % x.detach().cpu().numpy()[0], y))
 
             logger.debug("lambdas: " + array_to_string(self.lambdas))
             logger.debug("target_mus: " + array_to_string(self.target_mus[1:]))
diff --git a/tests/integration_tests/test_tracegraph_elbo.py b/tests/integration_tests/test_tracegraph_elbo.py
index c6e0644552..35aa5a2c52 100644
--- a/tests/integration_tests/test_tracegraph_elbo.py
+++ b/tests/integration_tests/test_tracegraph_elbo.py
@@ -25,11 +25,11 @@
 
 
 def param_mse(name, target):
-    return torch.sum(torch.pow(target - pyro.param(name), 2.0)).data.cpu().numpy()[0]
+    return torch.sum(torch.pow(target - pyro.param(name), 2.0)).item()
 
 
 def param_abs_error(name, target):
-    return torch.sum(torch.abs(target - pyro.param(name))).data.cpu().numpy()[0]
+    return torch.sum(torch.abs(target - pyro.param(name))).item()
 
 
 class NormalNormalTests(TestCase):
@@ -602,7 +602,7 @@ def per_param_callable(module_name, param_name, tags):
                     mean_1_error = torch.sum(torch.pow(pyro.param("mean_1_%d" % k), 2.0))
                     mean_2_error = torch.sum(torch.pow(pyro.param("mean_2_%d" % k), 2.0))
                     superfluous_error = torch.max(torch.max(mean_0_error, mean_1_error), mean_2_error)
-                    superfluous_errors.append(superfluous_error.data.cpu().numpy()[0])
+                    superfluous_errors.append(superfluous_error.detach().cpu().numpy())
 
             if step % 500 == 0:
                 logger.debug("mu error, log(sigma) error:  %.4f, %.4f" % (mu_error, log_sig_error))
diff --git a/tests/nn/test_clipped_nn.py b/tests/nn/test_clipped_nn.py
index 29a3ce0d0e..3162856a49 100644
--- a/tests/nn/test_clipped_nn.py
+++ b/tests/nn/test_clipped_nn.py
@@ -26,7 +26,7 @@ def test_clipped_softmax(Tensor):
     logger.info("softmax_ps = {}".format(softmax_ps))
     assert (softmax_ps.data >= epsilon).all()
     assert (softmax_ps.data <= 1 - epsilon).all()
-    assert_equal(softmax_ps.data.sum(), 1.0)
+    assert_equal(softmax_ps.sum().item(), 1.0)
 
 
 @pytest.mark.parametrize('Tensor', [torch.FloatTensor, torch.DoubleTensor])
@@ -41,5 +41,5 @@ def test_clipped_sigmoid(Tensor):
     sigmoid_ps = clipped_sigmoid(ps)
     logger.info("epsilon = {}".format(epsilon))
     logger.info("sigmoid_ps = {}".format(sigmoid_ps))
-    assert (sigmoid_ps.data >= epsilon).all()
-    assert (sigmoid_ps.data <= 1 - epsilon).all()
+    assert (sigmoid_ps >= epsilon).all()
+    assert (sigmoid_ps <= 1 - epsilon).all()
diff --git a/tests/ops/test_integrator.py b/tests/ops/test_integrator.py
index a0bf0aa751..5d6555ee24 100644
--- a/tests/ops/test_integrator.py
+++ b/tests/ops/test_integrator.py
@@ -125,8 +125,8 @@ def test_energy_conservation(example):
                                args.num_steps)
     energy_initial = model.energy(args.q_i, args.p_i)
     energy_final = model.energy(q_f, p_f)
-    logger.info("initial energy: {}".format(energy_initial.data[0]))
-    logger.info("final energy: {}".format(energy_final.data[0]))
+    logger.info("initial energy: {}".format(energy_initial.item()))
+    logger.info("final energy: {}".format(energy_final.item()))
     assert_equal(energy_final, energy_initial)
 
 
diff --git a/tests/params/test_param.py b/tests/params/test_param.py
index 4cd679c4a7..f8592f041e 100644
--- a/tests/params/test_param.py
+++ b/tests/params/test_param.py
@@ -30,11 +30,11 @@ def test_save_and_load(self):
         cost.backward()
         params = list(self.linear_module.parameters()) + [myparam]
         optim = torch.optim.Adam(params, lr=.01)
-        myparam_copy_stale = copy(pyro.param("myparam").data.cpu().numpy())
+        myparam_copy_stale = copy(pyro.param("myparam").detach().cpu().numpy())
 
         optim.step()
 
-        myparam_copy = copy(pyro.param("myparam").data.cpu().numpy())
+        myparam_copy = copy(pyro.param("myparam").detach().cpu().numpy())
         param_store_params = copy(pyro.get_param_store()._params)
         param_store_param_to_name = copy(pyro.get_param_store()._param_to_name)
         assert len(list(param_store_params.keys())) == 5
@@ -47,10 +47,10 @@ def test_save_and_load(self):
         pyro.get_param_store().load('paramstore.unittest.out')
 
         def modules_are_equal():
-            weights_equal = np.sum(np.fabs(self.linear_module3.weight.data.cpu().numpy() -
-                                   self.linear_module.weight.data.cpu().numpy())) == 0.0
-            bias_equal = np.sum(np.fabs(self.linear_module3.bias.data.cpu().numpy() -
-                                self.linear_module.bias.data.cpu().numpy())) == 0.0
+            weights_equal = np.sum(np.fabs(self.linear_module3.weight.detach().cpu().numpy() -
+                                   self.linear_module.weight.detach().cpu().numpy())) == 0.0
+            bias_equal = np.sum(np.fabs(self.linear_module3.bias.detach().cpu().numpy() -
+                                self.linear_module.bias.detach().cpu().numpy())) == 0.0
             return (weights_equal and bias_equal)
 
         assert not modules_are_equal()
@@ -63,8 +63,8 @@ def modules_are_equal():
 
         myparam = pyro.param("myparam")
         store = pyro.get_param_store()
-        assert myparam_copy_stale != myparam.data.cpu().numpy()
-        assert myparam_copy == myparam.data.cpu().numpy()
+        assert myparam_copy_stale != myparam.detach().cpu().numpy()
+        assert myparam_copy == myparam.detach().cpu().numpy()
         assert sorted(param_store_params.keys()) == sorted(store._params.keys())
         assert sorted(param_store_param_to_name.values()) == sorted(store._param_to_name.values())
         assert sorted(store._params.keys()) == sorted(store._param_to_name.values())
diff --git a/tests/poutine/test_poutines.py b/tests/poutine/test_poutines.py
index acbad21021..3e7a5354dc 100644
--- a/tests/poutine/test_poutines.py
+++ b/tests/poutine/test_poutines.py
@@ -19,7 +19,7 @@
 
 
 def eq(x, y, prec=1e-10):
-    return (torch.norm(x - y).data[0] < prec)
+    return (torch.norm(x - y).item() < prec)
 
 
 # XXX name is a bit silly
@@ -249,7 +249,7 @@ def test_queue_enumerate(self):
 
         tr_latents = []
         for tr in trs:
-            tr_latents.append(tuple([int(tr.nodes[name]["value"].view(-1).data[0]) for name in tr
+            tr_latents.append(tuple([int(tr.nodes[name]["value"].view(-1).item()) for name in tr
                                      if tr.nodes[name]["type"] == "sample" and
                                      not tr.nodes[name]["is_observed"]]))
 
@@ -412,7 +412,7 @@ def test_queue_enumerate(self):
         assert len(trs) == 2
 
         values = [
-            {name: tr.nodes[name]['value'].view(-1).data[0] for name in tr.nodes.keys()
+            {name: tr.nodes[name]['value'].view(-1).item() for name in tr.nodes.keys()
              if tr.nodes[name]['type'] == 'sample'}
             for tr in trs
         ]
diff --git a/tests/poutine/test_properties.py b/tests/poutine/test_properties.py
index 09d8cd8378..de01216ab2 100644
--- a/tests/poutine/test_properties.py
+++ b/tests/poutine/test_properties.py
@@ -90,7 +90,7 @@ def normal_normal_model():
                 do={'data': {'bern_0': ng_ones(1)}})
 def bernoulli_normal_model():
     bern_0 = pyro.sample('bern_0', dist.Bernoulli(ng_zeros(1) * 1e-2))
-    mu = ng_ones(1) if bern_0.data[0] else -ng_ones(1)
+    mu = ng_ones(1) if bern_0.item() else -ng_ones(1)
     normal_0 = ng_ones(1)
     pyro.sample('normal_0', dist.Normal(mu, ng_ones(1) * 1e-2),
                 obs=normal_0)
diff --git a/tutorial/source/air.ipynb b/tutorial/source/air.ipynb
index a2317cbcf0..31cb8e017f 100644
--- a/tutorial/source/air.ipynb
+++ b/tutorial/source/air.ipynb
@@ -236,7 +236,7 @@
     "def geom(num_trials=0):\n",
     "    p = Variable(torch.Tensor([0.5]))\n",
     "    x = pyro.sample('x{}'.format(num_trials), dist.Bernoulli(p))\n",
-    "    if x.data[0] == 1:\n",
+    "    if x[0] == 1:\n",
     "        return num_trials\n",
     "    else:\n",
     "        return geom(num_trials + 1)\n",
@@ -264,7 +264,7 @@
     "def geom_prior(x, step=0):\n",
     "    p = Variable(torch.Tensor([0.5]))\n",
     "    i = pyro.sample('i{}'.format(step), dist.Bernoulli(p))\n",
-    "    if i.data[0] == 1:\n",
+    "    if i[0] == 1:\n",
     "        return x\n",
     "    else: \n",
     "        x = x + prior_step_sketch(step)\n",
diff --git a/tutorial/source/gmm.ipynb b/tutorial/source/gmm.ipynb
index b41814f69f..8e8af131da 100644
--- a/tutorial/source/gmm.ipynb
+++ b/tutorial/source/gmm.ipynb
@@ -236,9 +236,9 @@
     "p = softmax(pyro.param('unconstrained_p'))\n",
     "pyplot.figure(figsize=(8, 4)).set_facecolor('white')\n",
     "pyplot.plot(data.data.numpy(), p.data.numpy()[:,0], 'ro',\n",
-    "            label='component with mean {:0.2g}'.format(mu.data[0]))\n",
+    "            label='component with mean {:0.2g}'.format(mu[0]))\n",
     "pyplot.plot(data.data.numpy(), p.data.numpy()[:,1], 'bo',\n",
-    "            label='component with mean {:0.2g}'.format(mu.data[1]))\n",
+    "            label='component with mean {:0.2g}'.format(mu[1]))\n",
     "pyplot.title('Mixture assignment weights')\n",
     "pyplot.xlabel('data value')\n",
     "pyplot.ylabel('assignment weight')\n",
diff --git a/tutorial/source/intro_part_i.ipynb b/tutorial/source/intro_part_i.ipynb
index d49cec0143..9cc4650719 100644
--- a/tutorial/source/intro_part_i.ipynb
+++ b/tutorial/source/intro_part_i.ipynb
@@ -113,12 +113,12 @@
    "source": [
     "def weather():\n",
     "    cloudy = pyro.sample('cloudy', dist.Bernoulli(Variable(torch.Tensor([0.3]))))\n",
-    "    cloudy = 'cloudy' if cloudy.data[0] == 1.0 else 'sunny'\n",
+    "    cloudy = 'cloudy' if cloudy.item() == 1.0 else 'sunny'\n",
     "    mean_temp = {'cloudy': [55.0], 'sunny': [75.0]}[cloudy]\n",
     "    sigma_temp = {'cloudy': [10.0], 'sunny': [15.0]}[cloudy]\n",
     "    temp = pyro.sample('temp', dist.Normal(Variable(torch.Tensor(mean_temp)), \n",
     "                                           Variable(torch.Tensor(sigma_temp))))\n",
-    "    return cloudy, temp.data[0]\n",
+    "    return cloudy, temp.item()\n",
     "\n",
     "for _ in range(3):\n",
     "    print(weather())"
diff --git a/tutorial/source/intro_part_ii.ipynb b/tutorial/source/intro_part_ii.ipynb
index c89d3dbc54..b0826bffc9 100644
--- a/tutorial/source/intro_part_ii.ipynb
+++ b/tutorial/source/intro_part_ii.ipynb
@@ -119,7 +119,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.hist([marginal(guess).data[0] for _ in range(100)], range=(5.0, 12.0))\n",
+    "plt.hist([marginal(guess).item() for _ in range(100)], range=(5.0, 12.0))\n",
     "plt.title(\"P(measurement | guess)\")\n",
     "plt.xlabel(\"weight\")\n",
     "plt.ylabel(\"#\")"
@@ -258,7 +258,7 @@
     "\n",
     "# The marginal distribution concentrates around the data\n",
     "print(marginal(guess))\n",
-    "plt.hist([marginal(guess)[\"weight\"].data[0] for _ in range(100)], range=(5.0, 12.0))\n",
+    "plt.hist([marginal(guess)[\"weight\"].item() for _ in range(100)], range=(5.0, 12.0))\n",
     "plt.title(\"P(weight | measurement, guess)\")\n",
     "plt.xlabel(\"weight\")\n",
     "plt.ylabel(\"#\")"
@@ -320,7 +320,7 @@
     "                                  num_samples=20)\n",
     "\n",
     "marginal = pyro.infer.Marginal(posterior, sites=[\"weight\"])\n",
-    "plt.hist([marginal(measurement, guess)[\"weight\"].data[0] for _ in range(100)], range=(5.0, 12.0))\n",
+    "plt.hist([marginal(measurement, guess)[\"weight\"].item() for _ in range(100)], range=(5.0, 12.0))\n",
     "plt.title(\"P(weight | measurement, guess)\")\n",
     "plt.xlabel(\"weight\")\n",
     "plt.ylabel(\"#\")"
@@ -410,7 +410,7 @@
     "posterior = pyro.infer.Importance(conditioned_scale, scale_parametrized_guide, num_samples=10)\n",
     "marginal = pyro.infer.Marginal(posterior, sites=[\"weight\"])\n",
     "\n",
-    "plt.hist([marginal(guess)[\"weight\"].data[0] for _ in range(100)], range=(5.0, 12.0))\n",
+    "plt.hist([marginal(guess)[\"weight\"].item() for _ in range(100)], range=(5.0, 12.0))\n",
     "plt.title(\"P(weight | measurement, guess)\")\n",
     "plt.xlabel(\"weight\")\n",
     "plt.ylabel(\"#\")"
@@ -429,7 +429,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.hist([scale_parametrized_guide(guess).data[0] for _ in range(100)], range=(5.0, 12.0))\n",
+    "plt.hist([scale_parametrized_guide(guess).item() for _ in range(100)], range=(5.0, 12.0))\n",
     "plt.title(\"P(weight | measurement, guess)\")\n",
     "plt.xlabel(\"weight\")\n",
     "plt.ylabel(\"#\")"
diff --git a/tutorial/source/svi_part_ii.ipynb b/tutorial/source/svi_part_ii.ipynb
index e6ec19b753..264e1686da 100644
--- a/tutorial/source/svi_part_ii.ipynb
+++ b/tutorial/source/svi_part_ii.ipynb
@@ -89,7 +89,7 @@
     "\n",
     "```python\n",
     "data = Variable(torch.zeros(10, 1))\n",
-    "data.data[0:6, 0] = torch.ones(6)  # 6 heads and 4 tails\n",
+    "data[0:6, 0] = torch.ones(6)  # 6 heads and 4 tails\n",
     "```\n",
     "\n",
     "Then we have:\n",


Code review comments:
diff_hunk: @@ -149,8 +149,8 @@ def test_shape_augmented_beta(alpha, beta):
     z = dist.rsample()
     cost = z.sum()
     (cost + cost.detach() * dist.score_parts(z)[1]).backward()
-    mean_alpha_grad = alphas.grad.data.mean()
-    mean_beta_grad = betas.grad.data.mean()
+    mean_alpha_grad = alphas.grad.data.mean().item()
path: tests/distributions/test_rejector.py
text: @user1:
Why not simply `alphas.grad.mean().item()`?

@author:
Yup, in this case we can just use that. Will change here and other patterns that I see.