pytorch 多GPU訓練總結(DataParallel的使用)


版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/weixin_40087578/article/details/87186613
這里記錄用pytorch 多GPU訓練 踩過的許多坑   僅針對單服務器多gpu 數據並行 而不是 多機器分布式訓練

一、官方思路包裝模型


這是pytorch 官方的原理圖  按照這個官方的原理圖  修改應該參照

https://blog.csdn.net/qq_19598705/article/details/80396325

上文也用dataParallel 包裝了optimizer, 對照官方原理圖中第二行第二個,將梯度分發出去,將每個模型上的梯度更新(第二行第三個),然后再將更新完梯度的模型參數合並到主gpu(第二行最后一個步驟)

其實完全沒必要,因為每次前向傳播的時候都會分發模型,用不着反向傳播時將梯度loss分發到各個GPU,單獨計算梯度,再合並模型。可以就在主GPU 上根據總loss 更新模型的梯度,不用再同步其他GPU上的模型,因為前向傳播的時候會分發模型。

所以 上述鏈接里 不用 dataParallel 包裝 optimizer。       

DataParallel並行計算只存在在前向傳播

總結步驟:

import os
import torch
args.gpu_id="2,7" ; #指定gpu id
args.cuda = not args.no_cuda and torch.cuda.is_available() #作為是否使用cpu的判定
#配置環境 也可以在運行時臨時指定 CUDA_VISIBLE_DEVICES='2,7' Python train.py
os.environ['CUDA_VISIBLE_DEVICES'] = args.gpu_id #這里的賦值必須是字符串,list會報錯
device_ids=range(torch.cuda.device_count()) #torch.cuda.device_count()=2
#device_ids=[0,1] 這里的0 就是上述指定 2,是主gpu, 1就是7,模型和數據由主gpu分發

if arg.cuda:
model=model.cuda() #這里將模型復制到gpu ,默認是cuda('0'),即轉到第一個GPU 2
if len(device_id)>1:
model=torch.nn.DaraParallel(model);#前提是model已經.cuda() 了

#前向傳播時數據也要cuda(),即復制到主gpu里
for batch_idx, (data, label) in pbar:
if args.cuda:
data,label= data.cuda(),label.cuda();
data_v = Variable(data)
target_var = Variable(label)
prediction= model(data_v,target_var,args)
#這里的prediction 預測結果是由兩個gpu合並過的,並行計算只存在在前向傳播里
#前向傳播每個gpu計算量為 batch_size/len(device_ids),等前向傳播完了將結果和到主gpu里
#prediction length=batch_size

criterion = nn.CrossEntropyLoss()
loss = criterion(prediction,target_var) #計算loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
之后調用model里的函數 繼承的函數可以直接調用 例如 model.state_dict() ,model.load_state_dict(torch.load(model_path)......不受影響。但是自己寫的函數 要加上.module才行  model.module.forward_getfeature(x)。自己寫的函數 不可以並行運算 ,只能在主gpu中運算。DataParallel並行計算僅存在在前向傳播。但可以換個思路 寫進forward 里或者被forward調用,多返回幾個參數不就得了 return feature,predict

二、解決多GPU 負載不均衡的問題
我的經歷是主gpu 顯存爆了,而其他gpu顯存只用了1/5,負載不均衡到不能忍,無法再加大batch_size

參考:https://discuss.pytorch.org/t/dataparallel-imbalanced-memory-usage/22551/20(看了半天的英文才看懂。。。)

負載不均衡的原因是     loss = criterion(prediction,target_var)  計算loss 占用了大量的內存,如果我們讓每個gpu單獨計算loss,再返回即可解決這個問題  即 prediction,loss=model(data,target)  (#如果后邊不用prediction,連prediction也不用返回,只返回loss)。每個gpu返回一個loss,合到主gpu就是一個list,要loss.mean() 或loss.sum(),推薦mean.

這樣以來,所有可以在其他gpu中單獨計算的都可以寫進forward 里,返回結果即可。但要注意 tensor類型的數組 合並len會增加,例如prediction lenth=batchsize,但是loss這種具體的數字,合並就是list了[loss1.loss2]

效果:主GPU 會稍微高一點。多個幾百M,  但是基本實現了負載均衡

例子:

#model 里的forward 的函數
def forward(self,x,target_var,args):
feature512=self.forward_GetFeature(x)
if target_var is None:
return feature512;
classifyResult = self.classifier(feature512)
# 如果用DataParallel,forward返回feature 是返回多個GPU合並的結果
# 每個GPU 返回 batchsize/n 個樣本,n為GPU數


#計算loss
center_loss = self.get_center_loss(feature512, target_var,args)
criterion = nn.CrossEntropyLoss()
cross_entropy_loss = criterion(classifyResult, target_var)
#CrossEntropyLoss 已經求了softmax 分類結果直接輸進去即可

loss = args.center_loss_weight * center_loss + cross_entropy_loss
prec = accuracy(classifyResult.data, target_var, topk=(1,))

# 如果是返回的標量的話,那返回過去就是list,是n個GPU的結果
# 要loss.mean() 之后 在loss.backward()
return prec[0],loss
 
————————————————
版權聲明:本文為CSDN博主「lllily」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_40087578/article/details/87186613


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM