Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Hackathon 5th No.10】为 Paddle 新增 bernoulli_ / log_normal_ / log_normal API #58432

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion python/paddle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,11 @@
)
from .tensor.random import (
bernoulli,
bernoulli_,
binomial,
check_shape,
log_normal,
log_normal_,
multinomial,
normal,
normal_,
Expand Down Expand Up @@ -943,8 +946,11 @@
'hypot',
'hypot_',
'index_fill',
"index_fill_",
'index_fill_',
'diagonal_scatter',
'combinations',
'signbit',
'bernoulli_',
'log_normal',
'log_normal_',
]
6 changes: 6 additions & 0 deletions python/paddle/tensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,11 @@
vander,
)
from .random import ( # noqa: F401
bernoulli_,
binomial,
exponential_,
log_normal,
log_normal_,
multinomial,
normal,
normal_,
Expand Down Expand Up @@ -784,6 +787,9 @@
'masked_scatter_',
"combinations",
'signbit',
'bernoulli_',
'log_normal',
'log_normal_',
]

# this list used in math_op_patch.py for magic_method bind
Expand Down
232 changes: 231 additions & 1 deletion python/paddle/tensor/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -1463,7 +1463,7 @@ def exponential_(x, lam=1.0, name=None):
f(x) = \lambda e^{-\lambda x}

Args:
x(Tensor): Input tensor. The data type should be float32, float64.
x (Tensor): Input tensor. The data type should be float32, float64.
lam(float, optional): :math:`\lambda` parameter of Exponential Distribution. Default, 1.0.
name(str, optional): The default value is None. Normally there is no
need for user to set this property. For more information, please
Expand Down Expand Up @@ -1502,3 +1502,233 @@ def exponential_(x, lam=1.0, name=None):
attrs={"lambda": lam},
)
return x


@dygraph_only
def bernoulli_(x, p=0.5, name=None):
r"""
This inplace OP fill input Tensor ``x`` with random number from a Bernoulli Distribution with probability ``p``.

Args:
x (Tensor): Input tensor. The data type should be float32, float64.
p (float, optional): probability :math:`p` parameter of Bernoulli Distribution. Default: 0.5.
name(str, optional): The default value is None. Normally there is no
need for user to set this property. For more information, please
refer to :ref:`api_guide_Name`.
Returns:
- x (Tensor): Input Tensor ``x``.
Examples:
.. code-block:: python

>>> import paddle
>>> x = paddle.empty((3, 4)).uniform_(0, 1)
>>> x.bernoulli_()
>>> # doctest: +SKIP('random check')
>>> print(x)
Tensor(shape=[3, 4], dtype=float32, place=Place(cpu), stop_gradient=True,
[[1., 1., 1., 1.],
[1., 0., 0., 1.],
[0., 1., 1., 0.]])
>>> # doctest: -SKIP

"""
if not (0 <= p and p <= 1):
raise ValueError(f"bernoulli_ expects p to be in [0, 1], but got p={p}")

check_variable_and_dtype(x, "x", ["float32", "float64"], "exponential")

uniform_(x, min=0.0, max=1.0)
return x.set_value((x < p).astype(x.dtype))


