欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

PyTorch?Distributed?Data?Parallel使用詳解

 更新時間:2023年03月19日 16:09:36   作者:光火  
這篇文章主要為大家介紹了PyTorch?Distributed?Data?Parallel使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

DDP

Distributed Data Parallel 簡稱 DDP,是 PyTorch 框架下一種適用于單機(jī)多卡、多機(jī)多卡任務(wù)的數(shù)據(jù)并行方式。由于其良好的執(zhí)行效率及廣泛的顯卡支持,熟練掌握 DDP 已經(jīng)成為深度學(xué)習(xí)從業(yè)者所必備的技能之一。本文結(jié)合具體代碼,詳細(xì)地說明了 DDP 在項目中的使用方式。讀者按照本文所給的范例,只需稍經(jīng)調(diào)試,即可實現(xiàn) DDP 的整套流程。

概念辨析

具體講解 DDP 之前,我們先了解了解它和 Data Parallel (DP) 之間的區(qū)別。DP 同樣是 PyTorch 常見的多 GPU 并行方式之一,且它的實現(xiàn)非常簡潔:

# 函數(shù)定義
torch.nn.DataParallel(module, device_ids=None, output_device=None, dim=0)
'''
module : 模型
device_ids : 參與訓(xùn)練的 GPU 列表
output_device : 指定輸出的 GPU, 通常省略, 即默認(rèn)使用索引為 0 的顯卡
'''
# 程序模板
device_ids = [0, 1]
net = torch.nn.DataParallel(net, device_ids=device_ids)

基本原理及固有缺陷:在 Data Parallel 模式下,數(shù)據(jù)會被自動切分,加載到 GPU。同時,模型也將拷貝至各個 GPU 進(jìn)行正向傳播。在多個進(jìn)程之間,會有一個進(jìn)程充當(dāng) master 節(jié)點,負(fù)責(zé)收集各張顯卡積累的梯度,并據(jù)此更新參數(shù),再統(tǒng)一發(fā)送至其他顯卡。因此整體而言,master 節(jié)點承擔(dān)了更多的計算與通信任務(wù),容易造成網(wǎng)絡(luò)堵塞,影響訓(xùn)練速度。

常見問題及解決方案:Data Parallel 要求模型必須在 device_ids[0] 擁有參數(shù)及緩沖區(qū),因此當(dāng)卡 0 被占用時,可以在 nn.DataParallel 之前添加如下代碼:

# 按照 PIC_BUS_ID 順序自 0 開始排列 GPU 設(shè)備
os.environ['CUDA_DEVICE_ORDER'] = 'PIC_BUS_ID'
# 設(shè)置當(dāng)前使用的 GPU 為 2、3 號設(shè)備
os.environ['CUDA_VISIBLE_DEVICES'] = '2, 3'

如此,device_ids[0] 將被默認(rèn)為 2 號卡,device_ids[1] 則對應(yīng) 3 號卡

相較于 DP, Distributed Data Parallel 的實現(xiàn)要復(fù)雜得多,但是它的優(yōu)勢也非常明顯:

  • DDP 速度更快,可以達(dá)到略低于顯卡數(shù)量的加速比;
  • DDP 可以實現(xiàn)負(fù)載的均勻分配,克服了 DP 需要一個進(jìn)程充當(dāng) master 節(jié)點的固有缺陷;
  • 采用 DDP 通??梢灾С指蟮?batch size,不會像 DP 那樣出現(xiàn)其他顯卡尚有余力,而卡 0 直接 out of memory 的情況;
  • 另外,在 DDP 模式下,輸入到 data loader 的 bacth size 不再代表總數(shù),而是每塊 GPU 各自負(fù)責(zé)的 sample 數(shù)量。比方說,batch_size = 30,有兩塊 GPU。在 DP 模式下,每塊 GPU 會負(fù)責(zé) 15 個樣本。而在 DDP 模式下,每塊 GPU 會各自負(fù)責(zé) 30 個樣本;
  • DDP 基本原理:倘若我們擁有 N 張顯卡,則在 Distributed Data Parallel 模式下,就會啟動 N 個進(jìn)程。每個進(jìn)程在各自的卡上加載模型,且模型的參數(shù)完全相同。訓(xùn)練過程中,各個進(jìn)程通過一種名為 Ring-Reduce 的方式與其他進(jìn)程通信,交換彼此的梯度,從而獲得所有的梯度信息。隨后,各個進(jìn)程利用梯度的平均值更新參數(shù)。由于初始值和更新量完全相同,所以各個進(jìn)程更新后的參數(shù)仍保持一致。

