【深度学习】单机多卡 | DataParallel将计算任务在多个 GPU 上并行执行,可以在多个 GPU 上分摊工作负载,从而加快训练速度

news2024/11/24 3:29:02

【深度学习】单机多卡 | DataParallel将计算任务在多个 GPU 上并行执行,可以在多个 GPU 上分摊工作负载,从而加快训练速度

  • 写在最前面
  • DataParallel (DP) 简介
    • 使用 DataParallel 的场景
    • 使用 DataParallel 的基本步骤
  • 代码部分
    • train.py
    • 简单的代码示例
      • 代码解析
    • DataParallel 的局限性
  • 小结


请添加图片描述

🌈你好呀!我是 是Yu欸
🌌 2024每日百字篆刻时光,感谢你的陪伴与支持 ~
🚀 欢迎一起踏上探险之旅,挖掘无限可能,共同成长!

写在最前面

希望在单机多卡的模式下运行我的模型代码,加快训练速度。

请教吕博:如何更改代码?
其中,提到模型先用DP方式运行

DP是什么?又被学到了一个知识点。

在深度学习和分布式计算领域,DP 通常指的是 DataParallelDataParallel 是一种将计算任务在多个 GPU 上并行执行的方法。它在单机多卡环境中非常有用,可以在多个 GPU 上分摊工作负载,从而加快训练速度。

DataParallel (DP) 简介

torch.nn.DataParallel 是 PyTorch 中的一个工具,可以让模型在多个 GPU 上并行运行。它通过将输入批次拆分成多个子批次,每个子批次发送到不同的 GPU 上,并行执行前向传播和反向传播,然后将每个 GPU 上的梯度聚合到主 GPU 上进行参数更新。

使用 DataParallel 的场景

  • 单机多卡训练: 当你有一台机器配备了多块 GPU,并希望利用所有的 GPU 资源来加速模型训练时,DataParallel 是一个简单而有效的解决方案。
  • 简化代码: 相比于更复杂的分布式训练方案,DataParallel 提供了一种较为简化的方式来实现多 GPU 并行训练,通常只需要对模型进行简单包装。

使用 DataParallel 的基本步骤

  1. 定义模型: 创建你的神经网络模型。
  2. 包装模型: 使用 torch.nn.DataParallel 包装你的模型。
  3. 将模型和数据迁移到 GPU: 使用 .to(device) 将模型和输入数据迁移到合适的设备上。
  4. 训练模型: 按照常规方式训练模型。

代码部分

train.py

仅展示相关部分

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0,1,2,3"
# import部分省略

def evaluate(model, device, dataloader):
    model.eval()
    total_loss, total_step = 0.0, 0.0

    # 使用with torch.no_grad()来禁用梯度计算
    with torch.no_grad():
        # 对dataloader中的每个batch进行遍历
        for step, batch in enumerate(dataloader):
            # 将batch中的数据移动到指定设备上
            batch = tuple(t.to(device) for t in batch)
            input_ids, attention_mask, decoder_input_ids, decoder_attention_mask, labels = batch

            # 通过模型进行前向传播,并获取输出结果
            outputs = model(input_ids, attention_mask=attention_mask, decoder_input_ids=decoder_input_ids,
                            decoder_attention_mask=decoder_attention_mask, labels=labels)

            # 获取模型输出结果中的loss值
            loss = outputs['loss']
            # 累加总损失和总步数
            total_loss += loss.item()
            total_step += 1

    # 返回总损失和None
    return total_loss / total_step, None


# 设置随机数种子、日志路径
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 初始化tokenizer、添加特殊的tokens
# define dataloader 定义数据加载器
# 每批的个数4/梯度累计个数4
batch_size = int(args.batch_size / args.gradient_accumulation_steps)
# from processing.dataset import BartDataset
# return input_ids, attention_mask, decoder_input_ids, decoder_attention_mask, labels
trian_dataset = BartDataset(tokenizer, args, mode='train')
train_dataloader = DataLoader(
    dataset=trian_dataset,
    batch_size=batch_size,
    shuffle=True,
    collate_fn=trian_dataset.collate_fn,
    num_workers=20 # 优化数据加载
)

eval_dataset = BartDataset(tokenizer, args, mode='test')
eval_dataloader = DataLoader(
    dataset=eval_dataset,
    batch_size=batch_size,
    shuffle=False,
    collate_fn=eval_dataset.collate_fn,
    num_workers=20 # 优化数据加载
)

# define model 实例化模型

# 检查GPU数量并设置DataParallel
if torch.cuda.device_count() > 1:
    print(f"Using {torch.cuda.device_count()} GPUs")
    net = nn.DataParallel(model)
else:
    print("Using single GPU or CPU")
    net = model

# define criterion 定义损失函数

