深度学习技巧应用33-零门槛实现模型在多个GPU的分布式流水线训练的应用技巧

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用33零门槛实现模型在多个GPU的分布式流水线训练的应用技巧,本文将帮助大家零门槛的实现模型在多个GPU的并行训练,如果你手头上没有GPU资源,根据本文的介绍也可实现模型的并行,让大家了解模型的并行是怎么实现的,揭开模型分布式训练的神秘面纱,提升自己的模型训练水平。在大规模语言模型训练领域迈进自己的脚步。
在这里插入图片描述

一、 神经网络模型并行的介绍

神经网络模型并行广泛应用于分布式训练技术中,本文展示了如何通过使用模型并行来解决多个GPU训练的问题,与DataParallel不同,模型并行将单个模型分割到不同的GPU上,而不是将整个模型复制到每个GPU上(假设一个模型M包含10层:使用DataParallel时,每个GPU将拥有这10层的副本,而在使用模型并行在两个GPU上时,每个GPU可能托管5层)。

模型并行的思想是将模型的不同子网络放置到不同的设备上,并相应地实现前向方法以跨设备移动中间输出。由于只有模型的某部分在单个设备上运行,因此一组设备可以共同服务于更大的模型。本文的的重点是将模型并行的思路展示给大家。

分布式流水线模型训练

分布式模型训练通常指的是在多个计算节点上并行地训练机器学习模型。这种训练方式可以提高模型训练的速度,尤其是在处理大规模数据集和复杂模型时。分布式训练可以通过不同的并行策略来实现,例如数据并行、模型并行、流水线并行等。以下是这些并行策略的一些基本数学原理:
1.数据并行(Data Parallelism):
数据并行是最常见的并行策略。在这种策略中,训练数据被分成多个部分,每个计算节点(或设备)独立地在自己的数据部分上训练完整的模型,并定期同步参数更新。
假设我们有一个损失函数 L ( θ ) L(\theta) L(θ),其中 θ \theta θ 是模型参数。在数据并行中,每个节点计算损失和梯度:
分布式流水线模型训练 ∇ θ L k ( θ ) = ∂ L k ( θ ) ∂ θ \nabla_{\theta} L_k(\theta) = \frac{\partial L_k(\theta)}{\partial \theta} θLk(θ)=θLk(θ)
其中 L k L_k Lk 是第 k k k 个节点上的损失函数。参数更新可以通过平均所有节点的梯度来实现:
θ ← θ − α 1 N ∑ k = 1 N ∇ θ L k ( θ ) \theta \leftarrow \theta - \alpha \frac{1}{N} \sum_{k=1}^{N} \nabla_{\theta} L_k(\theta) θθαN1k=1NθLk(θ)
其中 α \alpha α 是学习率, N N N 是节点数。
2.模型并行(Model Parallelism):
当模型太大以至于无法放入单个设备的内存时,可以使用模型并行。在这种策略中,模型的不同部分被放置在不同的计算节点上。每个节点只负责模型的一部分,所有节点协同工作以完成整个前向和后向传播。
3.流水线并行(Pipeline Parallelism):
流水线并行将模型分成几个部分,并且每个部分由不同的计算节点处理。这些节点按流水线的方式工作,即一个节点处理一部分输入并将结果传递给下一个节点,以此类推。这种方式可以减少延迟并提高设备利用率。

二、构建两个GPU的环境

很多人可能设备环境都没有GPU,下面我介绍在kaggle平台上实现模型并行,环境选择加速器选择GPU T4*2。

现在从一个包含两个线性层的模型开始,为了在两个GPU设备上运行这个模型,我们只需将每个线性层放置在不同的GPU上,并根据层的设备相应地移动输入和中间输出。

import torch
import torch.nn as nn
import torch.optim as optim

# 检查是否有可用的GPU
if torch.cuda.is_available():
    # 输出GPU数量
    print(f"Number of GPUs available: {torch.cuda.device_count()}")
    # 输出当前GPU的名称
    for i in range(torch.cuda.device_count()):
        print(f"GPU {i}: {torch.cuda.get_device_name(i)}")
else:
    print("No GPU available.")

