# Copyright 2020-2021 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.
# ============================================================================
"""Provide random seed api."""
from __future__ import absolute_import
import numpy as np
from mindspore import _checkparam as Validator
# constants
DEFAULT_GRAPH_SEED = 87654321
_MAXINT32 = 2**31 - 1
keyConstant = [3528531795, 2654435769, 3449720151, 3144134277]
# set global RNG seed
_GLOBAL_SEED = None
_KERNEL_SEED = {}
def _reset_op_seed():
"""
Reset op seeds in the kernel's dictionary.
"""
for (kernel_name, op_seed) in _KERNEL_SEED:
_KERNEL_SEED[(kernel_name, op_seed)] = op_seed
[docs]def set_seed(seed):
"""
Set global seed.
Note:
- The global seed is used by numpy.random, mindspore.common.Initializer and
mindspore.nn.probability.distribution.
- 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.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
numpy.random, so just set all seed by this API is recommended.
- In semi_auto_parallel/auto_parallel mode, when using set_seed, weights with same shape and same sharding
strategy in the same device would be initialized to the same result, otherwise, they would be initialized to
the different result.
Args:
seed (int): The seed to be set.
Raises:
ValueError: If seed is invalid (< 0).
TypeError: If seed isn't an int.
Examples:
>>> import numpy as np
>>> import mindspore as ms
>>> from mindspore import Tensor, set_seed, Parameter, ops
>>> from mindspore.common.initializer import initializer
>>> # Note: (1) Please make sure the code is running in PYNATIVE MODE;
>>> # (2) Because Composite-level ops need parameters to be Tensors, for below examples,
>>> # when using ops.uniform operator, minval and maxval are initialised as:
>>> minval = Tensor(1.0, ms.float32)
>>> maxval = Tensor(2.0, ms.float32)
>>>
>>> # 1. If global seed is not set, numpy.random and initializer will choose a random seed:
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A1
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A2
>>> w1 = Parameter(initializer("uniform", [2, 2], ms.float32), name="w1") # W1
>>> w1 = Parameter(initializer("uniform", [2, 2], ms.float32), name="w1") # W2
>>> # Rerun the program will get different results:
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A3
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A4
>>> w1 = Parameter(initializer("uniform", [2, 2], ms.float32), name="w1") # W3
>>> w1 = Parameter(initializer("uniform", [2, 2], ms.float32), name="w1") # W4
>>>
>>> # 2. If global seed is set, numpy.random and initializer will use it:
>>> set_seed(1234)
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A1
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A2
>>> w1 = Parameter(initializer("uniform", [2, 2], ms.float32), name="w1") # W1
>>> w1 = Parameter(initializer("uniform", [2, 2], ms.float32), name="w1") # W2
>>> # Rerun the program will get the same results:
>>> set_seed(1234)
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A1
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A2
>>> w1 = Parameter(initializer("uniform", [2, 2], ms.float32), name="w1") # W1
>>> w1 = Parameter(initializer("uniform", [2, 2], ms.float32), name="w1") # W2
>>>
>>> # 3. If neither global seed nor op seed is set, mindspore.ops.function.random_func and
>>> # mindspore.nn.probability.distribution will choose a random seed:
>>> c1 = ops.uniform((1, 4), minval, maxval) # C1
>>> c2 = ops.uniform((1, 4), minval, maxval) # C2
>>> # Rerun the program will get different results:
>>> c1 = ops.uniform((1, 4), minval, maxval) # C3
>>> c2 = ops.uniform((1, 4), minval, maxval) # C4
>>>
>>> # 4. If global seed is set, but op seed is not set, mindspore.ops.function.random_func and
>>> # mindspore.nn.probability.distribution will calculate a seed according to global seed and
>>> # default op seed. Each call will change the default op seed, thus each call get different
>>> # results.
>>> set_seed(1234)
>>> c1 = ops.uniform((1, 4), minval, maxval) # C1
>>> c2 = ops.uniform((1, 4), minval, maxval) # C2
>>> # Rerun the program will get the same results:
>>> set_seed(1234)
>>> c1 = ops.uniform((1, 4), minval, maxval) # C1
>>> c2 = ops.uniform((1, 4), minval, maxval) # C2
>>>
>>> # 5. If both global seed and op seed are set, mindspore.ops.function.random_func and
>>> # mindspore.nn.probability.distribution will calculate a seed according to global seed and
>>> # op seed counter. Each call will change the op seed counter, thus each call get different
>>> # results.
>>> set_seed(1234)
>>> c1 = ops.uniform((1, 4), minval, maxval, seed=2) # C1
>>> c2 = ops.uniform((1, 4), minval, maxval, seed=2) # C2
>>> # Rerun the program will get the same results:
>>> set_seed(1234)
>>> c1 = ops.uniform((1, 4), minval, maxval, seed=2) # C1
>>> c2 = ops.uniform((1, 4), minval, maxval, seed=2) # C2
>>>
>>> # 6. If op seed is set but global seed is not set, 0 will be used as global seed. Then
>>> # mindspore.ops.function.random_func and mindspore.nn.probability.distribution act as in
>>> # condition 5.
>>> c1 = ops.uniform((1, 4), minval, maxval, seed=2) # C1
>>> c2 = ops.uniform((1, 4), minval, maxval, seed=2) # C2
>>> # Rerun the program will get the different results:
>>> c1 = ops.uniform((1, 4), minval, maxval, seed=2) # C1
>>> c2 = ops.uniform((1, 4), minval, maxval, seed=2) # C2
>>>
>>> # 7. Recall set_seed() in the program will reset numpy seed and op seed counter of
>>> # mindspore.ops.function.random_func and mindspore.nn.probability.distribution.
>>> set_seed(1234)
>>> np_1 = np.random.normal(0, 1, [1]).astype(np.float32) # A1
>>> c1 = ops.uniform((1, 4), minval, maxval, seed=2) # C1
>>> set_seed(1234)
>>> np_2 = np.random.normal(0, 1, [1]).astype(np.float32) # still get A1
>>> c2 = ops.uniform((1, 4), minval, maxval, seed=2) # still get C1
"""
if not isinstance(seed, int):
raise TypeError("The argument 'seed' must be type of int, but got {}.".format(type(seed)))
Validator.check_non_negative_int(seed, "seed", "global_seed")
import mindspore.dataset as de
np.random.seed(seed)
de.config.set_seed(seed)
_reset_op_seed()
global _GLOBAL_SEED
_GLOBAL_SEED = seed
[docs]def get_seed():
"""
Get global seed.
Returns:
Integer. The global seed.
Examples:
>>> import mindspore as ms
>>> ms.set_seed(1234)
>>> seed = ms.get_seed()
>>> print(seed)
1234
"""
return _GLOBAL_SEED
def _truncate_seed(seed):
"""
Truncate the seed with MAXINT32.
Args:
seed (int): The seed to be truncated.
Returns:
Integer. The seed with MAXINT32.
"""
return seed % _MAXINT32
def _update_seeds(op_seed, kernel_name):
"""
Update the seed every time when the op seed is called.
Args:
op_seed (int): The op seed to be updated.
kernel_name (string): The random op kernel.
"""
global _KERNEL_SEED
if op_seed is not None:
_KERNEL_SEED[(kernel_name, op_seed)] = _KERNEL_SEED.get((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:
op_seed (int): The op seed to be updated.
kernel_name (string): The random op kernel.
"""
if (kernel_name, op_seed) not in _KERNEL_SEED:
_KERNEL_SEED[(kernel_name, op_seed)] = op_seed
return _KERNEL_SEED[(kernel_name, op_seed)]
def _get_graph_seed(op_seed, kernel_name):
"""
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.
If op-level seed is 0, use graph-level seed; if graph-level seed is also 0, the system would generate a
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:
minval = Tensor(1.0, mstype.float32)
maxval = Tensor(2.0, mstype.float32)
print(C.uniform((1, 4), minval, maxval, seed=1)) # generates 'A1'
print(C.uniform((1, 4), minval, maxval, seed=1)) # generates 'A2'
If the same program runs again, it repeat the results:
print(C.uniform((1, 4), minval, maxval, seed=1)) # generates 'A1'
print(C.uniform((1, 4), minval, maxval, seed=1)) # generates 'A2'
Returns:
Integer. The current graph-level seed.
Examples:
>>> print(_get_graph_seed(0, 'normal'))
(0, 0)
"""
global_seed = get_seed()
if global_seed == 0:
global_seed = DEFAULT_GRAPH_SEED
elif global_seed is None:
global_seed = 0
if op_seed is None:
op_seed = 0
# neither global seed or op seed is set, return (0, 0) to let kernel choose random seed.
if global_seed == 0 and op_seed == 0:
seeds = 0, 0
else:
Validator.check_non_negative_int(op_seed, "seed", kernel_name)
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