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

詳解pytorch的多GPU訓(xùn)練的兩種方式

 更新時(shí)間:2022年02月11日 16:22:56   作者:Mr_health  
本文主要介紹了詳解pytorch的多GPU訓(xùn)練的兩種方式,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

方法一:torch.nn.DataParallel

1. 原理

如下圖所示:小朋友一個(gè)人做4份作業(yè),假設(shè)1份需要60min,共需要240min。

這里的作業(yè)就是pytorch中要處理的data。

與此同時(shí),他也可以先花3min把作業(yè)分配給3個(gè)同伙,大家一起60min做完。最后他再花3min把作業(yè)收起來,一共需要66min。

這個(gè)小朋友就是主GPU。他的過程是:分發(fā) ->并行運(yùn)算->結(jié)果回收。 

這就是pytorch要使用的第一種并行方法:torch.nn.DataParallel

這種方法也稱為單進(jìn)程多GPU訓(xùn)練模式:DP模式,這種并行模式下并行的多卡都是由一個(gè)進(jìn)程進(jìn)行控制。換句話說,在進(jìn)行梯度的傳播時(shí),是在主GPU上進(jìn)行的。

采用torch.nn.DataParallel進(jìn)行多GPU并行訓(xùn)練時(shí),與其搭配的數(shù)據(jù)讀取代碼是:torch.utils.data.DataLoader

2. 常用的配套代碼如下

train_datasets = customData(train_txt)  #創(chuàng)建datasettrain_dataloaders = torch.utils.data.DataLoader(train_datasets,opt.batch_size,num_workers=train_num_workers,shuffle=True)  #創(chuàng)建dataloadermodel = efficientnet_b0(num_classes = opt.num_class)  #創(chuàng)建modeldevice_list = list(map(int,list(opt.device_id)))print("Using gpu"," ".join([str(v) for v in device_list]))device = device_list[0]  #主GPU,也就是分發(fā)任務(wù)和結(jié)果回收的GPU,也是梯度傳播更新的GPUmodel = torch.nn.DataParallel(model,device_ids=device_list)model.to(device)for data in train_dataloaders:    model.train(True)   inputs, labels = data   inputs = Variable(inputs.to(device))  #將數(shù)據(jù)放到主要GPU   labels = Variable(labels.to(device)) 

3. 優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):配置起來非常方便
  • 缺點(diǎn):GPU負(fù)載不均衡,主GPU的負(fù)載很大,而其他GPU的負(fù)載很少

方法二:torch.distributed

1. 代碼說明

這個(gè)方法本來是用于多機(jī)器多卡(多節(jié)點(diǎn)多卡)訓(xùn)練的,但是也可以用于單機(jī)多卡(即將節(jié)點(diǎn)數(shù)設(shè)置為1)訓(xùn)練。

初始化的代碼如下,這個(gè)一定要寫在最前面。

from torch.utils.data.distributed import DistributedSampler
torch.distributed.init_process_group(backend="nccl")

這里給出一個(gè)簡(jiǎn)單的demo.py作為說明:

import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.utils.data import Dataset, DataLoader
import os
from torch.utils.data.distributed import DistributedSampler
# 1) 初始化
torch.distributed.init_process_group(backend="nccl")
 
input_size = 5
output_size = 2
batch_size = 30
data_size = 90
 
# 2) 配置每個(gè)進(jìn)程的gpu
local_rank = torch.distributed.get_rank()
print('local_rank',local_rank)
torch.cuda.set_device(local_rank)
device = torch.device("cuda", local_rank)
 
class RandomDataset(Dataset):
    def __init__(self, size, length):
        self.len = length
        self.data = torch.randn(length, size).to('cuda')
 
    def __getitem__(self, index):
        return self.data[index]
 
    def __len__(self):
        return self.len
 
dataset = RandomDataset(input_size, data_size)
# 3)使用DistributedSampler
rand_loader = DataLoader(dataset=dataset,
                         batch_size=batch_size,
                         sampler=DistributedSampler(dataset))
 
class Model(nn.Module):
    def __init__(self, input_size, output_size):
        super(Model, self).__init__()
        self.fc = nn.Linear(input_size, output_size)
 
    def forward(self, input):
        output = self.fc(input)
        print("  In Model: input size", input.size(),
              "output size", output.size())
        return output
 
model = Model(input_size, output_size)
 
# 4) 封裝之前要把模型移到對(duì)應(yīng)的gpu
model.to(device)
 
if torch.cuda.device_count() > 1:
    print("Let's use", torch.cuda.device_count(), "GPUs!")
    # 5) 封裝
    model = torch.nn.parallel.DistributedDataParallel(model,
                                                      device_ids=[local_rank],
                                                      output_device=local_rank)
 
for data in rand_loader:
    if torch.cuda.is_available():
        input_var = data
    else:
        input_var = data
 
    output = model(input_var)
    print("Outside: input size", input_var.size(), "output_size", output.size())

