Pytorch实现多GPU并行训练(DDP)

这篇具有很好参考价值的文章主要介绍了Pytorch实现多GPU并行训练(DDP)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Pytorch实现并行训练通常有两个接口:DP(DataParallel)DDP(DistributedDataParallel)。目前DP(DataParallel)已经被Pytorch官方deprecate掉了,原因有二:1,DP(DataParallel)只支持单机多卡,无法支持多机多卡;2,DP(DataParallel)即便在单机多卡模式下效率也不及DDP(DistributedDataParallel)。我们可以看一下官方文档里的描述:

DistributedDataParallel is proven to be significantly faster than torch.nn.DataParallel for single-node multi-GPU data parallel training.

基于这两个缺点,DP(DataParallel)本文就不介绍了,即使DP(DataParallel)更好上手。本文重点讲解如何使用DDP(DistributedDataParallel)来完成并行训练。

官方文档:https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html
官方视频教程:https://pytorch.org/tutorials/beginner/ddp_series_intro.html


DDP

DDP是基于多进程来实现并行训练,每个GPU依靠独立的进程来驱动,进程之间有特殊的通信机制。先介绍几个变量:

变量名 意义
rank 0 在节点中的编号,比如当前是第0张GPU
world_size 4 整个节点数量,比如一共4张GPU

我们先看一个单GPU代码,然后将其转换成DDP并行训练代码,观察变化来进行学习
单GPU版本:(实验代码来自https://github.com/pytorch/examples/tree/main/distributed/ddp-tutorial-series)

import torch
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader

class MyTrainDataset(Dataset):
    def __init__(self, size):
        self.size = size
        self.data = [(torch.rand(20), torch.rand(1)) for _ in range(size)]

    def __len__(self):
        return self.size
    
    def __getitem__(self, index):
        return self.data[index]


class Trainer:
    def __init__(
        self,
        model: torch.nn.Module,
        train_data: DataLoader,
        optimizer: torch.optim.Optimizer,
        gpu_id: int,
        save_every: int,
    ) -> None:
        self.gpu_id = gpu_id
        self.model = model.to(gpu_id)
        self.train_data = train_data
        self.optimizer = optimizer
        self.save_every = save_every

    def _run_batch(self, source, targets):
        self.optimizer.zero_grad()
        output = self.model(source)
        loss = F.cross_entropy(output, targets)
        loss.backward()
        self.optimizer.step()

    def _run_epoch(self, epoch):
        b_sz = len(next(iter(self.train_data))[0])
        print(f"[GPU{self.gpu_id}] Epoch {epoch} | Batchsize: {b_sz} | Steps: {len(self.train_data)}")
        for source, targets in self.train_data:
            source = source.to(self.gpu_id)
            targets = targets.to(self.gpu_id)
            self._run_batch(source, targets)

    def _save_checkpoint(self, epoch):
        ckp = self.model.state_dict()
        PATH = "checkpoint.pt"
        torch.save(ckp, PATH)
        print(f"Epoch {epoch} | Training checkpoint saved at {PATH}")

    def train(self, max_epochs: int):
        for epoch in range(max_epochs):
            self._run_epoch(epoch)
            if epoch % self.save_every == 0:
                self._save_checkpoint(epoch)


def load_train_objs():
    train_set = MyTrainDataset(2048)  # load your dataset
    model = torch.nn.Linear(20, 1)  # load your model
    optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
    return train_set, model, optimizer


def prepare_dataloader(dataset: Dataset, batch_size: int):
    return DataLoader(
        dataset,
        batch_size=batch_size,
        pin_memory=True,
        shuffle=True
    )


def main(device, total_epochs, save_every, batch_size):
    dataset, model, optimizer = load_train_objs()
    train_data = prepare_dataloader(dataset, batch_size)
    trainer = Trainer(model, train_data, optimizer, device, save_every)
    trainer.train(total_epochs)


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description='simple distributed training job')
    parser.add_argument('total_epochs', type=int, help='Total epochs to train the model')
    parser.add_argument('save_every', type=int, help='How often to save a snapshot')
    parser.add_argument('--batch_size', default=32, type=int, help='Input batch size on each device (default: 32)')
    args = parser.parse_args()
    
    device = 0  # shorthand for cuda:0
    main(device, args.total_epochs, args.save_every, args.batch_size)

直接复制上面代码,保存为single_gpu.py,然后用以下命令就可以让代码运行:

python single_gpu.py 10000 1000

这一步如果运行失败,请留言告知。如果运行成功,我们观察GPU,会发现该程序只会调用第一张GPU。我们将上述代码稍微改进,就可以得到多GPU版本了:

import torch
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader

import torch.multiprocessing as mp
from torch.utils.data.distributed import DistributedSampler
from torch.nn.parallel import DistributedDataParallel as DDP
from torch.distributed import init_process_group, destroy_process_group
import os

class MyTrainDataset(Dataset):
    def __init__(self, size):
        self.size = size
        self.data = [(torch.rand(20), torch.rand(1)) for _ in range(size)]

    def __len__(self):
        return self.size
    
    def __getitem__(self, index):
        return self.data[index]

