作者:昇腾实战派 x 3号小金鱼

摘要

近期,我们在NPU集群上开展了Qwen2.5VL-72B模型的128K长序列训练任务。针对多模态大模型在长序列场景下面临的显存压力和计算效率挑战,本文记录了FSDP2框架适配、显存异常管理、Ring/Ulysses/Hybrid混合序列并行、ViT-DP异构并行、重计算优化、模型加载优化(meta-device、cpu-init、dcp使能)等一系列关键技术攻关工作。

一、概述与优化目标

需求背景
随着Qwen VL系列模型的发布以及下游任务的亮眼表现,为构建多模态理解标杆模型,我们选用了Qwen2.5VL-72B模型进行深度优化。

优化目标

MindSpeed-MM仓Qwen2.5VL-72B模型支持128K长序列训练,在8机Atlas 800T A3服务器资源下,拉起模型128K长序列训练,提升MFU。

二、Qwen2.5VL MFU优化

该模型开箱采用Megatron-LM ring-attention CP切分,同时采用PTD切分,有两个主要因素导致性能不理想:

  • 其一是多模态模型的异构性导致存算失衡,以VLM模型为例,模型同时存在ViT和LLM两种异构的结构,ViT的计算量较大但通常冻结参数不参与训练,其显存占用较小;LLM在不开启重计算的场景下显存占用通常较大,导致传统的Megatron PTD切分无法使得两者同时存算均衡。 分析该模型开箱性能数据的PP流水,除了第一个pp stage具有较高的计算负载,其它的stage都产生了较大的PP空泡,导致硬件利用率低下、整体性能较差。为了解决该问题,通常可以采用FSDP2这种无Pipeline Parallel的切分框架,此外也可以采用DistTrain(DistTrain: Addressing Model and Data Heterogeneity with Disaggregated Training for Multimodal Large Language Models)这种异构的并行策略框架。本文采用FSDP2方案,该章节后续会依次介绍(NPU、GPU)开箱性能数据、基础性能优化、Megatron-FSDP2(后续简称FSDP2)介绍及适配流程等。
  • 其二是开箱长序列采用ring attention算法,ViT模块序列长度是LLM的4倍,当时mindspeed ring_attention不支持TND格式输入,需要转为SBND格式,构建atten mask会占用很大的显存,另外FA的耗时也远大于LLM FA,会在2.2章节补充介绍。

2.1 VLM模型异构MFU优化

2.1.2 Megatron-FSDP2框架适配

本项目选择使用FSDP2替换Megatron PTD,以消除Pipeline parallel,解决pp空泡带的问题。

PyTorch的完全分片数据并行(FSDP)旨在提供一个高性能的即时执行模式实现,对模型参数、梯度、优化器状态进行切分,包含通信分桶和通信/计算掩盖功能。进一步,FSDP2移除了FlatParameter,采用沿0维分片的DTensor表示分片参数,支持对单个参数的便捷操作、免通信的分片状态字典,以及更简化的初始化流程。FSDP示意图:

mindspeed fsdp2_config.yaml的配置项如下:MindSpeed/FSDP2介绍

sharding_size: int	# 分片组大小,表示每个参数分片组的NPU数量
sub_modules_to_wrap: Optional[Iterable[torch.nn.Module]] = None	# 需要进行FSDP包装的模块类列表,需要通过绝对路径引入:例如:mindspeed_mm.models.predictor.dits.sat_dit.VideoDiTBlock
reshard_after_forward: Union[bool, int] = True	# 前向计算后立即重新分片参数
param_dtype: bf16	# 参数存储精度
reduce_dtype: fp32	# 梯度通信精度
cast_forward_inputs: bool = True	# 自动转换前向输入到计算精度
ignored_modules	AEModel, TextEncoder: Optional[Iterable[torch.nn.Module]] = None # 排除FSDP管理的模块列表, 需要通过绝对路径引入:例如:mindspeed_mm.models.ae.base.AEModel
num_to_forward_prefetch: int  # 指定前向计算预取(forward prefetch)的层数,默认值为0

MindSpeed上使用Megatron-FSDP2,

脚本修改:

export CUDA_DEVICE_MAX_CONNECTIONS=2 # fsdp设置不能为1
--untie-embeddings-and-output-weights
--use-torch-fsdp2 \    # fsdp2使能方法
--fsdp2-config-path ./examples/qwen2.5vl/fsdp2_config.yaml \   # fsdp2配置文件
--ckpt-format torch_dist \   # PyTorch (FSDP2)分布式检查点格式
--use-cpu-initialization \   # 模型较大时NPU初始化会oom,需要使用cpu初始化   
--init-model-with-meta-device \  #模型尺寸较大时需要使能meta-device参数初始化  
# fdsp2需要关闭分布式优化器

fsdp2_config.yaml配置文件如下:

# sharding_size: 2  # device_mesh("replicate", "shard"),该配置源至其中的shard(可以不指定,默认为all ranks shard)
sub_modules_to_wrap:   # 需要进行FSDP切分的模块(此处置对LLM的emb和transformer_layer进行FSDP切分,llm_head以及vit暂未切分)
  - megatron.core.transformer.transformer_layer.TransformerLayer
  - megatron.core.models.common.embeddings.language_model_embedding.LanguageModelEmbedding
  - megatron.core.models.common.embeddings.rotary_pos_embedding.RotaryEmbedding
param_dtype: "bf16"
reduce_dtype: "fp32"  # 梯度在fp32高精度下聚合
cast_forward_inputs: True  # 前向过程中自动将输入数据转为混合精度策略中指定的数据类型(可以通过MixedPrecisionPolicy精细化控制)
ignored_modules:   # 不需要进行FSDP切分的模块
  - mindspeed_mm.models.vision.vision_model.VisionModel
recompute_modules:   # 重计算模块
  - megatron.core.transformer.transformer_layer.TransformerLayer

2.2 128K长序列MFU优化

该章节后续主要会依次介绍USP方案适配、ViT-DP长序列优化等内容。MindSpeed-MM中CP算法(Ulysses、Ring Attention、USP等)的适配过程在本文中不多做介绍,感兴趣可以参考MindSpeed-MM相关适配代码:Ring-Attention核心适配代码Ulysses核心适配代码

2.2.1 USP CP方案介绍

在本章节开始的性能优化明细中,有两个提升值得关注,分别是在FSDP2场景下,第一个ring16—>ring4uly4存在提升效果,第二个是在vit-dp是从ring16—>ring2uly8也存在性能提升。

为了探究这个性能收益来源,我们在Qwen2.5VL 72B模型减层场景下进行了ring和uly性能对比。ring和uly主要性能差异在于FA算子,且FAG算子性能区别较小。因为ulysses进入FA时没有切序列长度(完整序列长度),相对ring而言具有更高的FA效率,因此ulysses cp方案性能优化ring cp方案,(head头数、序列长度等)条件允许的情况下应该尽可能使用ulysses或者usp。

以CP16(ulysses_dregree=4为例),usp序列切分算法原理示意图如下所示:

算法流程如下: usp-paper

数据类型为BNSD,CP GROUP被分为两个维度(ring cp_group和ulysses cp_group),可以看做一个二维列表(ulysses对应列,ring对应行),例如cp_size8可以看做2×4,则ulysses cp_size为2,ring cp_size为4。 上表中alltoall可以表示为在特性维度上进行gather,在特定维度上进行scatter,例如ulysses前向过程会对seq维度进行gather、对应hc维度进行scatter(对应scatter_idx=1,gather_idx=2),反向过程则相反。

为什么要使用USP:ulysses和ringattention两种算法对比如下表:

另外,以Qwen2.5VL 72B模型为例:各模块heads数量和GQA数量如下表:

默认情况下,Megatron parallel rank order为tp-cp-ep-dp-pp,因此在使用FSDP2场景时(无TP),CP rank分配具有最高优先级。在该Qwen2.5VL 72B模型中,CP_SIZE最大取值8.(受限于GQA),按照上表分析,CP通信在单机中时,通常ulysses性能优化ring,因此对该模型我们会优先选择ulysses algo。但是对于128k序列长度,cp_size8切分之后序列仍然较大(oom无法支持训练)。因此自然会引出USP,ulysses无法进一步切分的序列长度,使用ring进一步切分。因此,在无法全部使用Ulysses进行CP切分的场景下,使用USP混合序列切分就是更合适的选择。

2.2.2 USP_ViT-DP方案介绍