# define optimizer优化器
# 参考:https://blog.csdn.net/hottie_xiaomiao/article/details/124392847
# 打印每一次迭代元素的名字和param
param_optimizer = list(model.named_parameters())
no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']
# 参数组:每组参数可以指定自己的优化器参数,即可使用不同的优化策略
optimizer_grouped_parameters = [
    {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.01},
    {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
]
optimizer = torch.optim.AdamW(optimizer_grouped_parameters, lr=args.bart_lr)
# 计算总步数
total_steps = int(len(trian_dataset) * args.epochs / args.gradient_accumulation_steps)
# 初始化学习率调整器
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=int(args.warmup_steps * total_steps),
                                            num_training_steps=total_steps)

# Begin training
# logger部分省略

# 定义一个初始的最好评估损失,即正无穷大
best_eval_loss = float('inf')
# 定义当前的步骤和当前损失
current_step, current_loss = 0, 0
# 定义全局步骤数
global_step = 0

# 对于每个 epoch 进行循环
for epoch in range(args.epochs):
    # 将模型设置为训练模式
    model.train()
    # 对训练数据集进行循环
    for step, batch in enumerate(train_dataloader):
        # 将batch中的每一个tensor都移动到指定的设备上(如GPU)
        batch = tuple(t.to(device) for t in batch)
        # 从batch中获取输入,注意这里的命名方式要和模型中forward函数中的输入命名相同
        input_ids, attention_mask, decoder_input_ids, decoder_attention_mask, labels = batch
        # 将输入传递给模型进行前向计算
        outputs = model(input_ids, attention_mask=attention_mask, decoder_input_ids=decoder_input_ids,
                        decoder_attention_mask=decoder_attention_mask, labels=labels)
        # TODO:1
        # print(outputs)

        # 获取模型的损失
        loss = outputs['loss']

        # 记录当前损失和步骤数,用于计算平均损失
        current_loss += loss.item()
        current_step += 1

        # 如果使用了梯度累积,则将损失除以累积步骤数
        if args.gradient_accumulation_steps > 1:
            loss = loss / args.gradient_accumulation_steps

        # 反向传播计算梯度
        loss.backward()
        # 将梯度进行裁剪,以防止梯度爆炸
        clip_grad_norm_(model.parameters(), args.max_clip_norm)

        # 如果达到了梯度累积的步骤数,则进行一次优化更新
        if (step + 1) % args.gradient_accumulation_steps == 0:
            optimizer.step()
            scheduler.step()
            optimizer.zero_grad()
            global_step += 1

        # 如果当前步骤是一个log间隔的倍数,则打印日志信息,清空当前步骤和当前损失

    # 在训练完一个 epoch 后,对模型在验证集上进行评估
    eval_loss, _ = evaluate(model, device, eval_dataloader)
    logger.info("Eval loss: {:.6f}, the best loss: {:.6f}".format(eval_loss, best_eval_loss))
    # 如果当前的评估损失比之前的最好评估损失更小,则更新最好评估损失
    if eval_loss < best_eval_loss:
        best_eval_loss = eval_loss

    # 创建一个输出目录,用于存储模型的输出
    # 在日志中输出检查点保存路径,将模型、tokenizer、args的设置保存到检查点路径中

简单的代码示例

以下是使用 DataParallel 在多 GPU 上运行模型的一个简单示例:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset

# 设置环境变量,指定使用的GPU
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0,1,6,7"

# 定义设备
globalDevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 定义一个简单的CNN模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv = nn.Conv2d(3, 16, 3, 1)
        self.fc = nn.Linear(16 * 26 * 26, 10)

    def forward(self, x):
        x = self.conv(x)
        x = torch.relu(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

# 实例化模型
cnn = CNN().to(globalDevice)

# 检查GPU数量并设置DataParallel
if torch.cuda.device_count() > 1:
    print(f"Using {torch.cuda.device_count()} GPUs")
    net = nn.DataParallel(cnn)
else:
    print("Using single GPU or CPU")
    net = cnn

# 定义数据集和数据加载器
class SimpleDataset(Dataset):
    def __init__(self, size):
        self.size = size

    def __len__(self):
        return self.size

    def __getitem__(self, idx):
        return torch.randn(3, 28, 28), torch.tensor(1)

dataset = SimpleDataset(1000)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True, num_workers=20)

# 定义优化器和损失函数
optimizer = optim.SGD(net.parameters(), lr=0.01)
criterion = nn.CrossEntropyLoss()

# 简单的训练过程
for epoch in range(args.epochs):
    for inputs, labels in dataloader:
        inputs, labels = inputs.to(globalDevice), labels.to(globalDevice)
        optimizer.zero_grad()
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}, Loss: {loss.item()}")

