转载:【AI系统】混合并行

news/2024/12/12 18:33:01/文章来源:https://www.cnblogs.com/xueaigc/p/18603159

混合并行(HybridParallel)是一种用于分布式计算的高级策略,它结合了数据并行和模型并行的优势,以更高效地利用计算资源,解决深度学习中的大模型训练问题。混合并行不仅能提高计算效率,还能在有限的硬件资源下处理更大的模型和数据集。在深度学习中,数据并行和模型并行各自有其适用的场景和局限性。数据并行适用于训练样本较多而模型较小的情况,通过将数据集分割成多个子集并在不同的设备上同时训练来提高训练速度。而模型并行则适用于模型较大无法单独放入一个设备内存的情况,通过将模型切分成不同的部分分别在多个设备上进行计算。混合并行通过将这两种并行方式结合,加速计算和处理超大模型,从而在各种硬件条件下实现高效的神经网络模型训练。现主流的混合并行为 3D 混合并行,但由于他们一般都在大规模分布式深度学习训练框架中使用,如:Deepspeed 和 Colossal AI,而不是 AI 框架,因此只进行简单讨论。

image

混合并行是由多个并行方式组合而成的:

  • 数据并行:将数据集分割成多个子集,在多个设备上分别处理这些子集。这种方式能显著提高数据处理速度,但需要确保子集之间处理结果一致。

  • 张量并行:将模型的不同部分分配给不同的设备进行处理。这种方式能充分利用各种设备的计算能力,但需要注意设备之间的通信开销。

  • 流水线并行:将模型按层分割成若干块,每块都交给一个设备进行处理。这种方式能提高设备的利用率,但需要确保流水线中各阶段正确传递中间结果。

3D 混合并行 DP+PP+TP

3D 混合并行是一种在深度学习训练中常用的混合并行策略,它将数据并行、模型并行和流水线并行三种并行方式结合起来,以优化资源利用率和训练效率。这种策略尤其适用于处理大规模的神经网络模型。由于每个维度至少需要 2 个 GPU,因此在这里至少需要 8 个 GPU 才能实现完整的 3D 并行。

image

数据、模型和流水线并行各自在提高内存和计算效率方面发挥特定作用。

  • 内存效率:模型的层被分为流水线阶段,每个阶段的层通过模型并行进一步划分。这种 2D 组合同时减少了模型、优化器和激活函数消耗的内存。然而,不能无限制地分割模型,否则会因通信开销而限制计算效率。

  • 计算效率:为了让 Worker 数量在不牺牲计算效率的情况下超越模型和流水线并行,使用 ZeRO 驱动的数据并行(ZeRO-DP)。ZeRO-DP 不仅通过优化器状态分区进一步提高了内存效率,还通过利用拓扑感知映射,使 GPU 数量的扩展具有最小的通信开销。

  • 拓扑感知 3D 映射:3D 并行中的每个维度都被仔细映射到 Worker 上,通过利用两个关键的架构属性实现最大计算效率。

通过流水线和模型并行,数据并行组通信的梯度大小线性减少,因此总通信量比纯数据并行减少。此外,每个数据并行组在一部分本地 Worker 之间独立且并行地执行通信。因此,数据并行通信的有效带宽通过通信量减少和本地化及并行性的结合而得到放大。

模型并行是三种策略中通信开销最大的,所以优先将模型并行组放置在一个节点中,以利用较大的节点内带宽。其次,流水线并行通信量最低,因此在不同节点之间调度流水线,这将不受通信带宽的限制。最后,若张量并行没有跨节点,则数据并行也不需要跨节点;否则数据并行组也需要跨节点。

值得注意的是 ZeRO,它是 DP 的超级可伸缩增强版,在完全分片的数据并行一文中已经讨论过了。通常它是一个独立的功能,不需要 PP 或 TP。但它也可以与 PP、TP 结合使用。当 ZeRO-DP 与 PP (以及 TP) 结合时,它通常只启用 ZeRO 阶段 1,只对优化器状态进行分片(ZeRO 阶段 2 还会对梯度进行分片,阶段 3 也对模型权重进行分片)。虽然理论上可以将 ZeRO 阶段 2 与流水线并行一起使用,但它会对性能产生不良影响。每个 micro batch 都需要一个额外的 reduce-scatter 通信来在分片之前聚合梯度,这会增加潜在的显著通信开销。根据流水线并行的性质,一般会使用小的 micro batch ,并把重点放在算术强度 (micro batch size) 与最小化流水线气泡 (micro batch 的数量) 两者间折衷。因此,增加的通信开销会损害流水线并行。

使用 Torch RPC 实现 DP+PP

可以使用 Torch RPC 实现 DP+PP 的简单实现。需要初始化 RPC 框架,以便在不同设备之间进行远程过程调用。代码中使用 rpc.init_rpc 来启动 RPC 框架,并配置了 TensorPipeRpcBackendOptions 以支持不同的传输和通道方式。这里,tmpfile 用于指定初始化方法。