(1)啟動(dòng)方式:在torch.distributed當(dāng)中提供了一個(gè)用于啟動(dòng)的程序torch.distributed.launch,此幫助程序可用于為每個(gè)節(jié)點(diǎn)啟動(dòng)多個(gè)進(jìn)程以進(jìn)行分布式訓(xùn)練,它在每個(gè)訓(xùn)練節(jié)點(diǎn)上產(chǎn)生多個(gè)分布式訓(xùn)練進(jìn)程。

(2)啟動(dòng)命令:

CUDA_VISIBLE_DEVICES=1,2,3,4 python -m torch.distributed.launch --nproc_per_node=2 torch_ddp.py

這里需要說明一下參數(shù):

  • CUDA_VISIBLE_DEVICES:設(shè)置我們可用的GPU的id
  • torch.distributed.launch:用于啟動(dòng)多節(jié)點(diǎn)多GPU的訓(xùn)練
  • nproc_per_node:表示設(shè)置的進(jìn)程數(shù)量,一般情況設(shè)置為可用的GPU數(shù)量,即有多少個(gè)可用的GPU就設(shè)置多少個(gè)進(jìn)程。
  • local rank:關(guān)于這個(gè)參數(shù)的意義,我們將在后面的情形中進(jìn)行說明。

(3)一些情形的說明:

情形1:直接運(yùn)行上述的命令

運(yùn)行的結(jié)果如下:

local_rank 1
local_rank 0
Let's use 4 GPUs!
Let's use 4 GPUs!
  In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
  In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([15, 5]) output_size torch.Size([15, 2])
  In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
  In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([15, 5]) output_size torch.Size([15, 2])

可以看到local rank的輸出為0和1,其數(shù)量與我們?cè)O(shè)置的nproc_per_node是一樣的,與我們?cè)O(shè)置的可用GPU的數(shù)量是無關(guān)的。這里就要說明一下local rank的意義。

local rank:表示的是當(dāng)前的進(jìn)程在當(dāng)前節(jié)點(diǎn)的編號(hào),因?yàn)槲覀冊(cè)O(shè)置了2個(gè)進(jìn)程,因此進(jìn)程的編號(hào)就是0和1

在很多博客中都直接說明local_rank等于進(jìn)程內(nèi)的GPU編號(hào),這種說法實(shí)際上是不準(zhǔn)確的。這個(gè)編號(hào)并不是GPU的編號(hào)!!

在使用啟動(dòng)命令時(shí),torch.distributed.launch工具會(huì)默認(rèn)地根據(jù)nproc_per_node傳入local_rank參數(shù),之后再通過下面的代碼可以得到local_rank.

local_rank = torch.distributed.get_rank()

因?yàn)槭悄J(rèn)傳入?yún)?shù)local_rank,所以還可以這么寫,其輸出與torch.distributed.get_rank()相同

import argparse
parser = argparse.ArgumentParser()
# 注意這個(gè)參數(shù),必須要以這種形式指定,即使代碼中不使用。因?yàn)?launch 工具默認(rèn)傳遞該參數(shù)
parser.add_argument("--local_rank", type=int)
args = parser.parse_args()
 
local_rank = args.local_rank
print('local_rank',args.local_rank)

 情形2:將nproc_per_node設(shè)置為4,即將進(jìn)程數(shù)設(shè)置為可用的GPU數(shù)

運(yùn)行結(jié)果如下:

local_rank 2
local_rank 3
local_rank 1
local_rank 0
Let's use 4 GPUs!
Let's use 4 GPUs!
Let's use 4 GPUs!
Let's use 4 GPUs!
  In Model: input size torch.Size([23, 5]) output size torch.Size([23, 2])
Outside: input size torch.Size([23, 5]) output_size torch.Size([23, 2])
  In Model: input size torch.Size([23, 5]) output size torch.Size([23, 2])
Outside: input size torch.Size([23, 5]) output_size torch.Size([23, 2])
  In Model: input size torch.Size([23, 5]) output size torch.Size([23, 2])
Outside: input size torch.Size([23, 5]) output_size torch.Size([23, 2])
  In Model: input size torch.Size([23, 5]) output size torch.Size([23, 2])
Outside: input size torch.Size([23, 5]) output_size torch.Size([23, 2])

可以看到,此時(shí)的local_rank共有4個(gè),與進(jìn)程數(shù)相同。并且我們?cè)O(shè)置的可用GPU的id是1,2,3,4,而local_rank的輸出為0,1,2,3,可見local_rank并不是GPU的編號(hào)。

雖然在代碼中模型并行的device_ids設(shè)置為local_rank,而local_rank為0,1,2,3,但是實(shí)際上還是采用可用的GPU:1,2,3,4。可以通過nvidia-smi來查看,PID為86478,86479,86480,864782。

model = torch.nn.parallel.DistributedDataParallel(model,
                                             device_ids=[local_rank],
                                             output_device=local_rank)

情形3:將nproc_per_node設(shè)置為4,但是不設(shè)置可用的GPU ID

python -m torch.distributed.launch --nproc_per_node=4 ddp.py

此時(shí)我們?cè)偈褂胣vidia-smi來查看GPU的使用情況,如下??梢钥吹酱藭r(shí)使用的GPU就是local rank的id。相比于情形2,我們可以總結(jié):