class MainModel(nn.Module):
    def __init__(self):
        super(MainModel, self).__init__()
        self.net1 = torch.nn.Linear(10, 10).to('cuda:0')
        self.relu = torch.nn.ReLU()
        self.net2 = torch.nn.Linear(10, 5).to('cuda:1')

    def forward(self, x):
        x = self.relu(self.net1(x.to('cuda:0')))
        return self.net2(x.to('cuda:1'))

model = MainModel()
loss_fn = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.001)

optimizer.zero_grad()
outputs = model(torch.randn(20, 10))
labels = torch.randn(20, 5).to('cuda:1')
loss_fn(outputs, labels).backward()
optimizer.step()

运行后,我们看到两台GPU信息

Number of GPUs available: 2
GPU 0: Tesla T4
GPU 1: Tesla T4

三、将模型并行应用于现有模块

这里我们需要将torchvision.models.resnet50()分解到两个GPU上。从现有的ResNet模块继承,并在构造过程中将层分割到两个GPU上。然后,重写前向方法,通过相应地移动中间输出,将两个子网络拼接起来。

from torchvision.models.resnet import ResNet, Bottleneck
import torch.nn as nn
num_classes = 1000

class ModelParallelResNet50(ResNet):
    def __init__(self, *args, **kwargs):
        super(ModelParallelResNet50, self).__init__(
            Bottleneck, [3, 4, 6, 3], num_classes=num_classes, *args, **kwargs)

        self.seq1 = nn.Sequential(
            self.conv1,
            self.bn1,
            self.relu,
            self.maxpool,

            self.layer1,
            self.layer2
        ).to('cuda:0')

        self.seq2 = nn.Sequential(
            self.layer3,
            self.layer4,
            self.avgpool,
        ).to('cuda:1')

        self.fc.to('cuda:1')

    def forward(self, x):
        x = self.seq2(self.seq1(x).to('cuda:1'))
        return self.fc(x.view(x.size(0), -1))

单个模型训练与并行模型时间比较

import torchvision.models as models
import matplotlib.pyplot as plt
plt.switch_backend('Agg')
import numpy as np
import timeit
import torch.nn as nn

num_batches = 3
batch_size = 120
image_w = 128
image_h = 128


def train(model):
    model.train(True)
    loss_fn = nn.MSELoss()
    optimizer = optim.SGD(model.parameters(), lr=0.001)

    one_hot_indices = torch.LongTensor(batch_size) \
                           .random_(0, num_classes) \
                           .view(batch_size, 1)

    for _ in range(num_batches):
        # generate random inputs and labels
        inputs = torch.randn(batch_size, 3, image_w, image_h)
        labels = torch.zeros(batch_size, num_classes) \
                      .scatter_(1, one_hot_indices, 1)

        # run forward pass
        optimizer.zero_grad()
        outputs = model(inputs.to('cuda:0'))

        # run backward pass
        labels = labels.to(outputs.device)
        loss = loss_fn(outputs, labels)
        loss.backward()
        optimizer.step()


num_repeat = 10

stmt = "train(model)"

setup = "model = ModelParallelResNet50()"
mp_run_times = timeit.repeat(
    stmt, setup, number=1, repeat=num_repeat, globals=globals())
mp_mean, mp_std = np.mean(mp_run_times), np.std(mp_run_times)

setup = "import torchvision.models as models;" + \
        "model = models.resnet50(num_classes=num_classes).to('cuda:0')"
rn_run_times = timeit.repeat(
    stmt, setup, number=1, repeat=num_repeat, globals=globals())
rn_mean, rn_std = np.mean(rn_run_times), np.std(rn_run_times)


def plot(means, stds, labels, fig_name):
    fig, ax = plt.subplots()
    ax.bar(np.arange(len(means)), means, yerr=stds,
           align='center', alpha=0.5, ecolor='red', capsize=10, width=0.6)
    ax.set_ylabel('ResNet50 Execution Time (Second)')
    ax.set_xticks(np.arange(len(means)))
    ax.set_xticklabels(labels)
    ax.yaxis.grid(True)
    plt.tight_layout()
    plt.savefig(fig_name)
    plt.close(fig)


plot([mp_mean, rn_mean],
     [mp_std, rn_std],
     ['Model Parallel', 'Single GPU'],
     'mp_and_rn.png')

