docs: def _gradient_penalty(self, real_samples, fake_samples, kwargs):
          """
          Compute the norm of the gradients for each sample in a batch, and
          penalize anything on either side of unit norm
          """
          import torch
          from torch.autograd import Variable, grad
  
          real_samples = real_samples.view(fake_samples.shape)
  
          subset_size = real_samples.shape[0]
  
          real_samples = real_samples[:subset_size]
          fake_samples = fake_samples[:subset_size]
  
          alpha = torch.rand(subset_size)
          if self.use_cuda:
              alpha = alpha.cuda()
          alpha = alpha.view((-1,) + ((1,) * (real_samples.dim() - 1)))
  
          interpolates = alpha * real_samples + ((1 - alpha) * fake_samples)
          interpolates = Variable(interpolates, requires_grad=True)
          if self.use_cuda:
              interpolates = interpolates.cuda()
  
          d_output = self.critic(interpolates, **kwargs)
  
          grad_ouputs = torch.ones(d_output.size())
          if self.use_cuda:
              grad_ouputs = grad_ouputs.cuda()
  
          gradients = grad(
              outputs=d_output,
              inputs=interpolates,
              grad_outputs=grad_ouputs,
              create_graph=True,
              retain_graph=True,
              only_inputs=True)[0]
          return ((gradients.norm(2, dim=1) - 1) ** 2).mean() * 10