常用術(shù)語

  • rank
    • 進(jìn)程號
    • 多進(jìn)程上下文中,通常假定 rank = 0 為主進(jìn)程或第一個進(jìn)程
  • node
    • 物理節(jié)點,表示一個容器或一臺機(jī)器
    • 節(jié)點內(nèi)部可以包含多個 GPU
  • local_rank
    • 一個 node 中,進(jìn)程的相對序號
    • local_rank 在 node 之間獨立
  • world_size
    • 全局進(jìn)程數(shù)
    • 一個分布式任務(wù)中 rank 的數(shù)量
  • group
    • 進(jìn)程組
    • 一個分布式任務(wù)就對應(yīng)一個進(jìn)程組
    • 只有當(dāng)用戶創(chuàng)立多個進(jìn)程組時,才會用到

代碼實現(xiàn)

Distributed Data Parallel 可以通過 Python 的 torch.distributed.launch 啟動器,在命令行分布式地執(zhí)行 Python 文件。執(zhí)行過程中,啟動器會將當(dāng)前進(jìn)程(其實就是 GPU)的 index 通過參數(shù)傳遞給 Python,而我們可以利用如下方式獲取這個 index:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--local_rank', default=-1, type=int,
                    metavar='N', help='Local process rank.')
args = parser.parse_args()
# print(args.local_rank)
# local_rank 表示本地進(jìn)程序號

隨后,初始化進(jìn)程組。對于在 GPU 執(zhí)行的任務(wù),建議選擇 nccl (由 NVIDIA 推出) 作為通信后端。對于在 CPU 執(zhí)行的任務(wù),建議選擇 gloo (由 Facebook 推出) 作為通信后端。倘若不傳入 init_method,則默認(rèn)為 env://,表示自環(huán)境變量讀取分布式信息

dist.init_process_group(backend='nccl', init_method='env://')
# 初始化進(jìn)程組之后, 通常會執(zhí)行這兩行代碼
torch.cuda.set_device(args.local_rank)
device = torch.device('cuda', args.local_rank)
# 后續(xù)的 model = model.to(device), tensor.cuda(device)
# 對應(yīng)的都是這里由 args.local_rank 初始化得到的 device

數(shù)據(jù)部分,使用 Distributed Sampler 劃分?jǐn)?shù)據(jù)集,并將 sampler 傳入 data loader。需要注意的是,此時在 data loader 中不能指定 shuffle 為 True,否則會報錯 (sampler 已具備隨機(jī)打亂功能)

dev_sampler = data.DistributedSampler(dev_data_set)
train_sampler = data.DistributedSampler(train_data_set)
dev_loader = data.DataLoader(dev_data_set, batch_size=dev_batch_size, 
                             shuffle=False, sampler=dev_sampler)
train_loader = data.DataLoader(train_data_set, batch_size=train_batch_size, 
                               shuffle=False, sampler=train_sampler)

模型部分,首先將將模型送至 device,即對應(yīng)的 GPU 上,再使用 Distributed Data Parallel 包裝模型(順序顛倒會報錯)

model = model.to(device)
model = nn.parallel.DistributedDataParallel(
    model, device_ids=[args.local_rank], output_device=args.local_rank
)

Distributed Data Parallel 模式下,保存模型應(yīng)使用 net.module.state_dict(),而非 net.state_dict()。且無論是保存模型,還是 LOGGER 打印,只對 local_rank 為 0 的進(jìn)程操作即可,因此代碼中會有很多 args.local_rank == 0 的判斷

if args.local_rank == 0:
    LOGGER.info(f'saving latest model: {output_path}')
    torch.save({'model': model.module.state_dict(), 
                'optimizer': None, 'epoch': epoch, 'best-f1': best_f1}, 
               open(os.path.join(output_path, 'latest_model_{}.pth'.format(fold)), 'wb'))

利用 torch.load 加載模型時,設(shè)置 map_location=device,否則卡 0 會承擔(dān)更多的開銷

load_model = torch.load(best_path, map_location=device)
model.load_state_dict(load_model['model'])
  • dist.barrier() 可用于同步多個進(jìn)程,建議只在必要的位置使用,如初始化 DDP 模型之前、權(quán)重更新之后、開啟新一輪 epoch 之前
  • 計算 accuracy 時,可以使用 dist.all_reduce(score, op=dist.ReduceOp.SUM),將各個進(jìn)程計算的準(zhǔn)確率求平均
  • 計算 f1-score 時,可以使用 dist.all_gather(all_prediction_list, prediction_list),將各個進(jìn)程獲得的預(yù)測值和真實值匯總到 all_list,再統(tǒng)一代入公式