运行结果如图:
在这里插入图片描述

通过流水线输入加速的比较

class PipelineParallelResNet50(ModelParallelResNet50):
    def __init__(self, split_size=20, *args, **kwargs):
        super(PipelineParallelResNet50, self).__init__(*args, **kwargs)
        self.split_size = split_size

    def forward(self, x):
        splits = iter(x.split(self.split_size, dim=0))
        s_next = next(splits)
        s_prev = self.seq1(s_next).to('cuda:1')
        ret = []

        for s_next in splits:
            # A. ``s_prev`` runs on ``cuda:1``
            s_prev = self.seq2(s_prev)
            ret.append(self.fc(s_prev.view(s_prev.size(0), -1)))

            # B. ``s_next`` runs on ``cuda:0``, which can run concurrently with A
            s_prev = self.seq1(s_next).to('cuda:1')

        s_prev = self.seq2(s_prev)
        ret.append(self.fc(s_prev.view(s_prev.size(0), -1)))

        return torch.cat(ret)


setup = "model = PipelineParallelResNet50()"
pp_run_times = timeit.repeat(
    stmt, setup, number=1, repeat=num_repeat, globals=globals())
pp_mean, pp_std = np.mean(pp_run_times), np.std(pp_run_times)

plot([mp_mean, rn_mean, pp_mean],
     [mp_std, rn_std, pp_std],
     ['Model Parallel', 'Single GPU', 'Pipelining Model Parallel'],
     'mp_and_rn_and_pp.png')

运行结果如图:
在这里插入图片描述

我们需要注意的是:设备到设备的张量复制操作会在源设备和目标设备上的当前流上同步。如果您创建了多个流,您必须确保复制操作得到适当的同步。在复制操作完成之前写入源张量或读取/写入目标张量可能会导致未定义的行为。上述实现的只在源设备和目标设备上都使用默认流,因此不需要强制额外的同步。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何测试python 版本与 torch 、 torchvision 版本是否对应?

python 版本与 torch 、 torchvision 版本的对应关系如下图所示: 打开 anaconda powershell prompt,输入如下命令: >python>>>import torch>>>c torch.ones((3,1)) //创建矩阵>>>c c.cuda(0) …

备考 | 2024年NOC大赛 15套图形化编程、Python、C++初复决赛真题试卷

为了让大家备考2024年NOC大赛,今天给大家分享全国中小学信息技术创新与实践大赛(NOC大赛)2023年图形化编程Scratch小低组、小高组初赛复赛决赛6套真题试卷,Python编程小高组、初中组初赛复赛决赛6套真题试卷,C编程初中组初赛复赛决…

『论文阅读|2024 WACV 多目标跟踪Deep-EloU|纯中文版』

论文题目: Iterative Scale-Up ExpansionIoU and Deep Features Association for Multi-Object Tracking in Sports 论文特点: 作者提出了一种迭代扩展的 ExpansionIoU 和深度特征关联方法Deep-EIoU,用于体育场景中的多目标跟踪,旨…

选现货白银投资划不划算?

可以肯定的是选择现货白银投资是划算的,但投资者需要有足够的知识和经验,以及对市场的敏锐观察力。只有这样,投资者才能在现货白银投资中获取收益。在投资市场上,白银作为一种特殊的投资品种,一直以来都备受投资者们的…

macOS跨进程通信: TCP Socket 创建实例

macOS跨进程通信: TCP Socket 创建实例 一: 简介 Socket 是 网络传输的抽象概念。 一般我们常用的有Tcp Socket和 UDP Scoket, 和类Unix 系统(包括Mac)独有的 Unix Domain Socket(UDS)。 Tcp Socket 能够…

东莞UG逆向建模设计汽车内外饰出stp图抄数3d造型建模代画图服务

汽车内外饰三维扫描及逆向建模是一项复杂且技术性强的工程。它涉及到使用高精度的三维扫描仪对汽车内外饰进行全面、细致的扫描,获取其精确的三维数据。这个过程中,需要确保扫描的环境、光线、角度等因素对扫描结果的影响最小化,以保证获取的…

【测试开发八股文】算法