# In 'run_worker'ntokens = len(vocab) # the size of vocabularyemsize = 4096 # embedding dimensionnhid = 4096 # the dimension of the feedforward network model in ``nn.TransformerEncoder``nlayers = 8 # the number of ``nn.TransformerEncoderLayer`` in ``nn.TransformerEncoder``nhead = 16 # the number of heads in the Multihead Attention modelsdropout = 0.2 # the dropout valuefrom torch.distributed import rpctmpfile = tempfile.NamedTemporaryFile()rpc.init_rpc(name="worker",rank=0,world_size=1,rpc_backend_options=rpc.TensorPipeRpcBackendOptions(init_method="file://{}".format(tmpfile.name),# Specifying _transports and _channels is a workaround and we no longer# will have to specify _transports and _channels for PyTorch# versions >= 1.8.1_transports=["ibv", "uv"],_channels=["cuda_ipc", "cuda_basic"],))# Number of GPUs for model parallelism.num_gpus = 2partition_len = ((nlayers - 1) // num_gpus) + 1# Add encoder in the beginning.tmp_list = [Encoder(ntokens, emsize, dropout).cuda(2 * rank)]module_list = []# Add all the necessary transformer blocks.for i in range(nlayers):transformer_block = TransformerEncoderLayer(emsize, nhead, nhid, dropout)if i != 0 and i % (partition_len) == 0:module_list.append(nn.Sequential(*tmp_list))tmp_list = []device = i // (partition_len)tmp_list.append(transformer_block.to(2 * rank + device))# Add decoder in the end.tmp_list.append(Decoder(ntokens, emsize).cuda(2 * rank + num_gpus - 1))module_list.append(nn.Sequential(*tmp_list))# Need to use 'checkpoint=never' since as of PyTorch 1.8, Pipe checkpointing# doesn't work with DDP.from torch.distributed.pipeline.sync import Pipechunks = 8model = Pipe(torch.nn.Sequential(*module_list), chunks = chunks, checkpoint="never")# Initialize process group and wrap model in DDP.from torch.nn.parallel import DistributedDataParallelimport torch.distributed as distos.environ['MASTER_ADDR'] = 'localhost'os.environ['MASTER_PORT'] = '29500'dist.init_process_group(backend="nccl", rank=rank, world_size=world_size)model = DistributedDataParallel(model)def get_total_params(module: torch.nn.Module):total_params = 0for param in module.parameters():total_params += param.numel()return total_paramsprint_with_rank('Total parameters in model: {:,}'.format(get_total_params(model)))

定义并行化模型的结构。模型包含一个编码器、多个 Transformer 编码层和一个解码器。将这些模块分配到不同的 GPU 上,以实现模型并行。首先,计算每个 GPU 上分配的层数 partition_len,然后创建模型的各个部分并将其移动到相应的设备上。接下来使用 Pipe 模块将模型进行流水线并行化。这里,将模型划分为 8 个块,并设置 checkpoint 为 "never" 以兼容数据并行(DDP)。然后,初始化进程组,并使用 DistributedDataParallel(DDP)包装模型,实现数据并行。进程组的初始化需要指定主节点的地址和端口。最后,定义一个函数来计算模型的总参数数量,并打印结果。

使用 Torch Device Mesh 实现 DP+PP

数据并行(Data Parallelism, DP)和模型并行(Model Parallelism, MP)是两种常见的方法。下面介绍如何使用 Torch Device Mesh 实现数据并行和流水线并行(Pipeline Parallelism, PP)的结合,以提升训练效率和模型性能。

首先,通过 init_device_mesh 初始化一个二维设备网格(Device Mesh)。在这个示例中,在 64 个 GPU 上进行训练,其中每 8 个 GPU 进行一次数据并行,另外 8 个 GPU 进行一次张量并行(Tensor Parallelism, TP)。这样构建的二维网格包含两个维度:数据并行维度(dp)和张量并行维度(tp)。

from torch.distributed.device_mesh import init_device_mesh
from torch.distributed.tensor.parallel import ColwiseParallel, RowwiseParallel, parallelize_module
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP# i.e. 2-D mesh is [dp, tp], training on 64 GPUs that performs 8 way DP and 8 way TP
mesh_2d = init_device_mesh("cuda", (8, 8))
tp_mesh = mesh_2d["tp"] # a submesh that connects intra-host devices
dp_mesh = mesh_2d["dp"] # a submesh that connects inter-host devicesmodel = Model(...)tp_plan = {...}# apply Tensor Parallel intra-host on tp_mesh
model_tp = parallelize_module(model, tp_mesh, tp_plan)
# apply FSDP inter-host on dp_mesh
model_2d = FSDP(model_tp, device_mesh=dp_mesh, use_orig_params=True, ...)

定义模型,并创建一个张量并行计划(Tensor Parallel Plan),该计划指示模型的哪些部分需要并行化,具体配置可以参考上一篇文章中使用 Device Mesh 进行张量并行的内容。通过 parallelize_module 函数,可以在 tp_mesh 上应用张量并行,从而在主机内实现模型的并行化。在 dp_mesh 上应用完全分片数据并行(Fully Sharded Data Parallel, FSDP)。FSDP 通过自动分片和重组模型参数,进一步优化跨主机的模型训练。通过将 model_tp 传递给 FSDP,就可以进行简单的 DP+PP 并行了。

