Pytorch | Pytorch框架中模型和數據的gpu和cpu模式:model.to(device), model.cuda(), model.cpu(), DataParallel

 

背景介紹

我們在使用Pytorch訓練時,模型和數據有可能加載在不同的設備上(gpu和cpu),在算梯度或者loss的時候,報錯信息類似如下:

RuntimeError: Function AddBackward0 returned an invalid gradient at index 1 - expected type torch.cuda.FloatTensor but got torch.FloatTensor

RuntimeError: Expected object of device type cuda but got device type cpu for argument #2 'target' in call to _thnn_l1_loss_forward

 

這種報錯通常有幾種情況:

1. 數據在cpu上,模型在gpu上;

2. 數據在gpu上,模型在cpu上;

3. 指定層不支持gpu或cpu版本,但數據和模型都在gpu或cpu上。

 

解決方案

第1步:將模型和數據都移到同一設備上。

假設模型和數據定義如下:

model = Model()
input = dataloader()
output = model(input)

移動到gpu上:

# solution: 0
device = 'gpu'
model = model.to(device)
data = data.to(device)

# solution: 1
model = model.cuda()
data = data.cuda()

移動到cpu上:

# solution: 0
device = 'cpu'
model = model.to(device)
data = data.to(device)

# solution: 1
model = model.cpu()
data = data.cpu()

 

第二步:打印模型model和數據data在gpu上還是cpu上。

1. 通過判斷模型model的參數是否在cuda上來判定模型是否在gpu上。

print('Is model on gpu: ', next(model.parameters()).is_cuda)

輸出若是True,則model在gpu上;若是False,則model在cpu上。

2. 輸出數據data的device字段。

print('data device: ', data.device)

輸出gpu則在gpu上,輸出cpu則在cpu上。

 

第三步:排查策略。

一般故事就到這裏結束了。但是實際很多情況中,因爲模型定義、數據加載複雜多樣,很多時候還是會出現開頭的報錯。這個時候建議大家:

1. 在模型的定義開頭處,設置統一的device變量。

有很多論壇裏的解法建議大家添加了層之後,就逐層進行第一步裏的設置。但是這樣的操作很繁瑣,而且可能會有很多冗餘和缺漏。在模型定義之後,統一移到指定device上,然後通過第二步的輸出,確保模型定義model和數據data都在指定的device上即可。

2. 逐步進行第二步的輸出。

我遇到的情況是,我的模型定義後,參數已經在cpu上,數據也在cpu上,但是在訓練過程中仍然報錯。所以我在每個關鍵節點處都設置了第二步的打印策略,檢查是不是中間處理步驟有設置出錯。

3. 定位問題,修改代碼。

 

DataParallel:自動拷貝到GPU上

前面提到的排查策略後,我發現在模型設置移到cpu之後(打印輸出正確),仍然出現開頭的報錯。定位到問題就是在模型model定義後,還使用了DataParallel進行多卡(多gpu)的訓練設置。

具體的官方定義在:data_parallel_tutorial

 

在使用了DataParallel api之後,模型model會被自動拷貝到可用gpu上。當需求是在cpu模式下進行訓練,DataParallel就會自動把數據和模型都拷貝到gpu上,所以就有了開頭的報錯。

這個是在網上找到的解答:does-dataparallel-matters-in-cpu-mode

Ya, In CPU mode you cannot use DataParallel ().
Wrapping a module with DataParallel () simply copies the model over multiple GPUs and puts the results in device_ids[0] i.e. in 1st of the number of GPUs provided. If you are running in CPU mode, you should simply remove the DataParallel () wrapping. Regarding the documentation line “The code does not need to be changed in CPU-mode.” I guess it means the rest of code( other than model definition) need not be changed. But I am not sure about this and someone else can explain this part.

直白來說,在cpu模式下,我們不能使用DataParallel。該API會直接把模型拷貝到多塊可用的gpu上,並把結果保存在第0塊gpu上。若我們的模型運行在cpu模式下,我們需要移除DataParallel()。

 

在去掉了DataParallel之後,模型就可以和數據運行在cpu上了。

 

 

 

歡迎大家留言區指正~ 預祝大家煉丹順利哈哈哈哈哈哈~

 

 

 

 

 

 

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