distributions.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import numpy as np
  2. import torch
  3. class AbstractDistribution:
  4. def sample(self):
  5. raise NotImplementedError()
  6. def mode(self):
  7. raise NotImplementedError()
  8. class DiracDistribution(AbstractDistribution):
  9. def __init__(self, value):
  10. self.value = value
  11. def sample(self):
  12. return self.value
  13. def mode(self):
  14. return self.value
  15. class DiagonalGaussianDistribution(object):
  16. def __init__(self, parameters, deterministic=False):
  17. self.parameters = parameters
  18. self.mean, self.logvar = torch.chunk(parameters, 2, dim=1)
  19. self.logvar = torch.clamp(self.logvar, -30.0, 20.0)
  20. self.deterministic = deterministic
  21. self.std = torch.exp(0.5 * self.logvar)
  22. self.var = torch.exp(self.logvar)
  23. if self.deterministic:
  24. self.var = self.std = torch.zeros_like(self.mean).to(
  25. device=self.parameters.device
  26. )
  27. def sample(self):
  28. x = self.mean + self.std * torch.randn(self.mean.shape).to(
  29. device=self.parameters.device
  30. )
  31. return x
  32. def kl(self, other=None):
  33. if self.deterministic:
  34. return torch.Tensor([0.0])
  35. else:
  36. if other is None:
  37. return 0.5 * torch.sum(
  38. torch.pow(self.mean, 2) + self.var - 1.0 - self.logvar,
  39. dim=[1, 2, 3],
  40. )
  41. else:
  42. return 0.5 * torch.sum(
  43. torch.pow(self.mean - other.mean, 2) / other.var
  44. + self.var / other.var
  45. - 1.0
  46. - self.logvar
  47. + other.logvar,
  48. dim=[1, 2, 3],
  49. )
  50. def nll(self, sample, dims=[1, 2, 3]):
  51. if self.deterministic:
  52. return torch.Tensor([0.0])
  53. logtwopi = np.log(2.0 * np.pi)
  54. return 0.5 * torch.sum(
  55. logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var,
  56. dim=dims,
  57. )
  58. def mode(self):
  59. return self.mean
  60. def normal_kl(mean1, logvar1, mean2, logvar2):
  61. """
  62. source: https://github.com/openai/guided-diffusion/blob/27c20a8fab9cb472df5d6bdd6c8d11c8f430b924/guided_diffusion/losses.py#L12
  63. Compute the KL divergence between two gaussians.
  64. Shapes are automatically broadcasted, so batches can be compared to
  65. scalars, among other use cases.
  66. """
  67. tensor = None
  68. for obj in (mean1, logvar1, mean2, logvar2):
  69. if isinstance(obj, torch.Tensor):
  70. tensor = obj
  71. break
  72. assert tensor is not None, "at least one argument must be a Tensor"
  73. # Force variances to be Tensors. Broadcasting helps convert scalars to
  74. # Tensors, but it does not work for torch.exp().
  75. logvar1, logvar2 = [
  76. x if isinstance(x, torch.Tensor) else torch.tensor(x).to(tensor)
  77. for x in (logvar1, logvar2)
  78. ]
  79. return 0.5 * (
  80. -1.0
  81. + logvar2
  82. - logvar1
  83. + torch.exp(logvar1 - logvar2)
  84. + ((mean1 - mean2) ** 2) * torch.exp(-logvar2)
  85. )