I think you are running in the case described in the PyTorch 0.4 migration guide 82.
.detach() is safer in that it catches inplace modifications that will cause autograd to give wrong results when backpropagating through the graph that you have “detached from”.
Consider the following more minimal example:
a = torch.arange(5., requires_grad=True)
b = a**2
c = a.detach() # error with detach(), wrong result with .data
c.zero_()
b.sum().backward()
print(a.grad)


This errors, and rightfully so, because it detects that a has changed inplace and this will trip gradient calculation.
If you comment the c.zero_() or use .clone().detach(), you see that a.grad is 2*a just as it should be.
If you use .data, the connection will fully break and you’ll silently get the wrong result that a.grad is 0.

.data is intended to support some old-style updates (that could use  with torch.no_grad(): instead), e.g. in optimizers. Most likely, you should not use it in your own code unless you are exactly sure that it is the right thing to do.
Best regards
Thomas