def ddp_setup(rank, world_size):
    """
    Args:
        rank: Unique identifier of each process
        world_size: Total number of processes
    """
    os.environ["MASTER_ADDR"] = "localhost"
    os.environ["MASTER_PORT"] = "12355"
    init_process_group(backend="nccl", rank=rank, world_size=world_size)
    torch.cuda.set_device(rank)

class Trainer:
    def __init__(
        self,
        model: torch.nn.Module,
        train_data: DataLoader,
        optimizer: torch.optim.Optimizer,
        gpu_id: int,
        save_every: int,
    ) -> None:
        self.gpu_id = gpu_id
        self.model = model.to(gpu_id)
        self.train_data = train_data
        self.optimizer = optimizer
        self.save_every = save_every
        self.model = DDP(model, device_ids=[gpu_id])

    def _run_batch(self, source, targets):
        self.optimizer.zero_grad()
        output = self.model(source)
        loss = F.cross_entropy(output, targets)
        loss.backward()
        self.optimizer.step()

    def _run_epoch(self, epoch):
        b_sz = len(next(iter(self.train_data))[0])
        print(f"[GPU{self.gpu_id}] Epoch {epoch} | Batchsize: {b_sz} | Steps: {len(self.train_data)}")
        self.train_data.sampler.set_epoch(epoch)
        for source, targets in self.train_data:
            source = source.to(self.gpu_id)
            targets = targets.to(self.gpu_id)
            self._run_batch(source, targets)

    def _save_checkpoint(self, epoch):
        ckp = self.model.module.state_dict()
        PATH = "checkpoint.pt"
        torch.save(ckp, PATH)
        print(f"Epoch {epoch} | Training checkpoint saved at {PATH}")

    def train(self, max_epochs: int):
        for epoch in range(max_epochs):
            self._run_epoch(epoch)
            if self.gpu_id == 0 and epoch % self.save_every == 0:
                self._save_checkpoint(epoch)


def load_train_objs():
    train_set = MyTrainDataset(2048)  # load your dataset
    model = torch.nn.Linear(20, 1)  # load your model
    optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
    return train_set, model, optimizer


def prepare_dataloader(dataset: Dataset, batch_size: int):
    return DataLoader(
        dataset,
        batch_size=batch_size,
        pin_memory=True,
        shuffle=False,
        sampler=DistributedSampler(dataset)
    )


def main(rank: int, world_size: int, save_every: int, total_epochs: int, batch_size: int):
    ddp_setup(rank, world_size)
    dataset, model, optimizer = load_train_objs()
    train_data = prepare_dataloader(dataset, batch_size)
    trainer = Trainer(model, train_data, optimizer, rank, save_every)
    trainer.train(total_epochs)
    destroy_process_group()


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description='simple distributed training job')
    parser.add_argument('total_epochs', type=int, help='Total epochs to train the model')
    parser.add_argument('save_every', type=int, help='How often to save a snapshot')
    parser.add_argument('--batch_size', default=32, type=int, help='Input batch size on each device (default: 32)')
    args = parser.parse_args()
    
    world_size = torch.cuda.device_count()
    mp.spawn(main, args=(world_size, args.save_every, args.total_epochs, args.batch_size), nprocs=world_size)

将上述代码保存为mp_train.py,然后直接运行:

python mp_train.py 10000 1000

我们再观察GPU,会发现所有GPU均被调用。到此,实验部分已经结束。下面进入分析阶段。


从单卡变为多卡,需要进行哪些修改?

我们可以简单比对以下差别:

vimdiff single_gpu.py mp_train.py

vimdiff可以比较两个文档的差别,这是一个小trick,分享给大家。

  • 第一步:初始化
import torch.multiprocessing as mp
from torch.utils.data.distributed import DistributedSampler
from torch.nn.parallel import DistributedDataParallel as DDP
from torch.distributed import init_process_group, destroy_process_group
import os

def ddp_setup(rank, world_size):
   """
   Args:
       rank: Unique identifier of each process
       world_size: Total number of processes
   """
   os.environ["MASTER_ADDR"] = "localhost"
   os.environ["MASTER_PORT"] = "12355"
   init_process_group(backend="nccl", rank=rank, world_size=world_size)
   torch.cuda.set_device(rank)

init_process_group就是初始化群组,相当于给GPU们打了个招呼,我们要进行并行训练啦。这里的"os.environ"可以不用修改,因为只是单机多卡,不涉及多机多卡。"nccl"表示Nvidia Collective Communications Library,是一个跨GPU的通信后端类型,一般也不用修改。这里需要注意的是rankworld_size,比如咱们是4卡机,rank的取值范围是[0,1,2,3],而world_size=4。

  • 第二步:改造模型和数据加载器
    Pytorch实现多GPU并行训练(DDP)
    Pytorch实现多GPU并行训练(DDP)
    左边是单卡模式,右边是多卡模式。
  • 步骤三:用多进程启动