def log_normal(mean=1.0, std=1.0, shape=None, dtype=None, name=None):
r"""
Returns a Tensor filled with random values sampled from a Log Normal
Distribution, with ``mean``, ``std``, ``shape`` and ``dtype``.
The Log Normal Distribution is defined as follows:

Equation:
.. math::

f(x) = \frac{1}{x\sigma\sqrt{2\pi}}e^{-\frac{(\ln{x}-\mu)^2}{2\sigma^2}}

Args:
mean (float|Tensor, optional): The mean of the output Tensor's normal distribution.
If ``mean`` is float, all elements of the output Tensor shared the same mean.
If ``mean`` is a Tensor(data type supports float32, float64), it has per-element means.
Default is 0.0
std (float|Tensor, optional): The standard deviation of the output Tensor's normal distribution.
If ``std`` is float, all elements of the output Tensor shared the same standard deviation.
If ``std`` is a Tensor(data type supports float32, float64), it has per-element standard deviations.
Defaule is 1.0
shape (tuple|list|Tensor): Shape of the Tensor to be created. The data type is ``int32`` or ``int64`` .
If ``shape`` is a list or tuple, each element of it should be integer or 0-D Tensor with shape [].
If ``shape`` is an Tensor, it should be an 1-D Tensor which represents a list. If ``mean`` or ``std``
is a Tensor, the shape of the output Tensor is the same as ``mean`` or ``std`` , attr ``shape`` is ignored.
Default is None
name (str, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
Returns:
- out (Tensor): A Tensor filled with random values sampled from a log normal distribution with ``mean`` and ``std`` .
Examples:
.. code-block:: python

:name: log_normal-example-1
>>> import paddle
>>> out1 = paddle.log_normal(shape=[2, 3])
>>> print(out1)
>>> # doctest: +SKIP("Random output")
Tensor(shape=[2, 3], dtype=float32, place=Place(cpu), stop_gradient=True,
[[-0.85107994, -0.85490644, -1.35941815],
[-0.55500370, 0.20964541, 2.24193954]])
>>> # doctest: -SKIP

:name: log_normal-example-2
>>> import paddle
>>> mean_tensor = paddle.to_tensor([1.0, 2.0, 3.0])
>>> out2 = paddle.log_normal(mean=mean_tensor)
>>> print(out2)
>>> # doctest: +SKIP("Random output")
Tensor(shape=[3], dtype=float32, place=Place(cpu), stop_gradient=True,
[1.05411839, 3.71514320, 3.42665267])
>>> # doctest: -SKIP

:name: log_normal-example-3
>>> import paddle
>>> mean_tensor = paddle.to_tensor([1.0, 2.0, 3.0])
>>> std_tensor = paddle.to_tensor([1.0, 2.0, 3.0])
>>> out3 = paddle.log_normal(mean=mean_tensor, std=std_tensor)
>>> print(out3)
>>> # doctest: +SKIP("Random output")
Tensor(shape=[3], dtype=float32, place=Place(cpu), stop_gradient=True,
[0.48646951, 0.00815189, 3.74022293])
>>> # doctest: -SKIP

"""
if not in_dynamic_or_pir_mode():
check_type(mean, 'mean', (list, tuple, Variable), 'log_normal')
check_type(std, 'std', (list, tuple, Variable), 'log_normal')
if isinstance(mean, Variable):
check_dtype(
mean.dtype,
'mean',
['float32', 'float64'],
'log_normal',
"If mean is a Tensor, it's data type only support float32, float64",
)
if isinstance(std, Variable):
check_dtype(
std.dtype,
'std',
['float16', 'float32', 'float64'],
'log_normal',
"If std is a Tensor, it's data type only support float32, float64",
)
if shape is not None:
check_shape(shape, 'log_normal')

def normalize_mean_std(mean, std):
n_mean = paddle.log(mean**2 / paddle.sqrt(mean**2 + std**2))
n_std = paddle.sqrt(paddle.log(1 + (std**2 / mean**2)))
return n_mean, n_std

if isinstance(mean, Variable):
check_dtype(
mean.dtype,
'mean',
['float16', 'float32', 'float64'],
'log_normal',
"If mean is a Tensor, it's data type only support float32, float64",
)
if isinstance(std, Variable):
check_dtype(
std.dtype,
'std',
['float16', 'float32', 'float64'],
'log_normal',
"If std is a Tensor, it's data type only support float32, float64",
)
if std.dtype != mean.dtype:
std = paddle.cast(std, mean.dtype)
mean_shape = paddle.shape(mean)
std = paddle.reshape(std, mean_shape)
else:
std = paddle.to_tensor(std)
n_mean, n_std = normalize_mean_std(mean, std)
distribution = gaussian(
shape=paddle.shape(mean),
mean=n_mean,
std=n_std,
dtype=dtype,
name=name,
)
elif isinstance(std, Variable):
mean = paddle.to_tensor(mean)
n_mean, n_std = normalize_mean_std(mean, std)
distribution = gaussian(
shape=paddle.shape(std),
mean=n_mean,
std=n_std,
dtype=dtype,
name=name,
)
else:
mean = paddle.to_tensor(mean)
std = paddle.to_tensor(std)
n_mean, n_std = normalize_mean_std(mean, std)
distribution = gaussian(
mean=n_mean, std=n_std, shape=shape, dtype=dtype, name=name
)