ViT-DP原理,在qwen2.5vl 72b模型vit模块中存在windows_attention和full attention两种类型Attention计算,其中full attention表示对完整的视频帧/图像数据进行self-attention计算,windows attention则表示将视频帧/图像数据按照patch_size(14)以及spatial_merge_size(2)等参数进行分割后合并,将帧数据分割成一个个小块,然后进行独立的self-attention计算。

基于上述情况,有个直观的想法就是,可以沿着帧的维度(对应full attention)或者patch的维度(对应windows attention)将数据沿着序列维度在cp_group中进行分割,每个cp_rank可以进行独立的Attention计算,并不需要交互,计算完成后再沿着序列维度进行汇聚。Qwen2.5VL模型结构示意图如下:

ViT中两种类型Attention在ViT DP中切分示意图如下,

Windows Attention中多个patch组成一个windows,单个windows内部独立进行Attention计算: Full Attention中单个Frame内部独立进行Attention计算:

不管是Frame还是Windows,换一个角度都可以看做是dp视角的输入,将它们均匀划分到不同rank上独立运算,以实现并行提速。MindSpeed-MM仓中针对Qwen2.5VL ViT-DP的核心适配代码如下:

https://gitcode.com/Ascend/MindSpeed-MM/blob/master/mindspeed_mm/models/common/module_spec/qwen2vl_layer_spec.py#L107

https://gitcode.com/Ascend/MindSpeed-MM/blob/master/mindspeed_mm/models/vision/vision_encoders/qwen2vl_vit_model.py#L628

ViT-DP场景下,定义ViT独立的FA调用函数:
| Qwen2_5VitDotProductAttention替换DotProductAttention,实现ViT和LLM CP隔离:
隔离LLM USP中ulysses对Attention基类wrapper,实现对ViT core_attention去wrapper:

MindSpeed-MM VLM模型代码调用逻辑图如下所示,vlm_model.py文件中定义了模型主体结构(依次调用ViT、LLM,然后计算Loss):

本次适配的USP_ViT-DP CP长序列流程图如下所示:

优势分析:

Qwen2.5VL 128K序列长度需要开启CP16,由于LLM GQA限制,CP可配置为ring16 或者 usp(例如:r8u2),并且ViT和LLM共用CP配置(cp size/group/algo等),不管哪种情况ViT模块长序列都需要ring attention的参与。 对于qwen2.5vl vit中windows attention而言,使用变长FA(TND格式)能有效提升FA效率,当时mindspeed ring attention不支持TND格式,需要转为形如SBND格式,相对TND格式,FA的计算效率明显降低。

进一步,通过Vit-DP的方式将ViT的cp_algo和LLM进行解耦,以实现ViT独立的CP序列并行配置,考虑ViT中Full/Windows两种类型Self Attention都是在Frame帧或者Patch块中独立进行,Frame/Patch之间并不需要进行CP通信,因此不需要使用传统uly/ring/usp CP算法,可直接复用LLM的CP Group,将Frame/Patch的数据(平均)分发给LLM CP Group中的Ranks,进行独立的Attention计算,然后进行结果汇聚,最终实现零通信序列并行。

三、 小结与展望

目前Qwen2.5VL ViT-DP主要针对视频训练数据,对于图片训练数据在full-attention中,图片的数量可能无法达到cp_size,导致存在部分cp_rank无法分配到计算数据,导致Device闲置,通常会拉低MFU,不符合设计预期。此外ViT-DP实现方法上,进行了一定程度的侵入式、定制化修改,由于其核心思想是模型不同模块(ViT vs LLM)CP配置存在差异,其中配置包括:cp_size、cp_group、cp_algo等,这一定程度上和DistTrain并行单元内部独立设置model parallel配置思想类似,也是为了解决VLM等多模态模型异构挑战,DistTrain提出了一种解耦化(Disaggregated)的多模态LLM训练方案,主要包含两方面的创新:

  1. 将计算量、参数量迥异的不同模块分离开,从而使得各模块可以用各自最优的并行策略运行,比较理想地解决了模型异构性带来的问题
  2. 针对多模态场景存在的数据异构性,设计了分离式的数据预处理模块,较大程度上解决了数据异构对DP、PP不均衡性带来的问题

目前MindSpeed-MM版本已经构建DistTrain能力,已构建异构TP|DP|CP|PP方案,针对上述ViT-DP的需求场景,后续MindSpeed-MM可能会提供更加优雅的解决方案。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