world_size = torch.cuda.device_count()
mp.spawn(main, args=(world_size, args.save_every, args.total_epochs, args.batch_size), nprocs=world_size)

如有不解,欢迎留言讨论~文章来源地址https://www.toymoban.com/news/detail-511110.html

到了这里,关于Pytorch实现多GPU并行训练(DDP)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 分布式并行训练(DP、DDP、DeepSpeed)

    [pytorch distributed] 01 nn.DataParallel 数据并行初步 数据并行 vs. 模型并行 数据并行 :模型拷贝(per device),数据 split/chunk(对batch切分) 每个device上都拷贝一份完整模型,每个device分别处理1个batch的一部分(如batch_size=64, 2个device, 每device处理32个样本) 梯度反向传播时,每个设备上

    2024年02月07日
    浏览(34)
  • Pytorch 分布式并行DDP 卡死 挂起

    1 、使用A30显卡,使用分布式并行Distributed Data Parallel,运行程序时显卡显存充满,卡在设置local_rank处,并未启动进程组 2 、如图: 0 、 最新解决方案,针对Supermicro主板:BIOS-Advanced-NB Configuration-IOMMU-Disabled ==其它型号的主板的BIOS可能还需要禁用ACS: https://zhuanlan.zhihu.com/p/60

    2023年04月15日
    浏览(45)
  • 【NLP相关】PyTorch多GPU并行训练(DataParallel和DistributedDataParallel介绍、单机多卡和多机多卡案例展示)

    ❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博相关......)👈 当下深度学习应用越来越广泛,训练规模也越来越大,需要更快速的训练速

    2024年02月04日
    浏览(29)
  • pytorch中分布式训练DDP教程(新手快速入门!)

    PyTorch是深度学习领域广泛使用的开源深度学习框架之一。随着深度学习模型的不断增大和数据集的不断增长,单机训练往往不能满足我们的需求。为了加速训练过程,我们可以使用分布式训练技术。在PyTorch中,分布式数据并行(Distributed Data Parallel,简称DDP)是一种常见的分

    2024年02月16日
    浏览(31)
  • 【分布式训练】基于Pytorch的分布式数据并行训练

    简介: 在PyTorch中使用DistributedDataParallel进行多GPU分布式模型训练 加速神经网络训练的最简单方法是使用GPU,它在神经网络中常见的计算类型(矩阵乘法和加法)上提供了比CPU更大的加速。随着模型或数据集变得越来越大,一个GPU很快就会变得不足。例如,像BERT和GPT-2这样的

    2024年02月17日
    浏览(36)
  • Python基于Pytorch Transformer实现对iris鸢尾花的分类预测,分别使用CPU和GPU训练

    iris数据集是机器学习中一个经典的数据集,由英国统计学家Ronald Fisher在1936年收集整理而成。该数据集包含了3种不同品种的鸢尾花(Iris Setosa,Iris Versicolour,Iris Virginica)各50个样本,每个样本包含了花萼长度(sepal length)、花萼宽度(sepal width)、花瓣长度(petal length)、花瓣宽度

    2024年02月01日
    浏览(36)
  • Pytorch 多GPU训练

      2.1 单GPU声明 2.2 多GPU声明     重要:nn.DataParallel net = nn.DataParallel(net) net.to(device=device) 使用 nn.DataParallel 打包模型 然后用 nn.DataParallel 的 model.to(device) 把模型传送到多块GPU中进行运算 torch.nn.DataParallel(DP) DataParallel(DP)中的参数: module 即表示你 定义的模型 device_ids 表示你

    2024年02月16日
    浏览(24)
  • pytorch:单机多卡(GPU)训练

    目前pytorch支持2种多卡训练: 1. torch.nn.DataParallel 2. torch.nn.parallel.DistributedDataParallel 第一种只支持单机多卡,第二种支持单机多卡和多机多卡;性能上,第二种优于第一种,真正实现分布式训练。下面只介绍第二种方法的单机多卡训练: 根据以上设置,便可实现单机多卡的分

    2024年02月13日
    浏览(29)
  • 用 GPU 并行环境 Isaac Gym + 强化学习库 ElegantRL:训练机器人Ant,3小时6000分,最高12000分

    前排提醒,目前我们能 “用 ppo 四分钟训练 ant 到 6000 分”,比本文的 3 小时快了很多很多,有空会更新代码 https://blog.csdn.net/sinat_39620217/article/details/131724602 介绍了 Isaac Gym 库 如何使用 GPU 做大规模并行仿真,对环境模块提速。这篇帖子,我们使用 1 张 A100GPU 在 3 个小时之内

    2024年02月16日
    浏览(28)
  • 【pytorch实用小技巧】单gpu与多gpu训练与评估

    1、单gpu 首先检查GPU是否可用,并将模型、输入数据和目标标签移动到GPU上。 然后,定义损失函数和优化器。在训练循环中,将模型设置为训练模式,进行前向传播、计算损失、反向传播和参数更新。 在测试阶段,将模型设置为评估模式,并在测试数据上进行推断。 2、多

    2024年02月12日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包