return paddle.exp(distribution)


@dygraph_only
def log_normal_(x, mean=0.0, std=1.0, name=None):
r"""
This inplace OP fill input Tensor ``x`` with random number from a Log Normal Distribution
with ``mean`` and ``std``. The Log Normal Distribution is defined as follows:

Equation:
.. math::

f(x) = \frac{1}{x\sigma\sqrt{2\pi}}e^{-\frac{(\ln{x}-\mu)^2}{2\sigma^2}}

Args:
x (Tensor): The input tensor to be filled with random values.
mean (float|Tensor, optional): The mean of the output Tensor's normal distribution.
If ``mean`` is float, all elements of the output Tensor shared the same mean.
If ``mean`` is a Tensor(data type supports float32, float64), it has per-element means.
Default is 0.0
std (float|Tensor, optional): The standard deviation of the output Tensor's normal distribution.
If ``std`` is float, all elements of the output Tensor shared the same standard deviation.
If ``std`` is a Tensor(data type supports float32, float64), it has per-element standard deviations.
Defaule is 1.0
name(str, optional): The default value is None. Normally there is no
need for user to set this property. For more information, please
refer to :ref:`api_guide_Name`.
Returns:
A Tensor filled with random values sampled from a normal distribution with ``mean`` and ``std`` .
Examples:
.. code-block:: python

>>> import paddle
>>> x = paddle.randn([3, 4])
>>> x.log_normal_()
>>> # doctest: +SKIP('random check')
>>> print(x)
Tensor(shape=[3, 4], dtype=float32, place=Place(cpu), stop_gradient=True,
[[ 0.06132207, 1.11349595, 0.41906244, -0.24858207],
[-1.85169315, -1.50370061, 1.73954511, 0.13331604],
[ 1.66359663, -0.55764782, -0.59911072, -0.57773495]])
>>> # doctest: -SKIP

"""
if not isinstance(mean, Variable) or not isinstance(mean, float):
mean = paddle.to_tensor(mean, dtype=paddle.float64)
if not isinstance(std, Variable) or not isinstance(std, float):
std = paddle.to_tensor(std, dtype=paddle.float64)

n_mean = paddle.log(mean**2 / paddle.sqrt(mean**2 + std**2))
n_std = paddle.sqrt(paddle.log(1 + (std**2 / mean**2)))

return normal_(x, mean=n_mean, std=n_std).exp_()
33 changes: 33 additions & 0 deletions test/legacy_test/test_inplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1680,5 +1680,38 @@ def test_backward_error(self):
loss.backward()


class TestDygraphInplaceBernoulli(TestDygraphInplace):
def init_data(self):
self.shape = (20, 40)
self.input_var_numpy = np.random.random(self.shape)
self.dtype = "float32"
self.mean = 0
self.std = 1
self.seed = 100
self.p = 0.5

def inplace_api_processing(self, var):
return paddle.bernoulli_(var, p=self.p)

def non_inplace_api_processing(self, var):
return paddle.bernoulli(paddle.zeros(self.shape) + self.p)


class TestDygraphInplaceLogNormal(TestDygraphInplace):
def init_data(self):
self.shape = (20, 40)
self.input_var_numpy = np.random.random(self.shape)
self.dtype = "float32"
self.mean = 0
self.std = 1
self.seed = 100

def inplace_api_processing(self, var):
return paddle.log_normal_(var, self.shape, self.mean, self.std)

def non_inplace_api_processing(self, var):
return paddle.log_normal(var, self.shape, self.mean, self.std)


if __name__ == '__main__':
unittest.main()
Loading