随机种子¶
在强化学习中,不同的随机数作为种子会影响算法的结果。为了重现或公平比较其他算法的实验结果,我们需要使用相同的随机种子。
首先,在每个入口函数中,我们有一个全局的随机“种子”参数。例如,在 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)
对于收集器或评估器,如果只给出一个种子,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 还允许在每个环境中运行多个回合时启用 dynamic_seed。
随机种子的选择对环境至关重要。例如,在LunarLander环境中,环境的随机性由初始着陆时刻的随机性和着陆过程中的随机性共同决定。 对于初始时刻,着陆器从屏幕顶部中心开始,对其质心施加一个随机的初始力,以确保在不同种子下初始状态的水平速度、垂直速度、角度和角速度不同。同时,月球地形的分布将根据采样的随机数确定。 对于着陆过程,着陆器的动力学方程中有一个随机分散力(Stochastic dispersion),以确保环境传递函数在不同种子之间有所不同。
如链接 ding/envs/env/DI-engine_env_wrapper.py 所示,首先,DI-engine 在重置环境时设置环境种子,
如果 dynamic_seed 为 True,DI-engine 会在原始种子上添加一个随机整数,以使每个回合都不同。通过为这个随机生成器设置种子,可以保证可重复性。随机生成器通常是 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)
...
提示
当使用多个进程时,子进程的随机种子不会继承父进程的随机种子,而是保持系统的默认种子。 如这里所示, 我们通过在每个环境中重置相关种子来解决这个问题。
def seed(self, seed: int, dynamic_seed: bool = True) -> None:
...
np.random.seed(self._seed)