啟動方式

torch.distributed.launch

# 此處 --nproc_per_node 4 的含義是 server 有 4 張顯卡
python torch.distributed.launch --nproc_per_node 4 train.py
# 倘若使用 nohup, 則注意輸入命令后 exit 當(dāng)前終端
python torch.distributed.launch --nproc_per_node 4 train.py
  • torchrun,推薦使用這種方式,因為 torch.distributed.launch 即將棄用

代碼中,只需將 Argument Parser 相關(guān)的部分替換為

local_rank = int(os.environ['LOCAL_RANK'])

然后將 args.local_rank 全部改為 local_rank 即可

啟動命令

# 單機(jī)多卡訓(xùn)練時, 可以不指定 nnodes
torchrun --nnodes=1 --nproc_per_node=4 train.py
# 倘若使用 nohup, 則注意輸入命令后 exit 當(dāng)前終端
nohup torchrun --nnodes=1 --nproc_per_node=4 train.py > nohup.out &

以上就是PyTorch Distributed Data Parallel使用詳解的詳細(xì)內(nèi)容,更多關(guān)于PyTorch Distributed Data Parallel的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用python 的matplotlib 畫軌道實例

    使用python 的matplotlib 畫軌道實例

    今天小編就為大家分享一篇使用python 的matplotlib 畫軌道實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-01-01
  • 利用Python實現(xiàn)熱力圖的繪制

    利用Python實現(xiàn)熱力圖的繪制

    熱力圖,是一種通過對色塊著色來顯示數(shù)據(jù)的統(tǒng)計圖表。繪圖時,需指定顏色映射的規(guī)則。本文主要用Python來實現(xiàn)熱力圖的制作,需要的可以參考一下
    2022-09-09
  • Python技巧之變長和定長序列拆分

    Python技巧之變長和定長序列拆分

    這篇文章主要給大家分享的是Python技巧之變長和定長序列拆分,Python中的任何序列(可迭代的對象)都可以通過賦值操作進(jìn)行拆分,包括但不限于元組、列表、字符串、文件、迭代器、生成器等。想了解更多詳細(xì)的小伙伴請參考下面文章內(nèi)容
    2021-12-12
  • 通過python繪制華強(qiáng)買瓜的字符畫視頻的步驟詳解

    通過python繪制華強(qiáng)買瓜的字符畫視頻的步驟詳解

    要把華強(qiáng)賣瓜做成字符視頻大概分為三步,通過讀取視頻,把每一幀轉(zhuǎn)為字符畫,接著把字符畫表現(xiàn)出來,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-11-11
  • 如何用Python編寫一個電子考勤系統(tǒng)

    如何用Python編寫一個電子考勤系統(tǒng)

    這篇文章主要介紹了用Python編寫一個電子考勤系統(tǒng),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02
  • pytest使用parametrize將參數(shù)化變量傳遞到fixture

    pytest使用parametrize將參數(shù)化變量傳遞到fixture

    這篇文章主要為大家介紹了pytest使用parametrize將參數(shù)化變量傳遞到fixture的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • python修改字典內(nèi)key對應(yīng)值的方法

    python修改字典內(nèi)key對應(yīng)值的方法

    這篇文章主要介紹了python修改字典內(nèi)key對應(yīng)值的方法,涉及Python中字典賦值的相關(guān)實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07
  • python在協(xié)程中增加任務(wù)實例操作

    python在協(xié)程中增加任務(wù)實例操作

    在本篇文章里小編給大家整理的是一篇關(guān)于python在協(xié)程中增加任務(wù)實例操作內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-02-02
  • python讀取文本中的坐標(biāo)方法

    python讀取文本中的坐標(biāo)方法

    今天小編就為大家分享一篇python讀取文本中的坐標(biāo)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-10-10
  • 學(xué)會用Python實現(xiàn)滑雪小游戲,再也不用去北海道啦

    學(xué)會用Python實現(xiàn)滑雪小游戲,再也不用去北海道啦

    Python除了極少的事情不能做之外,其他基本上可以說全能.,圖形處理、文本處理、數(shù)據(jù)庫編程、網(wǎng)絡(luò)編程、web編程、黑客編程、爬蟲編寫、機(jī)器學(xué)習(xí)、人工智能等.接下來我就教大家做一個不用去北海道也可以滑雪的小游戲,需要的朋友可以參考下
    2021-05-05

最新評論