使用 PyTorch 完全分片数据并行技术加速大模型训练
本文,我们将了解如何基于 PyTorch 最新的 完全分片数据并行 (Fully Sharded Data Parallel,FSDP) 功能用 Accelerate 库来训练大模型。
动机
随着机器学习 (ML) 模型的规模、大小和参数量的不断增加,ML 从业者发现在自己的硬件上训练甚至加载如此大的模型变得越来越难。 一方面,人们发现大模型与较小的模型相比,学习速度更快 (数据和计算效率更高) 且会有显著的提升 [1]; 另一方面,在大多数硬件上训练此类模型变得令人望而却步。
分布式训练是训练这些机器学习大模型的关键。 大规模分布式训练 领域最近取得了不少重大进展,我们将其中一些最突出的进展总结如下:
使用 ZeRO 数据并行 - 零冗余优化器 [2]
阶段 1: 跨数据并行进程 / GPU 对
优化器状态
进行分片阶段 2: 跨数据并行进程/ GPU 对
优化器状态 + 梯度
进行分片阶段 3: 跨数据并行进程 / GPU 对
优化器状态 + 梯度 + 模型参数
进行分片CPU 卸载: 进一步将 ZeRO 阶段 2 的
优化器状态 + 梯度
卸载到 CPU 上 [3]张量并行 [4]: 模型并行的一种形式,通过对各层参数进行精巧的跨加速器 / GPU 分片,在实现并行计算的同时避免了昂贵的通信同步开销。
流水线并行 [5]: 模型并行的另一种形式,其将模型的不同层放在不同的加速器 / GPU 上,并利用流水线来保持所有加速器同时运行。举个例子,在第 2 个加速器 / GPU 对第 1 个 micro batch 进行计算的同时,第 1 个加速器 / GPU 对第 2 个 micro batch 进行计算。
3D 并行 [3]: 采用
ZeRO 数据并行 + 张量并行 + 流水线并行
的方式来训练数百亿参数的大模型。例如,BigScience 176B 语言模型就采用了该并行方式 [6]。
本文我们主要关注 ZeRO 数据并行,更具体地讲是 PyTorch 最新的 完全分片数据并行 (Fully Sharded Data Parallel,FSDP) 功能。 DeepSpeed 和 FairScale 实现了 ZeRO 论文的核心思想。我们已经将其集成到了 transformers
的 Trainer
中,详见博文 通过 DeepSpeed 和 FairScale 使用 ZeRO 进行更大更快的训练[10]。最近,PyTorch 已正式将 Fairscale FSDP 整合进其 Distributed 模块中,并增加了更多的优化。
Accelerate : 无需更改任何代码即可使用 PyTorch FSDP
我们以基于 GPT-2 的 Large (762M) 和 XL (1.5B) 模型的因果语言建模任务为例。
以下是预训练 GPT-2 模型的代码。其与 此处 的官方因果语言建模示例相似,仅增加了 2 个参数 n_train
(2000) 和 n_val
(500) 以防止对整个数据集进行预处理/训练,从而支持更快地进行概念验证。
运行 accelerate config
命令后得到的 FSDP 配置示例如下:
compute_environment: LOCAL_MACHINE
deepspeed_config: {}
distributed_type: FSDP
fsdp_config:
min_num_params: 2000
offload_params: false
sharding_strategy: 1
machine_rank: 0
main_process_ip: null
main_process_port: null
main_training_function: main
mixed_precision: 'no'
num_machines: 1
num_processes: 2
use_cpu: false
多 GPU FSDP
本文我们使用单节点多 GPU 上作为实验平台。我们比较了分布式数据并行 (DDP) 和 FSDP 在各种不同配置下的性能。我们可以看到,对 GPT-2 Large(762M) 模型而言,DDP 尚能够支持其中某些 batch size 而不会引起内存不足 (OOM) 错误。但当使用 GPT-2 XL (1.5B) 时,即使 batch size 为 1,DDP 也会失败并出现 OOM 错误。同时,我们看到,FSDP 可以支持以更大的 batch size 训练 GPT-2 Large 模型,同时它还可以使用较大的 batch size 训练 DDP 训练不了的 GPT-2 XL 模型。
硬件配置: 2 张 24GB 英伟达 Titan RTX GPU。
GPT-2 Large 模型 (762M 参数) 的训练命令如下:
export BS=#`try with different batch sizes till you don't get OOM error,
#i.e., start with larger batch size and go on decreasing till it fits on GPU`
time accelerate launch run_clm_no_trainer.py \
--model_name_or_path gpt2-large \
--dataset_name wikitext \
--dataset_config_name wikitext-2-raw-v1 \
--per_device_train_batch_size $BS
--per_device_eval_batch_size $BS
--num_train_epochs 1
--block_size 12
FSDP 运行截屏:
并行方法 | 最大 Batch Size ($BS) | 大致训练时间 (分钟) | 备注 |
---|---|---|---|
DDP | 7 | 15 | |
DDP + FP16 | 7 | 8 | |
FSDP (配置: SHARD_GRAD_OP) | 11 | 11 | |
FSDP (配置: min_num_params = 1M + FULL_SHARD) | 15 | 12 | |
FSDP (配置: min_num_params = 2K + FULL_SHARD) | 15 | 13 | |
FSDP (配置: min_num_params = 1M + FULL_SHARD + CPU 卸载) | 20 | 23 | |
FSDP (配置: min_num_params = 2K + FULL_SHARD + CPU 卸载) | 22 | 24 |
表 1: GPT-2 Large (762M) 模型 FSDP 训练性能基准测试
从表 1 中我们可以看到,相对于 DDP 而言,FSDP 支持更大的 batch size,在不使用和使用 CPU 卸载设置的情况下 FSDP 支持的最大 batch size 分别可达 DDP 的 2 倍及 3 倍。从训练时间来看,混合精度的 DDP 最快,其后是分别使用 ZeRO 阶段 2 和阶段 3 的 FSDP。由于因果语言建模的任务的上下文序列长度 ( --block_size
) 是固定的,因此 FSDP 在训练时间上加速还不是太高。对于动态 batch size 的应用而言,支持更大 batch size 的 FSDP 可能会在训练时间方面有更大的加速。目前,FSDP 的混合精度支持在 transformers
上还存在一些 问题。一旦问题解决,训练时间将会进一步显著缩短。
使用 CPU 卸载来支持放不进 GPU 显存的大模型训练
训练 GPT-2 XL (1.5B) 模型的命令如下:
export BS=#`try with different batch sizes till you don't get OOM error,
#i.e., start with larger batch size and go on decreasing till it fits on GPU`
time accelerate launch run_clm_no_trainer.py \
--model_name_or_path gpt2-xl \
--dataset_name wikitext \
--dataset_config_name wikitext-2-raw-v1 \
--per_device_train_batch_size $BS
--per_device_eval_batch_size $BS
--num_train_epochs 1
--block_size 12
并行方法 | 最大 Batch Size ($BS) | GPU 数 | 大致训练时间 (小时) | 备注 |
---|---|---|---|---|
DDP | 1 | 1 | NA | OOM Error RuntimeError: CUDA out of memory. Tried to allocate 40.00 MiB (GPU 0; 23.65 GiB total capacity; 22.27 GiB already allocated; 20.31 MiB free; 22.76 GiB reserved in total by PyTorch) |
DDP | 1 | 2 | NA | OOM Error RuntimeError: CUDA out of memory. Tried to allocate 40.00 MiB (GPU 0; 23.65 GiB total capacity; 22.27 GiB already allocated; 20.31 MiB free; 22.76 GiB reserved in total by PyTorch) |
DDP + FP16 | 1 | 1 | NA | OOM Error RuntimeError: CUDA out of memory. Tried to allocate 40.00 MiB (GPU 0; 23.65 GiB total capacity; 22.27 GiB already allocated; 20.31 MiB free; 22.76 GiB reserved in total by PyTorch) |
FSDP (配置: min_num_params = 2K) | 5 | 2 | 0.6 | |
FSDP (配置: min_num_params = 2K + CPU 卸载) | 10 | 1 | 3 | |
FSDP (配置: min_num_params = 2K + CPU 卸载) | 14 | 2 | 1.16 |
表 2: GPT-2 XL (1.5B) 模型上的 FSDP 基准测试
从表 2 中,我们可以观察到 DDP (带和不带 fp16) 甚至在 batch size 为 1 的情况下就会出现 CUDA OOM 错误,从而无法运行。而开启了 ZeRO- 阶段 3 的 FSDP 能够以 batch size 为 5 (总 batch size = 10 (5 \(\times\) 2) ) 在 2 个 GPU 上运行。当使用 2 个 GPU 时,开启了 CPU 卸载的 FSDP 还能将最大 batch size 进一步增加到每 GPU 14。 开启了 CPU 卸载的 FSDP 可以在单个 GPU 上训练 GPT-2 1.5B 模型,batch size 为 10。这使得机器学习从业者能够用最少的计算资源来训练大模型,从而助力大模型训练民主化。
Accelerate 的 FSDP 集成的功能和限制
下面,我们深入了解以下 Accelerate 对 FSDP 的集成中,支持了那些功能,有什么已知的限制。
支持 FSDP 所需的 PyTorch 版本: PyTorch Nightly 或 1.12.0 之后的版本。
命令行支持的配置:
- 分片策略: [1] FULL_SHARD, [2] SHARD_GRAD_OP
- Min Num Params: FSDP 默认自动包装的最小参数量。
- Offload Params: 是否将参数和梯度卸载到 CPU。
如果想要对更多的控制参数进行配置,用户可以利用 FullyShardedDataParallelPlugin
,其可以指定 auto_wrap_policy
、 backward_prefetch
以及 ignored_modules
。
创建该类的实例后,用户可以在创建 Accelerator 对象时把该实例传进去。
有关这些选项的更多信息,请参阅 PyTorch FullyShardedDataParallel 代码。
接下来,我们体会下 min_num_params
配置的重要性。以下内容摘自 [8],它详细说明了 FSDP 自动包装策略的重要性。
(图源: 链接)
当使用 default_auto_wrap_policy
时,如果该层的参数量超过 min_num_params
,则该层将被包装在一个 FSDP 模块中。官方有一个在 GLUE MRPC 任务上微调 BERT-Large (330M) 模型的示例代码,其完整地展示了如何正确使用 FSDP 功能,其中还包含了用于跟踪峰值内存使用情况的代码。
fsdp_with_peak_mem_tracking.py
我们利用 Accelerate 的跟踪功能来记录训练和评估期间的峰值内存使用情况以及模型准确率指标。下图展示了 wandb 实验台 页面的截图。
我们可以看到,DDP 占用的内存是使用了自动模型包装功能的 FSDP 的两倍。不带自动模型包装的 FSDP 比带自动模型包装的 FSDP 的内存占用更多,但比 DDP 少得多。与 min_num_params=1M
时相比, min_num_params=2k
时带自动模型包装的 FSDP 占用的内存略少。这凸显了 FSDP 自动模型包装策略的重要性,用户应该调整 min_num_params
以找到能显著节省内存又不会导致大量通信开销的设置。如 [8] 中所述,PyTorch 团队也在为此开发自动配置调优工具。
需要注意的一些事项
PyTorch FSDP 会自动对模型子模块进行包装、将参数摊平并对其进行原位分片。因此,在模型包装之前创建的任何优化器都会被破坏并导致更多的内存占用。因此,强烈建议在对模型调用
prepare
方法后再创建优化器,这样效率会更高。对单模型而言,如果没有按照顺序调用的话,Accelerate
会抛出以下告警信息,并自动帮你包装模型并创建优化器。FSDP Warning: When using FSDP, it is efficient and recommended to call prepare for the model before creating the optimizer
即使如此,我们还是推荐用户在使用 FSDP 时用以下方式显式准备模型和优化器:
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", return_dict=True)
+ model = accelerator.prepare(model)
optimizer = torch.optim.AdamW(params=model.parameters(), lr=lr)
- model, optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(model,
- optimizer, train_dataloader, eval_dataloader, lr_scheduler
- )
+ optimizer, train_dataloader, eval_dataloader, lr_scheduler = accelerator.prepare(
+ optimizer, train_dataloader, eval_dataloader, lr_scheduler
+ )
对单模型而言,如果你的模型有多组参数,而你想为它们设置不同优化器超参。此时,如果你对整个模型统一调用
prepare
方法,这些参数的组别信息会丢失,你会看到如下告警信息:FSDP Warning: When using FSDP, several parameter groups will be conflated into a single one due to nested module wrapping and parameter flattening.
告警信息表明,在使用 FSDP 对模型进行包装后,之前创建的参数组信息丢失了。因为 FSDP 会将嵌套式的模块参数摊平为一维数组 (一个数组可能包含多个子模块的参数)。举个例子,下面是 GPU 0 上 FSDP 模型的有名称的参数 (当使用 2 个 GPU 时,FSDP 会把第一个分片的参数给 GPU 0, 因此其一维数组中大约会有 55M (110M / 2) 个参数)。此时,如果我们在 FSDP 包装前将 BERT-Base 模型的 [bias, LayerNorm.weight] 参数的权重衰减设为 0,则在模型包装后,该设置将无效。原因是,你可以看到下面这些字符串中均已不含这俩参数的名字,这俩参数已经被并入了其他层。想要了解更多细节,可参阅本 问题 (其中写道: 原模型参数没有 .grads 属性意味着它们无法单独被优化器优化 (这就是我们为什么不能支持对多组参数设置不同的优化器超参)
)。
{
'_fsdp_wrapped_module.flat_param': torch.Size([494209]),
'_fsdp_wrapped_module._fpw_module.bert.embeddings.word_embeddings._fsdp_wrapped_module.flat_param': torch.Size([11720448]),
'_fsdp_wrapped_module._fpw_module.bert.encoder._fsdp_wrapped_module.flat_param': torch.Size([42527232])
}
- 如果是多模型情况,须在创建优化器之前调用模型
prepare
方法,否则会抛出错误。 - FSDP 目前不支持混合精度,我们正在等待 PyTorch 修复对其的支持。
工作原理
(图源: 链接)
上述工作流概述了 FSDP 的幕后流程。我们先来了解一下 DDP 是如何工作的,然后再看 FSDP 是如何改进它的。在 DDP 中,每个工作进程 (加速器 / GPU) 都会保留一份模型的所有参数、梯度和优化器状态的副本。每个工作进程会获取不同的数据,这些数据会经过前向传播,计算损失,然后再反向传播以生成梯度。接着,执行 all-reduce 操作,此时每个工作进程从其余工作进程获取梯度并取平均。这样一轮下来,每个工作进程上的梯度都是相同的,且都是全局梯度,接着优化器再用这些梯度来更新模型参数。我们可以看到,每个 GPU 上都保留完整副本会消耗大量的显存,这限制了该方法所能支持的 batch size 以及模型尺寸。
FSDP 通过让各数据并行工作进程分片存储优化器状态、梯度和模型参数来解决这个问题。进一步地,还可以通过将这些张量卸载到 CPU 内存来支持那些 GPU 显存容纳不下的大模型。在具体运行时,与 DDP 类似,FSDP 的每个工作进程获取不同的数据。在前向传播过程中,如果启用了 CPU 卸载,则首先将本地分片的参数搬到 GPU/加速器。然后,每个工作进程对给定的 FSDP 包装模块/层执行 all-gather 操作以获取所需的参数,执行计算,然后释放/清空其他工作进程的参数分片。在对所有 FSDP 模块全部执行该操作后就是计算损失,然后是后向传播。在后向传播期间,再次执行 all-gather 操作以获取给定 FSDP 模块所需的所有参数,执行计算以获得局部梯度,然后再次释放其他工作进程的分片。最后,使用 reduce-scatter 操作对局部梯度进行平均并将相应分片给对应的工作进程,该操作使得每个工作进程都可以更新其本地分片的参数。如果启用了 CPU 卸载的话,梯度会传给 CPU,以便直接在 CPU 上更新参数。
如欲深入了解 PyTorch FSDP 工作原理以及相关实验及其结果,请参阅 [7,8,9]。
问题
如果在 accelerate 中使用 PyTorch FSDP 时遇到任何问题,请提交至 accelerate。
但如果你的问题是跟 PyTorch FSDP 配置和部署有关的 - 你需要提交相应的问题至 PyTorch。
参考文献
[2] ZeRO: Memory Optimizations Toward Training Trillion Parameter Models
[3] DeepSpeed: Extreme-scale model training for everyone - Microsoft Research
[4] Megatron-LM: Training Multi-Billion Parameter Language Models Using
Model Parallelism
[5] Introducing GPipe, an Open Source Library for Efficiently Training Large-scale Neural Network Models
[6] Which hardware do you need to train a 176B parameters model?
[7] Introducing PyTorch Fully Sharded Data Parallel (FSDP) API | PyTorch
[10] Fit More and Train Faster With ZeRO via DeepSpeed and FairScale
英文原文: https://hf.co/blog/pytorch-fsdp
原文作者: Sourab Mangrulkar,Sylvain Gugger
译者: Matrix Yao (姚伟峰),英特尔深度学习工程师,工作方向为 transformer-family 模型在各模态数据上的应用及大规模模型的训练推理。
使用 PyTorch 完全分片数据并行技术加速大模型训练的更多相关文章
- PyTorch Data Parrallel数据并行
PyTorch Data Parrallel数据并行 可选择:数据并行处理 本文将学习如何用 DataParallel 来使用多 GPU. 通过 PyTorch 使用多个 GPU 非常简单.可以将模型 ...
- PyTorch Tutorials 5 数据并行(选读)
%matplotlib inline 数据并行(选读) Authors: Sung Kim and Jenny Kang 在这个教程里,我们将学习如何使用 DataParallel 来使用多GPU. ...
- 如何借助分布式存储 JuiceFS 加速 AI 模型训练
传统的机器学习模型,数据集比较小,模型的算法也比较简单,使用单机存储,或者本地硬盘就足够了,像 JuiceFS 这样的分布式存储并不是必需品. 随着近几年深度学习的蓬勃发展,越来越多的团队开始遇到了单 ...
- pytorch学习笔记(10)--完整的模型训练(待完善)
一.神经网络训练 # file : train.py # time : 2022/8/11 上午10:03 # function : import torchvision.datasets from ...
- 深度神经网络DNN的多GPU数据并行框架 及其在语音识别的应用
深度神经网络(Deep Neural Networks, 简称DNN)是近年来机器学习领域中的研究热点,产生了广泛的应用.DNN具有深层结构.数千万参数需要学习,导致训练非常耗时.GPU有强大的计算能 ...
- 【深度学习系列2】Mariana DNN多GPU数据并行框架
[深度学习系列2]Mariana DNN多GPU数据并行框架 本文是腾讯深度学习系列文章的第二篇,聚焦于腾讯深度学习平台Mariana中深度神经网络DNN的多GPU数据并行框架. 深度神经网络( ...
- PyTorch如何加速数据并行训练?分布式秘籍大揭秘
PyTorch 在学术圈里已经成为最为流行的深度学习框架,如何在使用 PyTorch 时实现高效的并行化? 在芯片性能提升有限的今天,分布式训练成为了应对超大规模数据集和模型的主要方法.本文将向你介绍 ...
- StartDT AI Lab | 数据增强技术如何实现场景落地与业务增值?
有人说,「深度学习“等于”深度卷积神经网络算法模型+大规模数据+云端分布式算力」.也有人说,「能够在业内叱咤风云的AI都曾“身经百战”,经历过无数次的训练与试错」.以上都需要海量数据做依托,对于那些数 ...
- 千亿参数开源大模型 BLOOM 背后的技术
假设你现在有了数据,也搞到了预算,一切就绪,准备开始训练一个大模型,一显身手了,"一朝看尽长安花"似乎近在眼前 -- 且慢!训练可不仅仅像这两个字的发音那么简单,看看 BLOOM ...
- [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型
[源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型 目录 [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型 0x00 摘要 0x01 前言 1.1 改 ...
随机推荐
- UI通过元素定位实现特定区域截图
最近计划做一个自动截图的工具,目的是实现性能测试资源监控平台(grafana)各硬件资源的自动截图,解放手工操作.前期的截图做了如下探索. 1. 整个页面截图 1.1 代码实现 通过save_scre ...
- QA|如何获取元素属性值|网页计算器自动化测试实战
一般来说 类似于<value>123</value>这样的元素,我们获取元素值是用.text获取,但有时这个值不是写在这里,而是作为标签的属性值写进去的,此时我们就需要获取属性 ...
- COF框架集成mongodb驱动
今天打算在我的COF框架中集成mongodb驱动,这实在是简单的工作,因为基本上只是对pymongo的封装 数据库的集成大同小异,要考虑的点无非是以下几点: 1.命名 2.连接创建 3.连接池管理 4 ...
- 如何使用Python进行投资收益和风险分析
如何投资是现代企业.个人投资者所面临的实际问题,投资的目标是收益尽可能大,但是投资往往伴随着风险,如果在保证收益最大化的情况下,风险最小:或是风险相同的情况下,如何实现收益的最大化:通过本实训,可以使 ...
- 小知识:OCI实例的私钥文件权限
在OCI上创建新的实例时,会提示你保存私钥用于连接,而且该界面不会再次显示,所以一定要保存好这个私钥. 实例创建完成后,当使用保存的私钥进行连接时,却发现由于私钥文件的权限问题无法连接,查看当前私钥文 ...
- k8s证书到期处理
证书续期提示 当执行kubectl get nodes等提示 Unable to connect to the server: x509: certificate has expired or is ...
- CocoaPods 在iOS开发中养活了这么多项目,它到底是个啥?
对于iOS开发者而言,CocoaPods并不陌生,通过pod相关的命令操作,就可以很方便的将项目中用到的三方依赖库资源集成到项目环境中,大大的提升了开发的效率.CocoaPods作为iOS项目的包管理 ...
- 解决CentOS 7出现docker-compose: command not found
解决CentOS 7出现docker-compose: command not found 1. 安装docker-compose 既然使用了docker-compose那自然得安装了 在GitHub ...
- 如何vue3中使用全局变量,与Vue2的区别
对比: 在vue2.x中我们挂载全局变量或方法是通过是使用Vue.prototype.$xxxx=xxx的形式来挂载,然后通过this.$xxx来获取挂载到全局的变量或者方法 但是 在vue3.x中显 ...
- C#学习笔记--变量类型的转换
变量类型的转化: 转换原则 同类型的大的可以装小的,小类型的装大的就需要强制转换. 隐式转换: 同种类型的转换: //有符号 long-->int-->short-->sbyte l ...