1 Pytorch随机种子

最近在看一些开源的Pytorch项目时,几乎每一个项目都会设置随机数种子,比如下面这种

class RandomState(object):
    def __init__(self, seed):
        torch.set_num_threads(1)
        torch.backends.cudnn.deterministic = True
        torch.backends.cudnn.benchmark = False
        torch.manual_seed(seed)
        torch.cuda.manual_seed_all(seed)
        np.random.seed(seed)
        random.seed(seed)

    def save_rng_state(self):
        rng_dict = {}
        rng_dict["torch"] = torch.get_rng_state()
        rng_dict["cuda"] = torch.cuda.get_rng_state_all()
        rng_dict["numpy"] = np.random.get_state()
        rng_dict["random"] = random.getstate()
        return rng_dict

    def set_rng_state(self, rng_dict):
        torch.set_rng_state(rng_dict["torch"])
        torch.cuda.set_rng_state_all(rng_dict["cuda"])
        np.random.set_state(rng_dict["numpy"])
        random.setstate(rng_dict["random"])

其中,

torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
np.random.seed(seed)
random.seed(seed)

分别设置了pytorch、numpy和python的随机数种子。那么

1.1 随机数种子是什么?

随机数种子是针对随机方法而言的。

随机数一般可取任意数字。

随机方法常见的随机方法有生成随机数,以及其他像随机排序之类的,后者本质上也是基于生成随机数来实现的。

举个例子,如果我们简单调用 random.random() 来生成随机数,那么每一次生成的数都是随机的。但是,当我们预先使用 random.seed(x) 设定好随机种子,基于这个种子来执行随机算法,这个时候我们得到的随机数序列是相同的。实际上,所有的随机算法都是需要提供随机种子的,如果我们没有手动进行显式设置,系统则会根据时间来自己选择这个值,此时每次生成的随机数因时间差异而不同。

虽然计算机很擅长做精确计算,但是它们处理随机事件时非常不靠谱。随机数是一个难题。大多数随机数算法都努力创造一种呈均匀分布且难以预测的数据序列,但是在算法初始化阶段都需要提供随机数“种子”(random seed)。而完全相同的种子每次将产生相同的“随机”数序列,因此 用系统时间作为数序列生成的起点,会让程序运行的时候更具有随机性。

1.2 为什么Pytorch要设置随机数种子?

上面1.1节中说了, 如果随机数种子是相同的,那么每次都会产生相同的随机数序列。也就是说,在Pytorch设置一个固定的随机数种子是为了在每一次运行代码都希望得到完全相同的随机数序列,可以保证代码的可复现性,不会出现意想不到的结果。