代码解析

  1. 环境变量设置:

    os.environ["CUDA_VISIBLE_DEVICES"] = "0,1,6,7"
    

    指定要使用的 GPU。

  2. 定义设备:

    globalDevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    

    根据是否有可用的 GPU 设置设备。

  3. 定义模型:

    class CNN(nn.Module):
        ...
    
  4. 包装模型:

    if torch.cuda.device_count() > 1:
        net = nn.DataParallel(cnn)
    else:
        net = cnn
    

    如果检测到多个 GPU,使用 DataParallel 包装模型。

  5. 数据加载器:

    dataloader = DataLoader(dataset, batch_size=64, shuffle=True, num_workers=20)
    

    使用 num_workers 参数优化数据加载。

  6. 训练过程:

    for epoch in range(2):
        ...
    

DataParallel 的局限性

  • 数据并行粒度: DataParallel 进行的是数据并行操作,每个 GPU 处理一部分数据批次。这可能导致 GPU 利用率不均衡,尤其是在有计算负载差异的情况下。
  • 单节点限制: DataParallel 主要用于单节点多 GPU。如果需要跨节点并行(分布式训练),应该考虑使用 torch.nn.parallel.DistributedDataParallel

小结

DataParallel 是 PyTorch 提供的一种简单易用的多 GPU 并行方法,适合单节点多卡训练。通过这种方法,可以在多个 GPU 上分摊计算任务,提高训练速度和效率。对于更复杂的分布式计算任务,可以考虑使用 DistributedDataParallel


欢迎大家添加好友交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1874099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ffmpeg使用png编码器把rgb24编码为png图像

version #define LIBAVUTIL_VERSION_MAJOR 58 #define LIBAVUTIL_VERSION_MINOR 12 #define LIBAVUTIL_VERSION_MICRO 100 note 不使用AVOutputFormat code void CFfmpegOps::EncodeRGB24ToPNG(const char *infile, const char *width_str, const char *height_str, c…

什么是ArchiMate?有优缺点和运用场景?

一、什么是ArchiMate? ArchiMate是一种由The Open Group发布的企业级标准&#xff0c;它是一种整合多种架构的可视化业务分析模型语言&#xff0c;也属于架构描述语言&#xff08;ADL&#xff09;。ArchiMate主要从业务、应用和技术三个层次&#xff08;Layer&#xff09;&…

基于MATLAB对线阵天线进行道尔夫—切比雪夫加权

相控阵天线——基于MATLAB对线阵进行道尔夫—切比雪夫加权 目录 前言 一、阵列天线的综合 二、道尔夫—切比雪夫综合 三、单元间距的改变对切比雪夫阵列方向图的影响 四、单元数的改变对切比雪夫阵列激励分布的影响 五、副瓣电平SLL对切比雪夫阵列激励幅度的影响 六、副…

双路视频同屏显示(拼接)-基于野火Zynq7020开发板

前情提要 米联客FDMA驱动OV5640摄像头—基于野火Zynq7020开发板 本文在此基础上&#xff0c;实现了双路视频拼接。将ov5640输出的1024600的图像数据缩放为512600&#xff0c;分两路写入ddr3&#xff0c;并且显示在1024*600的RGB屏幕中。 纯FPGA也可以按此方法实现。 总体BLOC…

MySQL高级-SQL优化-小结

文章目录 1、insert 优化2、主键优化3、order by 优化4、group by 优化5、limit 优化6、count 优化7、update 优化 1、insert 优化 insert&#xff1a;批量插入、手动控制事务、主键顺序插入 大批量插入&#xff1a;load data local infile 2、主键优化 主键长度尽量短、顺序插…

遥感数据并行运算(satellite remote sensing data parallell processing)

文章内容仅用于自己知识学习和分享&#xff0c;如有侵权&#xff0c;还请联系并删除 &#xff1a;&#xff09; 之前不太会用&#xff0c;单纯想记录一下&#xff0c;后面或许还会用到 1. 教程 [1] Pleasingly Parallel Programming: link 1.1 处理器&#xff0c;核和线程 …

基于多模态知识图谱的多模态推理-MR-MKG

MR-MKG论文中提出了一种新的多模态推理方法&#xff0c;即利用多模态知识图&#xff08;Multimodal Knowledge Graph, MMKG&#xff09;进行多模态推理的方法。这种方法旨在通过从MMKG中学习&#xff0c;扩展大型语言模型&#xff08;LLMs&#xff09;的多模态知识。 1 三个模…

【AUTOSAR 基础软件】DEM模块详解(诊断故障管理)

文章包含了AUTOSAR基础软件&#xff08;BSW&#xff09;中DEM模块相关的内容详解。本文从ISO标准&#xff0c;AUTOSAR规范解析&#xff0c;ISOLAR-AB配置以及模块相关代码分析四个维度来帮读者清晰的认识和了解DEM这一基础软件模块。文中涉及的ISOLAR-AB配置以及模块相关代码都…

