normal.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. # Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import math
  15. from collections.abc import Iterable
  16. import numpy as np
  17. import paddle
  18. from paddle.base.data_feeder import check_type, convert_dtype
  19. from paddle.base.framework import Variable
  20. from paddle.distribution import distribution
  21. from paddle.framework import in_dynamic_mode
  22. from paddle.tensor import random
  23. class Normal(distribution.Distribution):
  24. r"""The Normal distribution with location `loc` and `scale` parameters.
  25. Mathematical details
  26. The probability density function (pdf) is
  27. .. math::
  28. pdf(x; \mu, \sigma) = \frac{1}{Z}e^{\frac {-0.5 (x - \mu)^2} {\sigma^2} }
  29. .. math::
  30. Z = (2 \pi \sigma^2)^{0.5}
  31. In the above equation:
  32. * :math:`loc = \mu`: is the mean.
  33. * :math:`scale = \sigma`: is the std.
  34. * :math:`Z`: is the normalization constant.
  35. Args:
  36. loc(int|float|list|tuple|numpy.ndarray|Tensor): The mean of normal distribution.The data type is float32 and float64.
  37. scale(int|float|list|tuple|numpy.ndarray|Tensor): The std of normal distribution.The data type is float32 and float64.
  38. name(str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
  39. Examples:
  40. .. code-block:: python
  41. >>> import paddle
  42. >>> from paddle.distribution import Normal
  43. >>> # Define a single scalar Normal distribution.
  44. >>> dist = Normal(loc=0., scale=3.)
  45. >>> # Define a batch of two scalar valued Normals.
  46. >>> # The first has mean 1 and standard deviation 11, the second 2 and 22.
  47. >>> dist = Normal(loc=[1., 2.], scale=[11., 22.])
  48. >>> # Get 3 samples, returning a 3 x 2 tensor.
  49. >>> dist.sample([3])
  50. >>> # Define a batch of two scalar valued Normals.
  51. >>> # Both have mean 1, but different standard deviations.
  52. >>> dist = Normal(loc=1., scale=[11., 22.])
  53. >>> # Complete example
  54. >>> value_tensor = paddle.to_tensor([0.8], dtype="float32")
  55. >>> normal_a = Normal([0.], [1.])
  56. >>> normal_b = Normal([0.5], [2.])
  57. >>> sample = normal_a.sample([2])
  58. >>> # a random tensor created by normal distribution with shape: [2, 1]
  59. >>> entropy = normal_a.entropy()
  60. >>> print(entropy)
  61. Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
  62. [1.41893852])
  63. >>> lp = normal_a.log_prob(value_tensor)
  64. >>> print(lp)
  65. Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
  66. [-1.23893857])
  67. >>> p = normal_a.probs(value_tensor)
  68. >>> print(p)
  69. Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
  70. [0.28969154])
  71. >>> kl = normal_a.kl_divergence(normal_b)
  72. >>> print(kl)
  73. Tensor(shape=[1], dtype=float32, place=Place(cpu), stop_gradient=True,
  74. [0.34939718])
  75. """
  76. def __init__(self, loc, scale, name=None):
  77. if not in_dynamic_mode():
  78. check_type(
  79. loc,
  80. 'loc',
  81. (
  82. int,
  83. float,
  84. np.ndarray,
  85. Variable,
  86. paddle.pir.Value,
  87. list,
  88. tuple,
  89. ),
  90. 'Normal',
  91. )
  92. check_type(
  93. scale,
  94. 'scale',
  95. (
  96. int,
  97. float,
  98. np.ndarray,
  99. Variable,
  100. paddle.pir.Value,
  101. list,
  102. tuple,
  103. ),
  104. 'Normal',
  105. )
  106. self.all_arg_is_float = False
  107. self.name = name if name is not None else 'Normal'
  108. self.dtype = 'float32'
  109. if isinstance(loc, int):
  110. loc = float(loc)
  111. if isinstance(scale, int):
  112. scale = float(scale)
  113. if self._validate_args(loc, scale):
  114. self.loc = loc
  115. self.scale = scale
  116. self.dtype = convert_dtype(loc.dtype)
  117. else:
  118. if isinstance(loc, float) and isinstance(scale, float):
  119. self.all_arg_is_float = True
  120. if isinstance(loc, np.ndarray) and str(loc.dtype) in [
  121. 'float32',
  122. 'float64',
  123. ]:
  124. self.dtype = loc.dtype
  125. elif isinstance(scale, np.ndarray) and str(scale.dtype) in [
  126. 'float32',
  127. 'float64',
  128. ]:
  129. self.dtype = scale.dtype
  130. self.loc, self.scale = self._to_tensor(loc, scale)
  131. if self.dtype != convert_dtype(self.loc.dtype):
  132. self.loc = paddle.cast(self.loc, dtype=self.dtype)
  133. self.scale = paddle.cast(self.scale, dtype=self.dtype)
  134. super().__init__(self.loc.shape)
  135. @property
  136. def mean(self):
  137. """Mean of normal distribution.
  138. Returns:
  139. Tensor: mean value.
  140. """
  141. return self.loc
  142. @property
  143. def variance(self):
  144. """Variance of normal distribution.
  145. Returns:
  146. Tensor: variance value.
  147. """
  148. return self.scale.pow(2)
  149. def sample(self, shape=(), seed=0):
  150. """Generate samples of the specified shape.
  151. Args:
  152. shape (Sequence[int], optional): Shape of the generated samples.
  153. seed (int): Python integer number.
  154. Returns:
  155. Tensor, A tensor with prepended dimensions shape.The data type is float32.
  156. """
  157. if not isinstance(shape, Iterable):
  158. raise TypeError('sample shape must be Iterable object.')
  159. if not in_dynamic_mode():
  160. check_type(seed, 'seed', (int), 'sample')
  161. shape = list(shape)
  162. batch_shape = list((self.loc + self.scale).shape)
  163. name = self.name + '_sample'
  164. if -1 in batch_shape:
  165. output_shape = shape + batch_shape
  166. fill_shape = list(batch_shape + shape)
  167. fill_shape[0] = paddle.shape(self.loc + self.scale)[0].item()
  168. zero_tmp = paddle.full(fill_shape, 0.0, self.dtype)
  169. zero_tmp_reshape = paddle.reshape(zero_tmp, output_shape)
  170. zero_tmp_shape = paddle.shape(zero_tmp_reshape)
  171. normal_random_tmp = random.gaussian(
  172. zero_tmp_shape, mean=0.0, std=1.0, seed=seed, dtype=self.dtype
  173. )
  174. output = normal_random_tmp * (zero_tmp_reshape + self.scale)
  175. output = paddle.add(output, self.loc, name=name)
  176. return output
  177. else:
  178. output_shape = shape + batch_shape
  179. output = random.gaussian(
  180. output_shape, mean=0.0, std=1.0, seed=seed, dtype=self.dtype
  181. ) * (paddle.zeros(output_shape, dtype=self.dtype) + self.scale)
  182. output = paddle.add(output, self.loc, name=name)
  183. if self.all_arg_is_float:
  184. return paddle.reshape(output, shape, name=name)
  185. else:
  186. return output
  187. def rsample(self, shape=()):
  188. """Generate reparameterized samples of the specified shape.
  189. Args:
  190. shape (Sequence[int], optional): Shape of the generated samples.
  191. Returns:
  192. Tensor: A tensor with prepended dimensions shape.The data type is float32.
  193. """
  194. if not isinstance(shape, Iterable):
  195. raise TypeError('sample shape must be Iterable object.')
  196. shape = self._extend_shape(tuple(shape))
  197. eps = paddle.normal(shape=shape)
  198. return self.loc + eps * self.scale
  199. def entropy(self):
  200. r"""Shannon entropy in nats.
  201. The entropy is
  202. .. math::
  203. entropy(\sigma) = 0.5 \log (2 \pi e \sigma^2)
  204. In the above equation:
  205. * :math:`scale = \sigma`: is the std.
  206. Returns:
  207. Tensor, Shannon entropy of normal distribution.The data type is float32.
  208. """
  209. name = self.name + '_entropy'
  210. batch_shape = list((self.loc + self.scale).shape)
  211. if -1 in batch_shape:
  212. fill_shape = list(batch_shape)
  213. fill_shape[0] = paddle.shape(self.loc + self.scale)[0].item()
  214. fill_dtype = (self.loc + self.scale).dtype
  215. zero_tmp = paddle.full(fill_shape, 0.0, fill_dtype)
  216. else:
  217. zero_tmp = paddle.full(batch_shape, 0.0, self.dtype)
  218. return paddle.add(
  219. 0.5 + zero_tmp,
  220. 0.5 * math.log(2 * math.pi) + paddle.log(self.scale + zero_tmp),
  221. name=name,
  222. )
  223. def log_prob(self, value):
  224. """Log probability density/mass function.
  225. Args:
  226. value (Tensor): The input tensor.
  227. Returns:
  228. Tensor: log probability.The data type is same with :attr:`value` .
  229. """
  230. name = self.name + '_log_prob'
  231. value = self._check_values_dtype_in_probs(self.loc, value)
  232. var = self.scale * self.scale
  233. log_scale = paddle.log(self.scale)
  234. return paddle.subtract(
  235. -1.0 * ((value - self.loc) * (value - self.loc)) / (2.0 * var),
  236. log_scale + math.log(math.sqrt(2.0 * math.pi)),
  237. name=name,
  238. )
  239. def probs(self, value):
  240. """Probability density/mass function.
  241. Args:
  242. value (Tensor): The input tensor.
  243. Returns:
  244. Tensor, probability. The data type is same with :attr:`value` .
  245. """
  246. name = self.name + '_probs'
  247. value = self._check_values_dtype_in_probs(self.loc, value)
  248. var = self.scale * self.scale
  249. return paddle.divide(
  250. paddle.exp(
  251. -1.0 * ((value - self.loc) * (value - self.loc)) / (2.0 * var)
  252. ),
  253. (math.sqrt(2 * math.pi) * self.scale),
  254. name=name,
  255. )
  256. def kl_divergence(self, other):
  257. r"""The KL-divergence between two normal distributions.
  258. The probability density function (pdf) is
  259. .. math::
  260. KL\_divergence(\mu_0, \sigma_0; \mu_1, \sigma_1) = 0.5 (ratio^2 + (\frac{diff}{\sigma_1})^2 - 1 - 2 \ln {ratio})
  261. .. math::
  262. ratio = \frac{\sigma_0}{\sigma_1}
  263. .. math::
  264. diff = \mu_1 - \mu_0
  265. In the above equation:
  266. * :math:`loc = \mu_0`: is the mean of current Normal distribution.
  267. * :math:`scale = \sigma_0`: is the std of current Normal distribution.
  268. * :math:`loc = \mu_1`: is the mean of other Normal distribution.
  269. * :math:`scale = \sigma_1`: is the std of other Normal distribution.
  270. * :math:`ratio`: is the ratio of scales.
  271. * :math:`diff`: is the difference between means.
  272. Args:
  273. other (Normal): instance of Normal.
  274. Returns:
  275. Tensor, kl-divergence between two normal distributions.The data type is float32.
  276. """
  277. if not in_dynamic_mode():
  278. check_type(other, 'other', Normal, 'kl_divergence')
  279. name = self.name + '_kl_divergence'
  280. var_ratio = self.scale / other.scale
  281. var_ratio = var_ratio * var_ratio
  282. t1 = (self.loc - other.loc) / other.scale
  283. t1 = t1 * t1
  284. return paddle.add(
  285. 0.5 * var_ratio, 0.5 * (t1 - 1.0 - paddle.log(var_ratio)), name=name
  286. )