forked from OSSInnovation/mindspore
!6600 Update the convention that random seed works.
Merge pull request !6600 from peixu_ren/custom_bijector
This commit is contained in:
commit
467ed2ccd0
|
@ -18,7 +18,7 @@ from .api import ms_function
|
||||||
from .dtype import *
|
from .dtype import *
|
||||||
from .parameter import Parameter, ParameterTuple
|
from .parameter import Parameter, ParameterTuple
|
||||||
from .tensor import MetaTensor, Tensor, RowTensor, SparseTensor
|
from .tensor import MetaTensor, Tensor, RowTensor, SparseTensor
|
||||||
from .seed import set_seed, get_seed
|
from .seed import set_seed, get_seed, _truncate_seed, _update_seeds, _get_op_seed
|
||||||
|
|
||||||
|
|
||||||
__all__ = dtype.__all__
|
__all__ = dtype.__all__
|
||||||
|
@ -27,5 +27,5 @@ __all__.extend([
|
||||||
'ms_function', # api
|
'ms_function', # api
|
||||||
'Parameter', 'ParameterTuple', # parameter
|
'Parameter', 'ParameterTuple', # parameter
|
||||||
"dtype",
|
"dtype",
|
||||||
"set_seed", "get_seed" # random seed
|
"set_seed", "get_seed", '_truncate_seed', '_update_seeds', '_get_op_seed' # random seed
|
||||||
])
|
])
|
||||||
|
|
|
@ -16,8 +16,13 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import mindspore.dataset as de
|
import mindspore.dataset as de
|
||||||
|
|
||||||
|
# constants
|
||||||
|
_MAXINT32 = 2**31 - 1
|
||||||
|
keyConstant = [3528531795, 2654435769, 3449720151, 3144134277]
|
||||||
|
|
||||||
# set global RNG seed
|
# set global RNG seed
|
||||||
_GLOBAL_SEED = None
|
_GLOBAL_SEED = None
|
||||||
|
_KERNEL_SEED = {}
|
||||||
|
|
||||||
def set_seed(seed):
|
def set_seed(seed):
|
||||||
"""
|
"""
|
||||||
|
@ -25,12 +30,12 @@ def set_seed(seed):
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
The global seed is used by numpy.random, mindspore.common.Initializer, mindspore.ops.composite.random_ops and
|
The global seed is used by numpy.random, mindspore.common.Initializer, mindspore.ops.composite.random_ops and
|
||||||
mindspore.nn.probability.distribution.
|
mindspore.nn.probability.distribution.
|
||||||
If global seed is not set, these packages will use their own default seed independently, numpy.random and
|
If global seed is not set, these packages will use their own default seed independently, numpy.random and
|
||||||
mindspore.common.Initializer will choose a random seed, mindspore.ops.composite.random_ops and
|
mindspore.common.Initializer will choose a random seed, mindspore.ops.composite.random_ops and
|
||||||
mindspore.nn.probability.distribution will use zero.
|
mindspore.nn.probability.distribution will use zero.
|
||||||
Seed set by numpy.random.seed() only used by numpy.random, while seed set by this API will also used by
|
Seed set by numpy.random.seed() only used by numpy.random, while seed set by this API will also used by
|
||||||
numpy.random, so just set all seed by this API is recommended.
|
numpy.random, so just set all seed by this API is recommended.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
seed (int): The seed to be set.
|
seed (int): The seed to be set.
|
||||||
|
@ -45,6 +50,7 @@ def set_seed(seed):
|
||||||
raise ValueError("The seed must be greater or equal to 0.")
|
raise ValueError("The seed must be greater or equal to 0.")
|
||||||
np.random.seed(seed)
|
np.random.seed(seed)
|
||||||
de.config.set_seed(seed)
|
de.config.set_seed(seed)
|
||||||
|
_reset_op_seed()
|
||||||
global _GLOBAL_SEED
|
global _GLOBAL_SEED
|
||||||
_GLOBAL_SEED = seed
|
_GLOBAL_SEED = seed
|
||||||
|
|
||||||
|
@ -54,3 +60,51 @@ def get_seed():
|
||||||
Get global random seed.
|
Get global random seed.
|
||||||
"""
|
"""
|
||||||
return _GLOBAL_SEED
|
return _GLOBAL_SEED
|
||||||
|
|
||||||
|
|
||||||
|
def _truncate_seed(seed):
|
||||||
|
"""
|
||||||
|
Truncate the seed with MAXINT32.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seed (int): The seed to be truncated.
|
||||||
|
"""
|
||||||
|
return seed % _MAXINT32 # Truncate to fit into 32-bit integer
|
||||||
|
|
||||||
|
|
||||||
|
def _update_seeds(op_seed, kernel_name):
|
||||||
|
"""
|
||||||
|
Update the seed every time when a random op is called.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seed (int): The op-seed to be updated.
|
||||||
|
kernel_name (string): The random op kernel.
|
||||||
|
"""
|
||||||
|
global _GLOBAL_SEED
|
||||||
|
global _KERNEL_SEED
|
||||||
|
if _GLOBAL_SEED is not None:
|
||||||
|
_GLOBAL_SEED += keyConstant[1] + keyConstant[3] * (2**8)
|
||||||
|
if op_seed is not None:
|
||||||
|
_KERNEL_SEED[(kernel_name, op_seed)] = _KERNEL_SEED[(kernel_name, op_seed)] + (keyConstant[0] ^ keyConstant[2])
|
||||||
|
|
||||||
|
|
||||||
|
def _get_op_seed(op_seed, kernel_name):
|
||||||
|
"""
|
||||||
|
Get op seed which is relating to the specific kernel.
|
||||||
|
If the seed does not exist, add it into the kernel's dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seed (int): The op-seed to be updated.
|
||||||
|
kernel_name (string): The random op kernel.
|
||||||
|
"""
|
||||||
|
if ((kernel_name, op_seed) not in _KERNEL_SEED) or (_KERNEL_SEED[(kernel_name, op_seed)] == -1):
|
||||||
|
_KERNEL_SEED[(kernel_name, op_seed)] = op_seed
|
||||||
|
_KERNEL_SEED[(kernel_name, op_seed)] = 0
|
||||||
|
return _KERNEL_SEED[(kernel_name, op_seed)]
|
||||||
|
|
||||||
|
def _reset_op_seed():
|
||||||
|
"""
|
||||||
|
Reset op seeds in the kernel's dictionary.
|
||||||
|
"""
|
||||||
|
for key in _KERNEL_SEED:
|
||||||
|
_KERNEL_SEED[key] = -1
|
||||||
|
|
|
@ -71,7 +71,7 @@ def check_int_positive(arg_name, arg_value, op_name):
|
||||||
|
|
||||||
|
|
||||||
@constexpr
|
@constexpr
|
||||||
def check_non_negative(arg_name, arg_value, op_name):
|
def check_int_non_negative(arg_name, arg_value, op_name):
|
||||||
"""Int type judgment."""
|
"""Int type judgment."""
|
||||||
if isinstance(arg_value, int):
|
if isinstance(arg_value, int):
|
||||||
if arg_value >= 0:
|
if arg_value >= 0:
|
||||||
|
|
|
@ -21,27 +21,45 @@ from ..primitive import constexpr
|
||||||
from .multitype_ops import _constexpr_utils as const_utils
|
from .multitype_ops import _constexpr_utils as const_utils
|
||||||
from ...common import dtype as mstype
|
from ...common import dtype as mstype
|
||||||
from ...common import get_seed as get_global_seed
|
from ...common import get_seed as get_global_seed
|
||||||
|
from ...common import _truncate_seed, _update_seeds, _get_op_seed
|
||||||
|
|
||||||
@constexpr
|
@constexpr
|
||||||
def get_seed():
|
def get_seed(op_seed, kernel_name):
|
||||||
"""
|
"""
|
||||||
Get the graph-level seed.
|
Get the graph-level seed.
|
||||||
Graph-level seed is used as a global variable, that can be used in different ops in case op-level seed is not set.
|
Graph-level seed is used as a global variable, that can be used in different ops in case op-level seed is not set.
|
||||||
If op-level seed is 0, use graph-level seed; if graph-level seed is also 0, the system would generate a
|
If op-level seed is 0, use graph-level seed; if graph-level seed is also 0, the system would generate a
|
||||||
random seed.
|
random seed.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
For each seed, either op-seed or graph-seed, a random sequence will be generated relating to this seed.
|
||||||
|
So, the state of the seed regarding to this op should be recorded.
|
||||||
|
A simple illustration should be:
|
||||||
|
If a random op is called twice within one program, the two results should be different:
|
||||||
|
print(C.uniform((1, 4), seed=1)) # generates 'A1'
|
||||||
|
print(C.uniform((1, 4), seed=1)) # generates 'A2'
|
||||||
|
If the same program runs again, it repeat the results:
|
||||||
|
print(C.uniform((1, 4), seed=1)) # generates 'A1'
|
||||||
|
print(C.uniform((1, 4), seed=1)) # generates 'A2'
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Interger. The current graph-level seed.
|
Interger. The current graph-level seed.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
>>> C.get_seed()
|
>>> C.get_seed(seed, 'normal')
|
||||||
"""
|
"""
|
||||||
global_seed = get_global_seed()
|
global_seed = get_global_seed()
|
||||||
if global_seed is None:
|
if global_seed is None:
|
||||||
return 0
|
global_seed = 0
|
||||||
return global_seed
|
if op_seed is None:
|
||||||
|
temp_seed = _get_op_seed(0, kernel_name)
|
||||||
|
else:
|
||||||
|
temp_seed = _get_op_seed(op_seed, kernel_name)
|
||||||
|
seeds = _truncate_seed(global_seed), _truncate_seed(temp_seed)
|
||||||
|
_update_seeds(op_seed, kernel_name)
|
||||||
|
return seeds
|
||||||
|
|
||||||
def normal(shape, mean, stddev, seed=0):
|
def normal(shape, mean, stddev, seed=None):
|
||||||
"""
|
"""
|
||||||
Generates random numbers according to the Normal (or Gaussian) random number distribution.
|
Generates random numbers according to the Normal (or Gaussian) random number distribution.
|
||||||
|
|
||||||
|
@ -52,7 +70,7 @@ def normal(shape, mean, stddev, seed=0):
|
||||||
stddev (Tensor): The deviation σ distribution parameter. It should be greater than 0.
|
stddev (Tensor): The deviation σ distribution parameter. It should be greater than 0.
|
||||||
with float32 data type.
|
with float32 data type.
|
||||||
seed (int): Seed is used as entropy source for the Random number engines to generate pseudo-random numbers.
|
seed (int): Seed is used as entropy source for the Random number engines to generate pseudo-random numbers.
|
||||||
must be non-negative. Default: 0.
|
must be non-negative. Default: None, which will be treated as 0.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tensor. The shape should be equal to the broadcasted shape between the input `shape` and shapes
|
Tensor. The shape should be equal to the broadcasted shape between the input `shape` and shapes
|
||||||
|
@ -69,15 +87,14 @@ def normal(shape, mean, stddev, seed=0):
|
||||||
stddev_dtype = F.dtype(stddev)
|
stddev_dtype = F.dtype(stddev)
|
||||||
const_utils.check_tensors_dtype_same(mean_dtype, mstype.float32, "normal")
|
const_utils.check_tensors_dtype_same(mean_dtype, mstype.float32, "normal")
|
||||||
const_utils.check_tensors_dtype_same(stddev_dtype, mstype.float32, "normal")
|
const_utils.check_tensors_dtype_same(stddev_dtype, mstype.float32, "normal")
|
||||||
const_utils.check_non_negative("seed", seed, "normal")
|
seed1, seed2 = get_seed(seed, "normal")
|
||||||
seed1 = get_seed()
|
const_utils.check_int_non_negative("seed", seed2, "normal")
|
||||||
seed2 = seed
|
|
||||||
stdnormal = P.StandardNormal(seed1, seed2)
|
stdnormal = P.StandardNormal(seed1, seed2)
|
||||||
random_normal = stdnormal(shape)
|
random_normal = stdnormal(shape)
|
||||||
value = random_normal * stddev + mean
|
value = random_normal * stddev + mean
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def laplace(shape, mean, lambda_param, seed=0):
|
def laplace(shape, mean, lambda_param, seed=None):
|
||||||
r"""
|
r"""
|
||||||
Generates random numbers according to the Laplace random number distribution.
|
Generates random numbers according to the Laplace random number distribution.
|
||||||
It is defined as:
|
It is defined as:
|
||||||
|
@ -92,7 +109,7 @@ def laplace(shape, mean, lambda_param, seed=0):
|
||||||
lambda_param (Tensor): The parameter used for controling the variance of this random distribution. The
|
lambda_param (Tensor): The parameter used for controling the variance of this random distribution. The
|
||||||
variance of Laplace distribution is equal to twice the square of lambda_param. With float32 data type.
|
variance of Laplace distribution is equal to twice the square of lambda_param. With float32 data type.
|
||||||
seed (int): Seed is used as entropy source for Random number engines generating pseudo-random numbers.
|
seed (int): Seed is used as entropy source for Random number engines generating pseudo-random numbers.
|
||||||
Default: 0.
|
Default: None, which will be treated as 0.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tensor. The shape should be the broadcasted shape of Input "shape" and shapes of mean and lambda_param.
|
Tensor. The shape should be the broadcasted shape of Input "shape" and shapes of mean and lambda_param.
|
||||||
|
@ -108,14 +125,14 @@ def laplace(shape, mean, lambda_param, seed=0):
|
||||||
lambda_param_dtype = F.dtype(lambda_param)
|
lambda_param_dtype = F.dtype(lambda_param)
|
||||||
const_utils.check_tensors_dtype_same(mean_dtype, mstype.float32, "laplace")
|
const_utils.check_tensors_dtype_same(mean_dtype, mstype.float32, "laplace")
|
||||||
const_utils.check_tensors_dtype_same(lambda_param_dtype, mstype.float32, "laplace")
|
const_utils.check_tensors_dtype_same(lambda_param_dtype, mstype.float32, "laplace")
|
||||||
seed1 = get_seed()
|
seed1, seed2 = get_seed(seed, "laplace")
|
||||||
seed2 = seed
|
const_utils.check_int_non_negative("seed", seed2, "laplace")
|
||||||
stdlaplace = P.StandardLaplace(seed1, seed2)
|
stdlaplace = P.StandardLaplace(seed1, seed2)
|
||||||
rnd = stdlaplace(shape)
|
rnd = stdlaplace(shape)
|
||||||
value = rnd * lambda_param + mean
|
value = rnd * lambda_param + mean
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def uniform(shape, minval, maxval, seed=0, dtype=mstype.float32):
|
def uniform(shape, minval, maxval, seed=None, dtype=mstype.float32):
|
||||||
"""
|
"""
|
||||||
Generates random numbers according to the Uniform random number distribution.
|
Generates random numbers according to the Uniform random number distribution.
|
||||||
|
|
||||||
|
@ -131,7 +148,7 @@ def uniform(shape, minval, maxval, seed=0, dtype=mstype.float32):
|
||||||
It defines the maximum possible generated value, with int32 or float32 data type.
|
It defines the maximum possible generated value, with int32 or float32 data type.
|
||||||
If dtype is int32, only one number is allowed.
|
If dtype is int32, only one number is allowed.
|
||||||
seed (int): Seed is used as entropy source for the random number engines to generate pseudo-random numbers,
|
seed (int): Seed is used as entropy source for the random number engines to generate pseudo-random numbers,
|
||||||
must be non-negative. Default: 0.
|
must be non-negative. Default: None, which will be treated as 0.
|
||||||
dtype (mindspore.dtype): type of the Uniform distribution. If it is int32, it generates numbers from discrete
|
dtype (mindspore.dtype): type of the Uniform distribution. If it is int32, it generates numbers from discrete
|
||||||
uniform distribution; if it is float32, it generates numbers from continuous uniform distribution. It only
|
uniform distribution; if it is float32, it generates numbers from continuous uniform distribution. It only
|
||||||
supports these two data types. Default: mstype.float32.
|
supports these two data types. Default: mstype.float32.
|
||||||
|
@ -159,9 +176,8 @@ def uniform(shape, minval, maxval, seed=0, dtype=mstype.float32):
|
||||||
const_utils.check_valid_type(dtype, [mstype.int32, mstype.float32], 'uniform')
|
const_utils.check_valid_type(dtype, [mstype.int32, mstype.float32], 'uniform')
|
||||||
const_utils.check_tensors_dtype_same(minval_dtype, dtype, "uniform")
|
const_utils.check_tensors_dtype_same(minval_dtype, dtype, "uniform")
|
||||||
const_utils.check_tensors_dtype_same(maxval_dtype, dtype, "uniform")
|
const_utils.check_tensors_dtype_same(maxval_dtype, dtype, "uniform")
|
||||||
const_utils.check_non_negative("seed", seed, "uniform")
|
seed1, seed2 = get_seed(seed, "uniform")
|
||||||
seed1 = get_seed()
|
const_utils.check_int_non_negative("seed", seed2, "uniform")
|
||||||
seed2 = seed
|
|
||||||
if const_utils.is_same_type(dtype, mstype.int32):
|
if const_utils.is_same_type(dtype, mstype.int32):
|
||||||
random_uniform = P.UniformInt(seed1, seed2)
|
random_uniform = P.UniformInt(seed1, seed2)
|
||||||
value = random_uniform(shape, minval, maxval)
|
value = random_uniform(shape, minval, maxval)
|
||||||
|
@ -171,7 +187,7 @@ def uniform(shape, minval, maxval, seed=0, dtype=mstype.float32):
|
||||||
value = random_uniform * (maxval - minval) + minval
|
value = random_uniform * (maxval - minval) + minval
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def gamma(shape, alpha, beta, seed=0):
|
def gamma(shape, alpha, beta, seed=None):
|
||||||
"""
|
"""
|
||||||
Generates random numbers according to the Gamma random number distribution.
|
Generates random numbers according to the Gamma random number distribution.
|
||||||
|
|
||||||
|
@ -180,7 +196,7 @@ def gamma(shape, alpha, beta, seed=0):
|
||||||
alpha (Tensor): The alpha α distribution parameter. It should be greater than 0 with float32 data type.
|
alpha (Tensor): The alpha α distribution parameter. It should be greater than 0 with float32 data type.
|
||||||
beta (Tensor): The beta β distribution parameter. It should be greater than 0 with float32 data type.
|
beta (Tensor): The beta β distribution parameter. It should be greater than 0 with float32 data type.
|
||||||
seed (int): Seed is used as entropy source for the random number engines to generate
|
seed (int): Seed is used as entropy source for the random number engines to generate
|
||||||
pseudo-random numbers, must be non-negative. Default: 0.
|
pseudo-random numbers, must be non-negative. Default: None, which will be treated as 0.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tensor. The shape should be equal to the broadcasted shape between the input "shape" and shapes
|
Tensor. The shape should be equal to the broadcasted shape between the input "shape" and shapes
|
||||||
|
@ -193,14 +209,13 @@ def gamma(shape, alpha, beta, seed=0):
|
||||||
>>> beta = Tensor(1.0, mstype.float32)
|
>>> beta = Tensor(1.0, mstype.float32)
|
||||||
>>> output = C.gamma(shape, alpha, beta, seed=5)
|
>>> output = C.gamma(shape, alpha, beta, seed=5)
|
||||||
"""
|
"""
|
||||||
const_utils.check_non_negative("seed", seed, "gamma")
|
seed1, seed2 = get_seed(seed, "gamma")
|
||||||
seed1 = get_seed()
|
const_utils.check_int_non_negative("seed", seed2, "gamma")
|
||||||
seed2 = seed
|
|
||||||
random_gamma = P.Gamma(seed1, seed2)
|
random_gamma = P.Gamma(seed1, seed2)
|
||||||
value = random_gamma(shape, alpha, beta)
|
value = random_gamma(shape, alpha, beta)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def poisson(shape, mean, seed=0):
|
def poisson(shape, mean, seed=None):
|
||||||
"""
|
"""
|
||||||
Generates random numbers according to the Poisson random number distribution.
|
Generates random numbers according to the Poisson random number distribution.
|
||||||
|
|
||||||
|
@ -208,7 +223,7 @@ def poisson(shape, mean, seed=0):
|
||||||
shape (tuple): The shape of random tensor to be generated.
|
shape (tuple): The shape of random tensor to be generated.
|
||||||
mean (Tensor): The mean μ distribution parameter. It should be greater than 0 with float32 data type.
|
mean (Tensor): The mean μ distribution parameter. It should be greater than 0 with float32 data type.
|
||||||
seed (int): Seed is used as entropy source for the random number engines to generate pseudo-random numbers
|
seed (int): Seed is used as entropy source for the random number engines to generate pseudo-random numbers
|
||||||
and must be non-negative. Default: 0.
|
and must be non-negative. Default: None, which will be treated as 0.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tensor. The shape should be equal to the broadcasted shape between the input "shape" and shapes of `mean`.
|
Tensor. The shape should be equal to the broadcasted shape between the input "shape" and shapes of `mean`.
|
||||||
|
@ -219,9 +234,8 @@ def poisson(shape, mean, seed=0):
|
||||||
>>> mean = Tensor(1.0, mstype.float32)
|
>>> mean = Tensor(1.0, mstype.float32)
|
||||||
>>> output = C.poisson(shape, mean, seed=5)
|
>>> output = C.poisson(shape, mean, seed=5)
|
||||||
"""
|
"""
|
||||||
const_utils.check_non_negative("seed", seed, "poisson")
|
seed1, seed2 = get_seed(seed, "poisson")
|
||||||
seed1 = get_seed()
|
const_utils.check_int_non_negative("seed", seed2, "poisson")
|
||||||
seed2 = seed
|
|
||||||
random_poisson = P.Poisson(seed1, seed2)
|
random_poisson = P.Poisson(seed1, seed2)
|
||||||
value = random_poisson(shape, mean)
|
value = random_poisson(shape, mean)
|
||||||
return value
|
return value
|
||||||
|
|
Loading…
Reference in New Issue