随机种子¶
在强化学习中,不同的随机数作为种子对算法的结果会有影响。为了复现或者公平比较其他算法的实验结果,我们需要使用相同的随机种子。
首先,在每个入口函数中,我们有一个全局的随机“种子”参数。例如在 ding/entry/serial_entry.py
中,
def serial_pipeline(..., seed: int = 0, ...):
...
在 ding/utils/default_helper.py
中, 我们定义了一个在入口函数中被调用的 set_pkg_seed
函数(如下图),以便于
为所有使用的相关程序包设置种子。
def set_pkg_seed(seed: int, use_cuda: bool = True) -> None:
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
if use_cuda and torch.cuda.is_available():
torch.cuda.manual_seed(seed)
对于 collector 或 evaluator的环境,如果只给出一个种子, DI-engine 将生成一个随机种子列表作为该组环境的种子。
def seed(self, seed: Union[List[int], int], dynamic_seed: bool = None) -> None:
"""
Overview:
Set the seed for each environment.
Arguments:
- seed (:obj:`Union[List[int], int]`): List of seeds for each environment, \
or one seed for the first environment and other seeds are generated automatically.
"""
if isinstance(seed, numbers.Integral):
seed = [seed + i for i in range(self.env_num)]
elif isinstance(seed, list):
assert len(seed) == self._env_num, "len(seed) {:d} != env_num {:d}".format(len(seed), self._env_num)
seed = seed
self._env_seed = seed
self._env_dynamic_seed = dynamic_seed
为了让环境更加多样化, DI-engine 也支持在每一个环境跑很多个episode的时候启用 dynamic_seed
。
如链接所示的那样 ding/envs/env/DI-engine_env_wrapper.py
, 首先,DI-engine 在重置环境时设置环境种子, 并且如果 dynamic_seed
是 True, DI-engine 会在原始种子中添加一个随机整数,以使每个
episode 不同。 并且可以通过设置这个随机生成器的种子来保证重现性。 这个随机生成器一般是 numpy.random
。
默认情况下,我们在收集数据时启用 dynamic_seed,在评估时禁用它。这样做的好处是可以让我们收集更多样化的训练数据,提高最终性能,但可能会降低收敛速度。 在评估时关闭 dynamic_seed 可以保证每次评估策略时,是在一组相同的随机种子上评估的,增加不同训练步数上模型评估结果的可比性。
def reset(self) -> None:
if hasattr(self, '_seed') and hasattr(self, '_dynamic_seed') and self._dynamic_seed:
np_seed = 100 * np.random.randint(1, 1000)
self._env.seed(self._seed + np_seed)
elif hasattr(self, '_seed'):
self._env.seed(self._seed)
...
Tip
当使用多个进程时,子进程的随机种子不会继承 父进程的随机种子,而是保持系统的默认种子。 如 这里 所示, 我们通过重置每个环境中的相关种子来解决这个问题。
def seed(self, seed: int, dynamic_seed: bool = True) -> None:
...
np.random.seed(self._seed)