PyTorch的可重複性問題 (如何使實驗結果可復現)

由於在模型訓練的過程中存在大量的隨機操作,使得對於同一份代碼,重複運行後得到的結果不一致。因此,爲了得到可重複的實驗結果,我們需要對隨機數生成器設置一個固定的種子。

許多博客都有介紹如何解決這個問題,但是很多都不夠全面,往往不能保證結果精確一致。我經過許多調研和實驗,總結了以下方法,記錄下來。

全部設置可以分爲三部分:

 

1. CUDNN

cudnn中對卷積操作進行了優化,犧牲了精度來換取計算效率。如果需要保證可重複性,可以使用如下設置:

from torch.backends import cudnn
cudnn.benchmark = False            # if benchmark=True, deterministic will be False
cudnn.deterministic = True

不過實際上這個設置對精度影響不大,僅僅是小數點後幾位的差別。所以如果不是對精度要求極高,其實不太建議修改,因爲會使計算效率降低。

 

2. Pytorch

torch.manual_seed(seed)            # 爲CPU設置隨機種子
torch.cuda.manual_seed(seed)       # 爲當前GPU設置隨機種子
torch.cuda.manual_seed_all(seed)   # 爲所有GPU設置隨機種子

 

3. Python & Numpy

如果讀取數據的過程採用了隨機預處理(如RandomCrop、RandomHorizontalFlip等),那麼對python、numpy的隨機數生成器也需要設置種子。

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

 

 

最後,關於dataloader:

注意,如果dataloader採用了多線程(num_workers > 1), 那麼由於讀取數據的順序不同,最終運行結果也會有差異。也就是說,改變num_workers參數,也會對實驗結果產生影響。目前暫時沒有發現解決這個問題的方法,但是隻要固定num_workers數目(線程數)不變,基本上也能夠重複實驗結果。

對於不同線程的隨機數種子設置,主要通過DataLoader的worker_init_fn參數來實現。默認情況下使用線程ID作爲隨機數種子。如果需要自己設定,可以參考以下代碼:

GLOBAL_SEED = 1

def set_seed(seed):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

GLOBAL_WORKER_ID = None
def worker_init_fn(worker_id):
    global GLOBAL_WORKER_ID
    GLOBAL_WORKER_ID = worker_id
    set_seed(GLOBAL_SEED + worker_id)

dataloader = DataLoader(dataset, batch_size=16, shuffle=True, num_workers=2, worker_init_fn=worker_init_fn)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章