如果您想了解更多AI知识,与AI专业人士交流,请立即访问昇腾社区官方网站https://www.hiascend.com/或者深入研读《AI系统:原理与架构》一书,这里汇聚了海量的AI学习资源和实践课程,为您的AI技术成长提供强劲动力。不仅如此,您还有机会投身于全国昇腾AI创新大赛和昇腾AI开发者创享日等盛事,发现AI世界的无限奥秘~
转载自: https://www.cnblogs.com/ZOMI/articles/18562959

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

转载:【AI系统】张量并行

在大模型的训练中,单个设备往往无法满足计算和存储需求,因此需要借助分布式训练技术。其中,模型并行(Model Parallelism, MP)是一种重要的方法。模型并行的基本思想是将模型的计算任务拆分到不同的设备上执行,以提高训练效率和处理更大规模的模型。下面将重点介绍模型并行…

转载:【AI系统】动手实现 PyTorch 微分

这里记录一下使用操作符重载(OO)编程方式的自动微分,其中数学实现模式则是使用反向模式(Reverse Mode),综合起来就叫做反向 OO 实现 AD 啦。 基础知识 下面一起来回顾一下操作符重载和反向模式的一些基本概念,然后一起去尝试着用 Python 去实现 PyTorch 这个 AI 框架中最…

转载:【AI系统】计算图原理

在前面的文章曾经提到过,目前主流的 AI 框架都选择使用计算图来抽象神经网络计算表达,通过通用的数据结构(张量)来理解、表达和执行神经网络模型,通过计算图可以把 AI 系统化的问题形象地表示出来。 本文将会以 AI 概念落地的时候,遇到的一些问题与挑战,因此引出了计算图…

转载:【AI系统】计算图基本介绍

在 AI 框架发展的最近一个阶段,技术上主要以计算图来描述神经网络。前期实践最终催生出了工业级 AI:TensorFlow 和 PyTorch,这一时期同时伴随着如 Chainer、DyNet、CNTK、PaddlePaddle、JAX 等激发了框架设计灵感的诸多实验课程。 TensorFlow 和 PyTorch,特别是 PyTorch 代…

转载:【AI系统】什么是微分

自动微分(Automatic Differentiation,AD)是一种对计算机程序进行高效准确求导的技术,一直被广泛应用于计算流体力学、大气科学、工业设计仿真优化等领域。 近年来,机器学习技术的兴起也驱动着对自动微分技术的研究进入一个新的阶段。随着自动微分和其他微分技术研究的深入…

转载:【AI系统】Auto-Tuning 原理

在硬件平台驱动算子运行需要使用各种优化方式来提高性能,然而传统的手工编写算子库面临各种窘境,衍生出了自动生成高性能算子的的方式,称为自动调优。在本文我们首先分析传统算子库面临的挑战,之后介绍基于 TVM 的业界领先的三个自动调优系统。 高性能算子挑战 DNN 部署的硬…

转载:【AI系统】微分计算模式

上一篇文章简单了解计算机中常用几种微分方式。本文将深入介绍 AI 框架离不开的核心功能:自动微分。 而自动微分则是分为前向微分和后向微分两种实现模式,不同的实现模式有不同的机制和计算逻辑,而无论哪种模式都离不开雅克比矩阵,所以我们也会深入了解一下雅克比矩阵的原理…

转载:【AI系统】AI 框架之争

在前面的内容主要是讲述了 AI 框架在数学上对自动微分进行表达和处理,最后表示称为开发者和应用程序都能很好地去编写深度学习中神经网络的工具和库,整体流程如下所示:除了要回答最核心的数学表示原理以外,实际上 AI 框架还要思考和解决许多问题,如 AI 框架如何对实际的神…

转载:【AI系统】指令和存储优化

除了应用极广的循环优化,在 AI 编译器底层还存在指令和存储这两种不同优化。 指令优化 指令优化依赖于硬件提供的特殊加速计算指令。这些指令,如向量化和张量化,能够显著提高计算密度和执行效率。向量化允许我们并行处理数据,而张量化则进一步扩展了这一概念,通过将数据组…

转载:【AI系统】算子循环优化

在具体硬件执行计算的时候,实际会大量地使用 for 等循环指令不断地去读取不同的数据执行重复的指令(SIMT/SIMD),因此循环优化主要是为了提升数据的局部性或者计算的并行性,从而提升整体算子性能,当然这二者都需要 AI 芯片硬件的支持。 循环优化挑战 数据局部性 数据的局部…

数字组合转字母删除二叉树节点字符串相乘打家劫舍ii无序数组第k大 无序数组前k大两个有序数组合并中文数字转换为整数最大连续子数组和零钱凑数

一、数字串转换为字符串 1-26个数字分别代表26个字符(A-z)输入"12326〞就可以拆分为【1,2,3,2,6】、(12, 3, 2, 6]. [1, 23, 2, 6]【1,23,26】、【12,3,26】等,将每种组合转成成对应字母输出,输出所有可能的结果返回所有可能的转换结果// 将数字串转换成字母串// 将数…