再現性¶
無法保證跨 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()
設定為相同的值,從使用隨機數字的運算中獲得相同的結果。
其他函式庫中的隨機數生成器¶
如果您或您正在使用的任何函式庫依賴 NumPy,您可以使用以下方式為全域 NumPy RNG 設置 seed:
import numpy as np
np.random.seed(0)
但是,某些應用程式和函式庫可能會使用 NumPy Random Generator 物件,而不是全域 RNG(https://numpy.dev.org.tw/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_CONFIG:https://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,
)