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

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

1、官方思路包裝模型服務器


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

https://blog.csdn.net/qq_19598705/article/details/80396325分佈式

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

其實徹底不必,由於每次前向傳播的時候都會分發模型,用不着反向傳播時將梯度loss分發到各個GPU,單獨計算梯度,再合併模型。能夠就在主GPU 上根據總loss 更新模型的梯度,不用再同步其餘GPU上的模型,由於前向傳播的時候會分發模型。.net

因此 上述連接裏 不用 dataParallel 包裝 optimizer。       blog

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字符串

2、解決多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

相關文章
相關標籤/搜索