You use .data here:
def to_np(x):
    return x.data.cpu().numpy()

You can replace it with
def to_np(x):
    return x.detach().cpu().numpy()

Also note that in both case, if you go to numpy, the autograd won’t be able to compute gradients. So no intermediary results should be done using numpy arrays, only Tensor should be used.