1、栈和队列 1)栈:先进后出;队列:先进先出 2)如何用栈实现队列? 分析:两个栈,栈1负责入队,栈2负责出队 改进点:出队时,栈2出队后,可不…

python基础——线程

线程的使用 from threading import Thread import time def func(i):print("start{}".format(i))time.sleep(1)print("end{}".format(i)) for i in range(10):Thread(targetfunc,args(i,)).start() 守护线程的使用 主进程结束时,守护线程不会…

常用芯片学习——HC244芯片

HC573 三态输出八路缓冲器|线路驱动器 使用说明 SNx4HC244 八路缓冲器和线路驱动器专门设计用于提高三态存储器地址驱动器、时钟驱动器以及总线导向接收器和发送器的性能和密度。SNx4HC244 器件配备两个具有独立输出使能 (OE) 输入的 4 位缓冲器和驱动器。当 OE 为低电平时&a…

1. Matplotlib的Figure基础概念

1. Matplotlib的Figure基础概念 一 **角色和作用**二 **类比:**三 **基本使用示例** Matplotlib是一个用于绘制二维图形的Python库,广泛应用于数据可视化领域。其灵活性和强大的功能使得用户能够轻松创建各种类型的图表,包括折线图、散点图、…

node 第二十二天 mongoDB最新版7.x安装教程

学习服务端其实就是学习数据库, 就web这一条线而言, 客户端的学习就是学习浏览器, 而服务端的学习就是学习数据库(当然还有服务器) 为什么学习mongoDB mongoDB是非关系型数据库(not only sql) 基本上补全了mysql的缺陷, 当然也缺失了部分mysql的优势. 当然, 非大型应用的业务场…

linux中文件锁定--flock命令

在Linux操作系统中,flock是一个用于文件锁定的命令。文件锁定是一种机制,用于在多任务和多用户环境中管理对共享资源(如文件)的访问。flock允许你在代码中设置锁,以确保在任何给定时刻只有一个进程可以访问被锁定的文件…

leetcode—— 腐烂的橘子

腐烂的橘子 在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一: 值 0 代表空单元格;值 1 代表新鲜橘子;值 2 代表腐烂的橘子。 每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回 直到…

Leetcode刷题(二十八)

找出字符串中第一个匹配项的下标(Easy) 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返…

数据链路层流量控制与传输层流量控制对比

目录 一.数据链路层 1.停止等待协议 无差错情况: 有差错情况: 2.滑动窗口协议 (1)后退N帧协议(GBN) (2)选择重传协议(SR) 二.传输层 1.传输层的可靠…

Github2024-01-23 开源项目日报 Top9

根据Github Trendings的统计,今日(2024-01-23统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目3Go项目2TypeScript项目2Dart项目1Jupyter Notebook项目1 gpt4free 语言模型集合改进计划 创建周期…

基于 LangChain 框架,向量数据库如何创建、读取、更新、删除(CRUD)

RAG是目前大语言模型从工具走向生产力实践的最热门的方式,它可以实现从海量的文本数据中检索相关的信息,并用于生成高质量的文本输出。 而聊到RAG,我们就很难避开使用RAG的基础设施-向量数据库 今天我将带领大家,以最为基础的CRU…

安装miniconda、tensorflow、libcudnn

目录 安装miniconda 安装tensorflow 安装 libcudnn 安装miniconda wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && bash Miniconda3-latest-Linux-x86_64.sh 安装tensorflow tensorflow官网,查看版本对应 https:…

Linux——进程程序替换

进程程序替换 文章目录 进程程序替换1. 进程程序替换的基本概念2. exec系列函数2.1 是否带p2.1.1 execl()2.1.2 execlp() 2.2 是否带e2.2.1 execle() 2.3 l或v2.3.1 execvp() 2.4 返回值 3. 注意点 本章思维导图: 注:本章思维导图对应的 .xmind和 .png…

万界星空科技注塑行业MES解决方案

注塑行业是一个具有发展潜力的行业,随着人们对物质生活的质量要求越来越高,日用品、医疗保健、汽车工业以及建筑等行业对注塑制品的需求量日益增长。注塑企业提供的多种多样的塑料产品已深入到经济生活的各个领域,为国家经济的各个部门包括轻…