快捷方式

可重现性

无法保证在 PyTorch 版本、单个提交或不同平台之间获得完全可重现的结果。此外,即使使用相同的种子,CPU 和 GPU 执行之间的结果也可能无法重现。

但是,您可以采取一些步骤来限制特定平台、设备和 PyTorch 版本中不确定性行为的来源数量。首先,您可以控制可能导致应用程序多次执行行为不同的随机性来源。其次,您可以配置 PyTorch 以避免对某些操作使用不确定性算法,以便对这些操作的多次调用(给定相同的输入)将产生相同的结果。

警告

确定性操作通常比不确定性操作慢,因此您的模型的单次运行性能可能会下降。但是,确定性可以通过促进实验、调试和回归测试来节省开发时间。

控制随机性来源

PyTorch 随机数生成器

您可以使用 torch.manual_seed() 为所有设备(包括 CPU 和 CUDA)播种 RNG

import torch
torch.manual_seed(0)

某些 PyTorch 操作可能在内部使用随机数。例如,torch.svd_lowrank() 就是这样做的。因此,使用相同的输入参数背靠背多次调用它可能会给出不同的结果。但是,只要在应用程序开始时将 torch.manual_seed() 设置为常量,并且所有其他不确定性来源都已消除,则每次在同一环境中运行应用程序时,都将生成相同的随机数序列。

也可以通过在后续调用之间将 torch.manual_seed() 设置为相同的值,从使用随机数的操作中获得相同的结果。

Python

对于自定义运算符,您可能还需要设置 python 种子

import random
random.seed(0)

其他库中的随机数生成器

如果您或您正在使用的任何库依赖 NumPy,您可以使用以下命令为全局 NumPy RNG 播种

import numpy as np
np.random.seed(0)

但是,某些应用程序和库可能使用 NumPy Random Generator 对象,而不是全局 RNG (https://numpy.com.cn/doc/stable/reference/random/generator.html),这些也需要一致地播种。

如果您正在使用任何其他使用随机数生成器的库,请参阅这些库的文档,了解如何为它们设置一致的种子。

CUDA 卷积基准测试

cuDNN 库(CUDA 卷积操作使用)可能是应用程序多次执行之间不确定性的来源。当使用一组新的尺寸参数调用 cuDNN 卷积时,一个可选功能可以运行多个卷积算法,对其进行基准测试以找到最快的算法。然后,在过程的其余部分中,对于相应的尺寸参数集,将一致地使用最快的算法。由于基准测试噪声和不同的硬件,即使在同一台机器上,基准测试也可能在后续运行中选择不同的算法。

禁用基准测试功能 torch.backends.cudnn.benchmark = False 会导致 cuDNN 确定性地选择一种算法,但可能会降低性能。

但是,如果您不需要应用程序多次执行之间的可重现性,那么如果启用基准测试功能 torch.backends.cudnn.benchmark = True,性能可能会提高。

请注意,此设置与下面讨论的 torch.backends.cudnn.deterministic 设置不同。

避免不确定性算法

torch.use_deterministic_algorithms() 允许您配置 PyTorch 以使用确定性算法代替不确定性算法(如果可用),并且如果已知某个操作是不确定的(并且没有确定性替代方案),则抛出错误。

请查看 torch.use_deterministic_algorithms() 的文档,了解受影响操作的完整列表。如果某个操作的行为与文档不符,或者您需要某个没有确定性实现的操作的确定性实现,请提交问题:https://github.com/pytorch/pytorch/issues?q=label:%22module:%20determinism%22

例如,运行 torch.Tensor.index_add_() 的不确定性 CUDA 实现将抛出错误

>>> import torch
>>> torch.use_deterministic_algorithms(True)
>>> torch.randn(2, 2).cuda().index_add_(0, torch.tensor([0, 1]), torch.randn(2, 2))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: index_add_cuda_ does not have a deterministic implementation, but you set
'torch.use_deterministic_algorithms(True)'. ...

当使用稀疏-密集 CUDA 张量调用 torch.bmm() 时,它通常使用不确定性算法,但是当确定性标志打开时,将使用其备用的确定性实现

>>> import torch
>>> torch.use_deterministic_algorithms(True)
>>> torch.bmm(torch.randn(2, 2, 2).to_sparse().cuda(), torch.randn(2, 2, 2).cuda())
tensor([[[ 1.1900, -2.3409],
         [ 0.4796,  0.8003]],
        [[ 0.1509,  1.8027],
         [ 0.0333, -1.1444]]], device='cuda:0')

此外,如果您正在使用 CUDA 张量,并且您的 CUDA 版本为 10.2 或更高版本,则应根据 CUDA 文档设置环境变量 CUBLAS_WORKSPACE_CONFIGhttps://docs.nvidia.com/cuda/cublas/index.html#results-reproducibility

CUDA 卷积确定性

虽然禁用 CUDA 卷积基准测试(如上所述)可确保 CUDA 每次运行应用程序时都选择相同的算法,但除非设置了 torch.use_deterministic_algorithms(True)torch.backends.cudnn.deterministic = True,否则该算法本身可能是不确定的。后一个设置仅控制此行为,与 torch.use_deterministic_algorithms() 不同,后者也会使其他 PyTorch 操作以确定性方式运行。

CUDA RNN 和 LSTM

在某些版本的 CUDA 中,RNN 和 LSTM 网络可能具有不确定性行为。有关详细信息和解决方法,请参阅 torch.nn.RNN()torch.nn.LSTM()

填充未初始化的内存

诸如 torch.empty()torch.Tensor.resize_() 之类的操作可以返回具有未初始化内存的张量,这些张量包含未定义的值。如果需要确定性,则将此类张量用作另一个操作的输入是无效的,因为输出将是不确定的。但是实际上并没有什么可以阻止运行此类无效代码。因此,为了安全起见,默认情况下 torch.utils.deterministic.fill_uninitialized_memory 设置为 True,如果设置了 torch.use_deterministic_algorithms(True),它将用已知值填充未初始化的内存。这将防止此类不确定性行为的可能性。

但是,填充未初始化的内存会损害性能。因此,如果您的程序有效且不使用未初始化的内存作为操作的输入,则可以关闭此设置以获得更好的性能。

DataLoader

DataLoader 将按照 多进程数据加载中的随机性 算法重新播种 worker。使用 worker_init_fn()generator 来保持可重现性

def seed_worker(worker_id):
    worker_seed = torch.initial_seed() % 2**32
    numpy.random.seed(worker_seed)
    random.seed(worker_seed)

g = torch.Generator()
g.manual_seed(0)

DataLoader(
    train_dataset,
    batch_size=batch_size,
    num_workers=num_workers,
    worker_init_fn=seed_worker,
    generator=g,
)

文档

访问 PyTorch 的全面开发者文档

查看文档

教程

获取面向初学者和高级开发者的深入教程

查看教程

资源

查找开发资源并获得问题解答

查看资源