added Cauchy distribution

This commit is contained in:
Xun Deng 2020-10-03 00:41:36 -04:00
parent e65c68a723
commit 1a68ccb40b
7 changed files with 871 additions and 5 deletions

View File

@ -175,6 +175,8 @@ class Bijector(Cell):
"""
Calculate batch_shape based on parameters.
"""
if 'param_dict' not in self.parameters.keys():
return None
param_dict = self.parameters['param_dict']
broadcast_shape_tensor = None
for value in param_dict.values():
@ -191,6 +193,8 @@ class Bijector(Cell):
"""
Check if the parameters used during initialization are scalars.
"""
if 'param_dict' not in self.parameters.keys():
return False
param_dict = self.parameters['param_dict']
for value in param_dict.values():
if value is None:

View File

@ -27,6 +27,7 @@ from .categorical import Categorical
from .log_normal import LogNormal
from .logistic import Logistic
from .gumbel import Gumbel
from .cauchy import Cauchy
__all__ = ['Distribution',
'TransformedDistribution',
@ -39,4 +40,5 @@ __all__ = ['Distribution',
'LogNormal',
'Logistic',
'Gumbel',
'Cauchy',
]

View File

@ -234,6 +234,11 @@ def raise_type_error(name, cur_type, required_type):
raise TypeError(
f"For {name} , the type should be or be subclass of {required_type}, but got {cur_type}")
@constexpr
def raise_not_defined(func_name, obj, *args, **kwargs):
raise ValueError(
f"{func_name} is undefined for {obj} distribution.")
@constexpr
def check_distribution_name(name, expected_name):

View File

@ -0,0 +1,345 @@
# Copyright 2020 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Cauchy Distribution"""
import numpy as np
from mindspore.ops import operations as P
from mindspore.ops import composite as C
from mindspore._checkparam import Validator
from mindspore.common import dtype as mstype
from .distribution import Distribution
from ._utils.utils import check_greater_zero, check_distribution_name, raise_not_defined
from ._utils.custom_ops import exp_generic, log_generic, log1p_generic
class Cauchy(Distribution):
"""
Cauchy distribution.
Args:
loc (int, float, list, numpy.ndarray, Tensor, Parameter): The location of the Cauchy distribution.
scale (int, float, list, numpy.ndarray, Tensor, Parameter): The scale of the Cauchy distribution.
seed (int): The seed used in sampling. The global seed is used if it is None. Default: None.
dtype (mindspore.dtype): The type of the event samples. Default: mstype.float32.
name (str): The name of the distribution. Default: 'Cauchy'.
Note:
`scale` must be greater than zero.
`dist_spec_args` are `loc` and `scale`.
`dtype` must be a float type because Cauchy distributions are continuous.
Cauchy distribution is not supported on GPU backend.
Examples:
>>> # To initialize a Cauchy distribution of loc 3.0 and scale 4.0.
>>> import mindspore.nn.probability.distribution as msd
>>> cauchy = msd.Cauchy(3.0, 4.0, dtype=mstype.float32)
>>>
>>> # The following creates two independent Cauchy distributions.
>>> cauchy = msd.Cauchy([3.0, 3.0], [4.0, 4.0], dtype=mstype.float32)
>>>
>>> # A Cauchy distribution can be initilize without arguments.
>>> # In this case, 'loc' and `scale` must be passed in through arguments.
>>> cauchy = msd.Cauchy(dtype=mstype.float32)
>>>
>>> # To use a Cauchy distribution in a network.
>>> class net(Cell):
>>> def __init__(self):
>>> super(net, self).__init__():
>>> self.cau1 = msd.Cauchy(0.0, 1.0, dtype=mstype.float32)
>>> self.cau2 = msd.Cauchy(dtype=mstype.float32)
>>>
>>> # The following calls are valid in construct.
>>> def construct(self, value, loc_b, scale_b, loc_a, scale_a):
>>>
>>> # Private interfaces of probability functions corresponding to public interfaces, including
>>> # `prob`, `log_prob`, `cdf`, `log_cdf`, `survival_function`, and `log_survival`, have the same arguments as follows.
>>> # Args:
>>> # value (Tensor): the value to be evaluated.
>>> # loc (Tensor): the location of the distribution. Default: self.loc.
>>> # scale (Tensor): the scale of the distribution. Default: self.scale.
>>>
>>> # Examples of `prob`.
>>> # Similar calls can be made to other probability functions
>>> # by replacing 'prob' by the name of the function
>>> ans = self.cau1.prob(value)
>>> # Evaluate with respect to distribution b.
>>> ans = self.cau1.prob(value, loc_b, scale_b)
>>> # `loc` and `scale` must be passed in during function calls
>>> ans = self.cau2.prob(value, loc_a, scale_a)
>>>
>>> # Functions `mode` and `entropy` have the same arguments.
>>> # Args:
>>> # loc (Tensor): the location of the distribution. Default: self.loc.
>>> # scale (Tensor): the scale of the distribution. Default: self.scale.
>>>
>>> # Example of `mode`.
>>> ans = self.cau1.mode() # return 0.0
>>> ans = self.cau1.mode(loc_b, scale_b) # return loc_b
>>> # `loc` and `scale` must be passed in during function calls.
>>> ans = self.cau2.mode(loc_a, scale_a)
>>>
>>> # Interfaces of 'kl_loss' and 'cross_entropy' are the same:
>>> # Args:
>>> # dist (str): the type of the distributions. Only "Cauchy" is supported.
>>> # loc_b (Tensor): the loc of distribution b.
>>> # scale_b (Tensor): the scale distribution b.
>>> # loc (Tensor): the loc of distribution a. Default: self.loc.
>>> # scale (Tensor): the scale distribution a. Default: self.scale.
>>>
>>> # Examples of `kl_loss`. `cross_entropy` is similar.
>>> ans = self.cau1.kl_loss('Cauchy', loc_b, scale_b)
>>> ans = self.cau1.kl_loss('Cauchy', loc_b, scale_b, loc_a, scale_a)
>>> # Additional `loc` and `scale` must be passed in.
>>> ans = self.cau2.kl_loss('Cauchy', loc_b, scale_b, loc_a, scale_a)
>>>
>>> # Examples of `sample`.
>>> # Args:
>>> # shape (tuple): the shape of the sample. Default: ()
>>> # loc (Tensor): the location of the distribution. Default: self.loc.
>>> # scale (Tensor): the scale of the distribution. Default: self.scale.
>>> ans = self.cau1.sample()
>>> ans = self.cau1.sample((2,3))
>>> ans = self.cau1.sample((2,3), loc_b, s_b)
>>> ans = self.cau2.sample((2,3), loc_a, s_a)
"""
def __init__(self,
loc=None,
scale=None,
seed=None,
dtype=mstype.float32,
name="Cauchy"):
"""
Constructor of Cauchy.
"""
param = dict(locals())
param['param_dict'] = {'loc': loc, 'scale': scale}
valid_dtype = mstype.float_type
Validator.check_type_name("dtype", dtype, valid_dtype, type(self).__name__)
super(Cauchy, self).__init__(seed, dtype, name, param)
self._loc = self._add_parameter(loc, 'loc')
self._scale = self._add_parameter(scale, 'scale')
if self._scale is not None:
check_greater_zero(self._scale, "scale")
# ops needed for the class
self.atan = P.Atan()
self.cast = P.Cast()
self.const = P.ScalarToArray()
self.dtypeop = P.DType()
self.exp = exp_generic
self.fill = P.Fill()
self.less = P.Less()
self.log = log_generic
self.log1p = log1p_generic
self.squeeze = P.Squeeze(0)
self.shape = P.Shape()
self.sq = P.Square()
self.sqrt = P.Sqrt()
self.tan = P.Tan()
self.uniform = C.uniform
def extend_repr(self):
if self.is_scalar_batch:
str_info = f'location = {self._loc}, scale = {self._scale}'
else:
str_info = f'batch_shape = {self._broadcast_shape}'
return str_info
@property
def loc(self):
"""
Return the location of the distribution.
"""
return self._loc
@property
def scale(self):
"""
Return the scale of the distribution.
"""
return self._scale
def _get_dist_type(self):
return "Cauchy"
def _get_dist_args(self, loc=None, scale=None):
if loc is not None:
self.checktensor(loc, 'loc')
else:
loc = self.loc
if scale is not None:
self.checktensor(scale, 'scale')
else:
scale = self.scale
return loc, scale
def _mode(self, loc=None, scale=None):
"""
The mode of the distribution.
"""
loc, scale = self._check_param_type(loc, scale)
return loc
def _mean(self, *args, **kwargs):
return raise_not_defined('mean', 'Cauchy', *args, **kwargs)
def _sd(self, *args, **kwargs):
return raise_not_defined('standard deviation', 'Cauchy', *args, **kwargs)
def _var(self, *args, **kwargs):
return raise_not_defined('variance', 'Cauchy', *args, **kwargs)
def _entropy(self, loc=None, scale=None):
r"""
Evaluate entropy.
.. math::
H(X) = \log(4 * \Pi * scale)
"""
loc, scale = self._check_param_type(loc, scale)
return self.log(4 * np.pi * scale)
def _log_prob(self, value, loc=None, scale=None):
r"""
Evaluate log probability.
Args:
value (Tensor): The value to be evaluated.
loc (Tensor): The location of the distribution. Default: self.loc.
scale (Tensor): The scale of the distribution. Default: self.scale.
.. math::
L(x) = \log(\frac{1}{\pi * scale} * \frac{scale^{2}}{(x - loc)^{2} + scale^{2}})
"""
value = self._check_value(value, 'value')
value = self.cast(value, self.dtype)
loc, scale = self._check_param_type(loc, scale)
z = (value - loc) / scale
log_unnormalized_prob = - self.log1p(self.sq(z))
log_normalization = self.log(np.pi * scale)
return log_unnormalized_prob - log_normalization
def _cdf(self, value, loc=None, scale=None):
r"""
Evaluate the cumulative distribution function on the given value.
Args:
value (Tensor): The value to be evaluated.
loc (Tensor): The location of the distribution. Default: self.loc.
scale (Tensor): The scale the distribution. Default: self.scale.
.. math::
cdf(x) = \frac{\arctan{(x - loc) / scale}}{\pi} + 0.5
"""
value = self._check_value(value, 'value')
value = self.cast(value, self.dtype)
loc, scale = self._check_param_type(loc, scale)
z = (value - loc) / scale
return self.atan(z) / np.pi + 0.5
def _log_cdf(self, value, loc=None, scale=None):
r"""
Evaluate the log cumulative distribution function on the given value.
Args:
value (Tensor): The value to be evaluated.
loc (Tensor): The location of the distribution. Default: self.loc.
scale (Tensor): The scale the distribution. Default: self.scale.
.. math::
log_cdf(x) = \log(\frac{\arctan(\frac{x-loc}{scale})}{\pi} + 0.5)
= \log {\arctan(\frac{x-loc}{scale}) + 0.5pi}{pi}
= \log1p \frac{2 * arctan(\frac{x-loc}{scale})}{pi} - \log2
"""
value = self._check_value(value, 'value')
value = self.cast(value, self.dtype)
loc, scale = self._check_param_type(loc, scale)
z = (value - loc) / scale
return self.log1p(2. * self.atan(z) / np.pi) - self.log(self.const(2.))
def _quantile(self, p, loc=None, scale=None):
loc, scale = self._check_param_type(loc, scale)
return loc + scale * self.tan(np.pi * (p - 0.5))
def _kl_loss(self, dist, loc_b, scale_b, loc=None, scale=None):
r"""
Evaluate Cauchy-Cauchy kl divergence, i.e. KL(a||b).
Args:
dist (str): The type of the distributions. Should be "Cauchy" in this case.
loc_b (Tensor): The loc of distribution b.
scale_b (Tensor): The scale of distribution b.
loc (Tensor): The loc of distribution a. Default: self.loc.
scale (Tensor): The scale of distribution a. Default: self.scale.
.. math::
KL(a||b) = \log(\frac{(scale_a + scale_b)^{2} + (loc_a - loc_b)^{2}}
{4 * scale_a * scale_b})
"""
check_distribution_name(dist, 'Cauchy')
loc, scale = self._check_param_type(loc, scale)
loc_b = self._check_value(loc_b, 'loc_b')
loc_b = self.cast(loc_b, self.parameter_type)
scale_b = self._check_value(scale_b, 'scale_b')
scale_b = self.cast(scale_b, self.parameter_type)
sum_square = self.sq(scale + scale_b)
square_diff = self.sq(loc - loc_b)
return self.log(sum_square + square_diff) - \
self.log(self.const(4.0)) - self.log(scale) - self.log(scale_b)
def _cross_entropy(self, dist, loc_b, scale_b, loc=None, scale=None):
r"""
Evaluate cross entropy between Cauchy distributions.
Args:
dist (str): The type of the distributions. Should be "Cauchy" in this case.
loc_b (Tensor): The loc of distribution b.
scale_b (Tensor): The scale of distribution b.
loc (Tensor): The loc of distribution a. Default: self.loc.
scale (Tensor): The scale of distribution a. Default: self.scale.
"""
check_distribution_name(dist, 'Cauchy')
return self._entropy(loc, scale) + self._kl_loss(dist, loc_b, scale_b, loc, scale)
def _sample(self, shape=(), loc=None, scale=None):
"""
Sampling.
Args:
shape (tuple): The shape of the sample. Default: ().
loc (Tensor): The location of the samples. Default: self.loc.
scale (Tensor): The scale of the samples. Default: self.scale.
Returns:
Tensor, with the shape being shape + batch_shape.
"""
shape = self.checktuple(shape, 'shape')
loc, scale = self._check_param_type(loc, scale)
batch_shape = self.shape(loc + scale)
origin_shape = shape + batch_shape
if origin_shape == ():
sample_shape = (1,)
else:
sample_shape = origin_shape
l_zero = self.const(0.0)
h_one = self.const(1.0)
sample_uniform = self.uniform(sample_shape, l_zero, h_one, self.seed)
sample = self._quantile(sample_uniform, loc, scale)
value = self.cast(sample, self.dtype)
if origin_shape == ():
value = self.squeeze(value)
return value

View File

@ -21,7 +21,7 @@ import mindspore.nn as nn
import mindspore.nn.probability.bijector as msb
import mindspore.nn.probability.distribution as msd
from .transformed_distribution import TransformedDistribution
from ._utils.utils import check_distribution_name, raise_not_implemented_util
from ._utils.utils import check_distribution_name
from ._utils.custom_ops import exp_generic, expm1_generic, log_generic
class Gumbel(TransformedDistribution):
@ -39,6 +39,7 @@ class Gumbel(TransformedDistribution):
`scale` must be greater than zero.
`dist_spec_args` are `loc` and `scale`.
`dtype` must be a float type because Gumbel distributions are continuous.
`kl_loss` and `cross_entropy` are not supported on GPU backend.
Examples:
>>> # To initialize a Gumbel distribution of `loc` 3.0 and `scale` 4.0.
@ -219,8 +220,6 @@ class Gumbel(TransformedDistribution):
loc_b (Tensor): The loc of distribution b.
scale_b (Tensor): The scale of distribution b.
"""
if self.device_target == 'GPU':
raise_not_implemented_util('On GPU backend, cross_entropy', self.name)
check_distribution_name(dist, 'Gumbel')
return self._entropy() + self._kl_loss(dist, loc_b, scale_b)
@ -237,8 +236,6 @@ class Gumbel(TransformedDistribution):
KL(a||b) = \log(scale_b / scale_a) + Euler-Mascheroni_constant * (scale_a / scale_b - 1.) +
\exp(\frac{(loc_b - loc_a)}{scale_b}) * \Gamma(scale_a / scale_b + 1.) - 1.
"""
if self.device_target == 'GPU':
raise_not_implemented_util('On GPU backend, kl_loss', self.name)
check_distribution_name(dist, 'Gumbel')
loc_b = self._check_value(loc_b, 'loc_b')
scale_b = self._check_value(scale_b, 'scale_b')

View File

@ -0,0 +1,282 @@
# Copyright 2019 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""test cases for Cauchy distribution"""
import numpy as np
from scipy import stats
import mindspore.context as context
import mindspore.nn as nn
import mindspore.nn.probability.distribution as msd
from mindspore import Tensor
from mindspore import dtype
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend")
class Prob(nn.Cell):
"""
Test class: probability of Cauchy distribution.
"""
def __init__(self):
super(Prob, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([[2.0], [4.0]]), dtype=dtype.float32)
def construct(self, x_):
return self.c.prob(x_)
def test_pdf():
"""
Test pdf.
"""
cauchy_benchmark = stats.cauchy(np.array([3.0]), np.array([[2.0], [4.0]]))
expect_pdf = cauchy_benchmark.pdf([1.0, 2.0]).astype(np.float32)
pdf = Prob()
output = pdf(Tensor([1.0, 2.0], dtype=dtype.float32))
tol = 1e-6
assert (np.abs(output.asnumpy() - expect_pdf) < tol).all()
class LogProb(nn.Cell):
"""
Test class: log probability of Cauchy distribution.
"""
def __init__(self):
super(LogProb, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([[2.0], [4.0]]), dtype=dtype.float32)
def construct(self, x_):
return self.c.log_prob(x_)
def test_log_likelihood():
"""
Test log_pdf.
"""
cauchy_benchmark = stats.cauchy(np.array([3.0]), np.array([[2.0], [4.0]]))
expect_logpdf = cauchy_benchmark.logpdf([1.0, 2.0]).astype(np.float32)
logprob = LogProb()
output = logprob(Tensor([1.0, 2.0], dtype=dtype.float32))
tol = 1e-6
assert (np.abs(output.asnumpy() - expect_logpdf) < tol).all()
class KL(nn.Cell):
"""
Test class: kl_loss of Cauchy distribution.
"""
def __init__(self):
super(KL, self).__init__()
self.c = msd.Cauchy(np.array([3.]), np.array([4.]), dtype=dtype.float32)
def construct(self, mu, s):
return self.c.kl_loss('Cauchy', mu, s)
def test_kl_loss():
"""
Test kl_loss.
"""
loc_b = np.array([0.]).astype(np.float32)
scale_b = np.array([1.]).astype(np.float32)
loc_a = np.array([3.0]).astype(np.float32)
scale_a = np.array([4.0]).astype(np.float32)
sum_square = np.square(scale_a + scale_b)
square_diff = np.square(loc_a - loc_b)
expect_kl_loss = np.log(sum_square + square_diff) - \
np.log(4.0 * scale_a * scale_b)
kl_loss = KL()
loc = Tensor(loc_b, dtype=dtype.float32)
scale = Tensor(scale_b, dtype=dtype.float32)
output = kl_loss(loc, scale)
tol = 1e-6
assert (np.abs(output.asnumpy() - expect_kl_loss) < tol).all()
class Basics(nn.Cell):
"""
Test class: mode of Cauchy distribution.
"""
def __init__(self):
super(Basics, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([2.0, 4.0]), dtype=dtype.float32)
def construct(self):
return self.c.mode()
def test_basics():
"""
Test mode.
"""
basics = Basics()
mode = basics()
expect_mode = np.array([3.0, 3.0])
tol = 1e-6
assert (np.abs(mode.asnumpy() - expect_mode) < tol).all()
class Sampling(nn.Cell):
"""
Test class: sample of Cauchy distribution.
"""
def __init__(self, shape, seed=0):
super(Sampling, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([[2.0], [4.0]]), seed=seed, dtype=dtype.float32)
self.shape = shape
def construct(self, mean=None, sd=None):
return self.c.sample(self.shape, mean, sd)
def test_sample():
"""
Test sample.
"""
shape = (2, 3)
seed = 10
mean = Tensor([2.0], dtype=dtype.float32)
sd = Tensor([2.0, 2.0, 2.0], dtype=dtype.float32)
sample = Sampling(shape, seed=seed)
output = sample(mean, sd)
assert output.shape == (2, 3, 3)
class CDF(nn.Cell):
"""
Test class: cdf of Cauchy distribution.
"""
def __init__(self):
super(CDF, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([[2.0], [4.0]]), dtype=dtype.float32)
def construct(self, x_):
return self.c.cdf(x_)
def test_cdf():
"""
Test cdf.
"""
cauchy_benchmark = stats.cauchy(np.array([3.0]), np.array([[2.0], [4.0]]))
expect_cdf = cauchy_benchmark.cdf([1.0, 2.0]).astype(np.float32)
cdf = CDF()
output = cdf(Tensor([1.0, 2.0], dtype=dtype.float32))
tol = 2e-5
assert (np.abs(output.asnumpy() - expect_cdf) < tol).all()
class LogCDF(nn.Cell):
"""
Test class: log_cdf of Cauchy distribution.
"""
def __init__(self):
super(LogCDF, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([[2.0], [4.0]]), dtype=dtype.float32)
def construct(self, x_):
return self.c.log_cdf(x_)
def test_log_cdf():
"""
Test log cdf.
"""
cauchy_benchmark = stats.cauchy(np.array([3.0]), np.array([[2.0], [4.0]]))
expect_logcdf = cauchy_benchmark.logcdf([1.0, 2.0]).astype(np.float32)
logcdf = LogCDF()
output = logcdf(Tensor([1.0, 2.0], dtype=dtype.float32))
tol = 5e-5
assert (np.abs(output.asnumpy() - expect_logcdf) < tol).all()
class SF(nn.Cell):
"""
Test class: survival function of Cauchy distribution.
"""
def __init__(self):
super(SF, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([[2.0], [4.0]]), dtype=dtype.float32)
def construct(self, x_):
return self.c.survival_function(x_)
def test_survival():
"""
Test log_survival.
"""
cauchy_benchmark = stats.cauchy(np.array([3.0]), np.array([[2.0], [4.0]]))
expect_survival = cauchy_benchmark.sf([1.0, 2.0]).astype(np.float32)
survival_function = SF()
output = survival_function(Tensor([1.0, 2.0], dtype=dtype.float32))
tol = 2e-5
assert (np.abs(output.asnumpy() - expect_survival) < tol).all()
class LogSF(nn.Cell):
"""
Test class: log survival function of Cauchy distribution.
"""
def __init__(self):
super(LogSF, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([[2.0], [4.0]]), dtype=dtype.float32)
def construct(self, x_):
return self.c.log_survival(x_)
def test_log_survival():
"""
Test log_survival.
"""
cauchy_benchmark = stats.cauchy(np.array([3.0]), np.array([[2.0], [4.0]]))
expect_log_survival = cauchy_benchmark.logsf([1.0, 2.0]).astype(np.float32)
log_survival = LogSF()
output = log_survival(Tensor([1.0, 2.0], dtype=dtype.float32))
tol = 2e-5
assert (np.abs(output.asnumpy() - expect_log_survival) < tol).all()
class EntropyH(nn.Cell):
"""
Test class: entropy of Cauchy distribution.
"""
def __init__(self):
super(EntropyH, self).__init__()
self.c = msd.Cauchy(np.array([3.0]), np.array([[2.0], [4.0]]), dtype=dtype.float32)
def construct(self):
return self.c.entropy()
def test_entropy():
"""
Test entropy.
"""
expect_entropy = np.log(4 * np.pi * np.array([[2.0], [4.0]]))
entropy = EntropyH()
output = entropy()
tol = 1e-6
assert (np.abs(output.asnumpy() - expect_entropy) < tol).all()
class CrossEntropy(nn.Cell):
"""
Test class: cross entropy between Cauchy distributions.
"""
def __init__(self):
super(CrossEntropy, self).__init__()
self.c = msd.Cauchy(np.array([3.]), np.array([[2.0], [4.0]]), dtype=dtype.float32)
def construct(self, mu, s):
entropy = self.c.entropy()
kl_loss = self.c.kl_loss('Cauchy', mu, s)
h_sum_kl = entropy + kl_loss
cross_entropy = self.c.cross_entropy('Cauchy', mu, s)
return h_sum_kl - cross_entropy
def test_cross_entropy():
"""
Test cross_entropy.
"""
cross_entropy = CrossEntropy()
mean = Tensor([1.0], dtype=dtype.float32)
sd = Tensor([1.0], dtype=dtype.float32)
diff = cross_entropy(mean, sd)
tol = 1e-6
assert (np.abs(diff.asnumpy() - np.zeros(diff.shape)) < tol).all()

View File

@ -0,0 +1,231 @@
# Copyright 2020 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
Test nn.probability.distribution.cauchy.
"""
import pytest
import mindspore.nn as nn
import mindspore.nn.probability.distribution as msd
from mindspore import dtype
from mindspore import Tensor
def test_cauchy_shape_errpr():
"""
Invalid shapes.
"""
with pytest.raises(ValueError):
msd.Cauchy([[2.], [1.]], [[2.], [3.], [4.]], dtype=dtype.float32)
def test_type():
with pytest.raises(TypeError):
msd.Cauchy(0., 1., dtype=dtype.int32)
def test_name():
with pytest.raises(TypeError):
msd.Cauchy(0., 1., name=1.0)
def test_seed():
with pytest.raises(TypeError):
msd.Cauchy(0., 1., seed='seed')
def test_scale():
with pytest.raises(ValueError):
msd.Cauchy(0., 0.)
with pytest.raises(ValueError):
msd.Cauchy(0., -1.)
def test_arguments():
"""
args passing during initialization.
"""
l = msd.Cauchy()
assert isinstance(l, msd.Distribution)
l = msd.Cauchy([3.0], [4.0], dtype=dtype.float32)
assert isinstance(l, msd.Distribution)
class CauchyProb(nn.Cell):
"""
Cauchy distribution: initialize with loc/scale.
"""
def __init__(self):
super(CauchyProb, self).__init__()
self.cauchy = msd.Cauchy(3.0, 4.0, dtype=dtype.float32)
def construct(self, value):
prob = self.cauchy.prob(value)
log_prob = self.cauchy.log_prob(value)
cdf = self.cauchy.cdf(value)
log_cdf = self.cauchy.log_cdf(value)
sf = self.cauchy.survival_function(value)
log_sf = self.cauchy.log_survival(value)
return prob + log_prob + cdf + log_cdf + sf + log_sf
def test_cauchy_prob():
"""
Test probability functions: passing value through construct.
"""
net = CauchyProb()
value = Tensor([0.5, 1.0], dtype=dtype.float32)
ans = net(value)
assert isinstance(ans, Tensor)
class CauchyProb1(nn.Cell):
"""
Cauchy distribution: initialize without loc/scale.
"""
def __init__(self):
super(CauchyProb1, self).__init__()
self.cauchy = msd.Cauchy()
def construct(self, value, mu, s):
prob = self.cauchy.prob(value, mu, s)
log_prob = self.cauchy.log_prob(value, mu, s)
cdf = self.cauchy.cdf(value, mu, s)
log_cdf = self.cauchy.log_cdf(value, mu, s)
sf = self.cauchy.survival_function(value, mu, s)
log_sf = self.cauchy.log_survival(value, mu, s)
return prob + log_prob + cdf + log_cdf + sf + log_sf
def test_cauchy_prob1():
"""
Test probability functions: passing loc/scale, value through construct.
"""
net = CauchyProb1()
value = Tensor([0.5, 1.0], dtype=dtype.float32)
mu = Tensor([0.0], dtype=dtype.float32)
s = Tensor([1.0], dtype=dtype.float32)
ans = net(value, mu, s)
assert isinstance(ans, Tensor)
class KL(nn.Cell):
"""
Test kl_loss and cross entropy.
"""
def __init__(self):
super(KL, self).__init__()
self.cauchy = msd.Cauchy(3.0, 4.0)
self.cauchy1 = msd.Cauchy()
def construct(self, mu, s, mu_a, s_a):
kl = self.cauchy.kl_loss('Cauchy', mu, s)
kl1 = self.cauchy1.kl_loss('Cauchy', mu, s, mu_a, s_a)
cross_entropy = self.cauchy.cross_entropy('Cauchy', mu, s)
cross_entropy1 = self.cauchy.cross_entropy('Cauchy', mu, s, mu_a, s_a)
return kl + kl1 + cross_entropy + cross_entropy1
def test_kl_cross_entropy():
"""
Test kl_loss and cross_entropy.
"""
net = KL()
mu = Tensor([0.0], dtype=dtype.float32)
s = Tensor([1.0], dtype=dtype.float32)
mu_a = Tensor([0.0], dtype=dtype.float32)
s_a = Tensor([1.0], dtype=dtype.float32)
ans = net(mu, s, mu_a, s_a)
assert isinstance(ans, Tensor)
class CauchyBasics(nn.Cell):
"""
Test class: basic loc/scale function.
"""
def __init__(self):
super(CauchyBasics, self).__init__()
self.cauchy = msd.Cauchy(3.0, 4.0, dtype=dtype.float32)
def construct(self):
mode = self.cauchy.mode()
entropy = self.cauchy.entropy()
return mode + entropy
class CauchyMean(nn.Cell):
"""
Test class: basic loc/scale function.
"""
def __init__(self):
super(CauchyMean, self).__init__()
self.cauchy = msd.Cauchy(3.0, 4.0, dtype=dtype.float32)
def construct(self):
return self.cauchy.mean()
class CauchyVar(nn.Cell):
"""
Test class: basic loc/scale function.
"""
def __init__(self):
super(CauchyVar, self).__init__()
self.cauchy = msd.Cauchy(3.0, 4.0, dtype=dtype.float32)
def construct(self):
return self.cauchy.var()
class CauchySd(nn.Cell):
"""
Test class: basic loc/scale function.
"""
def __init__(self):
super(CauchySd, self).__init__()
self.cauchy = msd.Cauchy(3.0, 4.0, dtype=dtype.float32)
def construct(self):
return self.cauchy.sd()
def test_bascis():
"""
Test mean/sd/var/mode/entropy functionality of Cauchy.
"""
net = CauchyBasics()
ans = net()
assert isinstance(ans, Tensor)
with pytest.raises(ValueError):
net = CauchyMean()
ans = net()
with pytest.raises(ValueError):
net = CauchyVar()
ans = net()
with pytest.raises(ValueError):
net = CauchySd()
ans = net()
class CauchyConstruct(nn.Cell):
"""
Cauchy distribution: going through construct.
"""
def __init__(self):
super(CauchyConstruct, self).__init__()
self.cauchy = msd.Cauchy(3.0, 4.0)
self.cauchy1 = msd.Cauchy()
def construct(self, value, mu, s):
prob = self.cauchy('prob', value)
prob1 = self.cauchy('prob', value, mu, s)
prob2 = self.cauchy1('prob', value, mu, s)
return prob + prob1 + prob2
def test_cauchy_construct():
"""
Test probability function going through construct.
"""
net = CauchyConstruct()
value = Tensor([0.5, 1.0], dtype=dtype.float32)
mu = Tensor([0.0], dtype=dtype.float32)
s = Tensor([1.0], dtype=dtype.float32)
ans = net(value, mu, s)
assert isinstance(ans, Tensor)