深度相机识别物体——实现数据集准备与数据集分割

一、数据集准备——Labelimg进行标定 1.安装labelimg——pip install labelimg -i https://pypi.tuna.tsinghua.edu.cn/simple 2.建立相应的数据集存放文件夹 3.打开labelimg&#xff0c;直接在命令行输入labelimg即可&#xff0c;并初始化 4.开始标注&#xff0c;设置标注好…

[Cloud Networking] VLAN

1 为什么需要 VLAN(Virtual Local Area Network) VLAN是一个逻辑网络&#xff0c;VLAN将设备/用户进行逻辑分组&#xff0c;VLAN需要在Switch上创建。为什么需要这样呢&#xff1f;为何不能所有设备都在同一个网络&#xff1f; 如下网络&#xff0c;如果设备过多&#xff0c;…

五线谱与简谱有什么区别 五线谱简谱混排怎么打 吉他谱软件哪个好

五线谱与简谱作为音乐记谱领域的两大主流系统&#xff0c;各自承载着深厚的历史渊源与独特的表现力&#xff0c;并在全球范围内被不同程度地接受和应用。尽管两者都是为了记录音乐作品中的音高和节奏信息&#xff0c;但其内在机制、适用范围以及学习曲线存在显著差别。下面我们…

QT拖放事件之七:子类化QMimeData,实现对多个自定义类型进行数据

1、前提说明 /*自定义的MIME类型数据存储在QMimeData对象中, 存在两种方法:1. setData(...)可以把自定义类型的数据以QByteArray的形式直接存储在QMimeData中,但是使用此方法一次只能对一个MIME类型进行处理(可参考 QT拖放事件六:自定义MIME类型的存储及读取demo ) 一文。…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-44目标检测算法综述:R-CNN、SSD和YOLO

41~44目标检测算法综述&#xff1a;R-CNN、SSD和YOLO 1. 区域卷积神经网络 (R-CNN 系列) 1.1 R-CNN 使用启发式搜索算法来选择锚框。使用预训练模型对每个锚框提取特征&#xff08;每个锚框视为一张图片&#xff0c;使用 CNN 提取特征&#xff09;。训练 SVM 进行类别分类&a…

Halcon 文本文件操作,形态学

一文件的读写 *******************************************************向文本文件写入字符串内容*************************************************************read_image (Image, fabrik)threshold (Image, Region, 0, 120)area_center (Region, Area, Row, Column)open_…

Linux启动elasticsearch,提示权限不够

Linux启动elasticsearch&#xff0c;提示权限不够&#xff0c;如下图所示&#xff1a; 解决办法&#xff1a; 设置文件所有者&#xff0c;即使用户由权限访问文件 sudo chown -R 用户名[:新组] ./elasticsearch-8.10.4 //切换到elasticsearch-8.10.4目录同级 chown详细格式…

面对.rmallox勒索病毒:如何有效防范及应对

引言&#xff1a; 在当今数字化社会&#xff0c;网络安全问题日益严重&#xff0c;勒索病毒成为企业和个人不可忽视的威胁之一。最近出现的.rmallox勒索病毒更是给全球各地的用户带来了严重的数据安全问题。本文将探讨.rmallox勒索病毒的特点、感染方式及应对策略&#xff0c;…

format()方法——格式化字符串

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法介绍 format()可以对数据进行格式化处理操作&#xff0c;语法如下&#xff1a; format(value, format_spec) format_spec为格式化解释。当参数…

【代码工厂】简单地图生成

要求 &#xff08;图片来自codingame&#xff09; 代码 # 定义一个函数&#xff0c;用于生成模式 def generate_pattern(n, a, border_char): # 初始化一个空列表&#xff0c;用于存储生成地图pattern []# 最上面那一行的处理line n * border_charpattern.append(line)# 遍…

Zabbix如何帮助企业将监控数据转化为竞争优势

By Fernanda Moraes 在我们生活的高度互联世界中&#xff0c;变化以越来越快和激烈的速度发生。这影响了消费者的认知与行为&#xff0c;迫使零售商寻找更有效的方式来吸引客户。Linx 是 StoneCo 集团旗下的一家公司&#xff0c;也是零售技术专家&#xff0c;Linx了解这一点&am…

两张图片怎样拼在一起?将两张图片拼在一起的几种方法介绍

两张图片怎样拼在一起&#xff1f;拼接两张图片是一种常见的编辑技巧&#xff0c;能够将不同的视觉元素融合成一个整体&#xff0c;从而创造出更加生动和丰富的图像效果。无论是为了设计创意作品、制作社交媒体内容&#xff0c;还是简单地为个人相册增添趣味&#xff0c;掌握如…