當(dāng)沒有設(shè)置可用的GPU ID時(shí),所采用的GPU id就等于local rank的id。本質(zhì)上是將進(jìn)程的編號(hào)作為GPU編號(hào)使用,因此local_rank等于進(jìn)程的編號(hào)這個(gè)定義是不變的。

當(dāng)設(shè)置可用的GPU ID,所采用的GPU id就等于GPU id。

情形4:將nproc_per_node設(shè)置為5,即超出了可以用的GPU數(shù)

輸出結(jié)果如下,可以看到是報(bào)錯(cuò)的,因?yàn)檫M(jìn)程數(shù)超出了可以用的GPU數(shù)量

local_rank 3
local_rank 2
local_rank 4
local_rank 1
local_rank 0
THCudaCheck FAIL file=/pytorch/torch/csrc/cuda/Module.cpp line=59 error=101 : invalid device ordinal
Traceback (most recent call last):
  File "ddp.py", line 18, in <module>
    torch.cuda.set_device(local_rank)
  File "/home/yckj3822/anaconda3/lib/python3.6/site-packages/torch/cuda/__init__.py", line 281, in set_device
    torch._C._cuda_setDevice(device)
RuntimeError: cuda runtime error (101) : invalid device ordinal at /pytorch/torch/csrc/cuda/Module.cpp:59

到此這篇關(guān)于詳解pytorch的多GPU訓(xùn)練的兩種方式的文章就介紹到這了,更多相關(guān)pytorch的多GPU訓(xùn)練內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 在Python中使用成員運(yùn)算符的示例

    在Python中使用成員運(yùn)算符的示例

    這篇文章主要介紹了在Python中使用成員運(yùn)算符的示例,是Python學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-05-05
  • Python中的基本數(shù)據(jù)類型講解

    Python中的基本數(shù)據(jù)類型講解

    這篇文章介紹了Python中的基本數(shù)據(jù)類型,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • python中MySQLdb模塊用法實(shí)例

    python中MySQLdb模塊用法實(shí)例

    這篇文章主要介紹了python中MySQLdb模塊用法,以實(shí)例形式詳細(xì)講述了MySQLdb模塊針對(duì)MySQL數(shù)據(jù)庫的各種常見操作方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2014-11-11
  • 520必備!這些Python表白代碼祝你脫單成功

    520必備!這些Python表白代碼祝你脫單成功

    不會(huì)還有程序猿沒有女朋友吧?沒關(guān)系,今天特地為大家整理了這些Python花式表白代碼,你就放心大膽的去吧,需要的朋友可以參考下
    2021-05-05
  • python?include標(biāo)簽的使用方式及說明

    python?include標(biāo)簽的使用方式及說明

    這篇文章主要介紹了python?include標(biāo)簽的使用方式及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 零基礎(chǔ)寫python爬蟲之爬蟲框架Scrapy安裝配置

    零基礎(chǔ)寫python爬蟲之爬蟲框架Scrapy安裝配置

    Scrapy是一個(gè)使用Python編寫的,輕量級(jí)的,簡(jiǎn)單輕巧,并且使用起來非常的方便。使用Scrapy可以很方便的完成網(wǎng)上數(shù)據(jù)的采集工作,它為我們完成了大量的工作,而不需要自己費(fèi)大力氣去開發(fā)。
    2014-11-11
  • 關(guān)于python time庫整理匯總

    關(guān)于python time庫整理匯總

    這篇文章主要給大家分享的是關(guān)于python time庫的整理,下面文章會(huì)介Time庫的作用,Time庫的使用及案列介紹,感興趣的小伙伴請(qǐng)和小拜年一起來閱讀下文吧
    2021-09-09
  • Python腳本打包成可執(zhí)行文件過程解析

    Python腳本打包成可執(zhí)行文件過程解析

    這篇文章主要介紹了Python腳本打包成可執(zhí)行文件過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 跟老齊學(xué)Python之玩轉(zhuǎn)字符串(2)

    跟老齊學(xué)Python之玩轉(zhuǎn)字符串(2)

    上一篇文章章中已經(jīng)講到連接兩個(gè)字符串的一種方法,本文繼續(xù)講訴連接字符串的方法2,字符串復(fù)制,字符串長(zhǎng)度,字符大小寫的轉(zhuǎn)換,希望對(duì)大家有所幫助。
    2014-09-09
  • 深入理解Python虛擬機(jī)中字節(jié)(bytes)的實(shí)現(xiàn)原理及源碼剖析

    深入理解Python虛擬機(jī)中字節(jié)(bytes)的實(shí)現(xiàn)原理及源碼剖析

    在本篇文章當(dāng)中主要給大家介紹在?cpython?內(nèi)部,bytes?的實(shí)現(xiàn)原理、內(nèi)存布局以及與?bytes?相關(guān)的一個(gè)比較重要的優(yōu)化點(diǎn)——?bytes?的拼接,需要的可以參考一下
    2023-03-03

最新評(píng)論