hetero-parallel
===============

Last updated: 12/08/2025. Author: Shangda-Yang

1. 技术背景与挑战
--------------------

随着AI技术的发展,多模态模型(如图文、音视频模型)已成为研究热点,其规模也在急剧增长。这些模型通常包含多个具有不同计算特性的模块,例如Vision Transformer,Audio Encoder,Text Decoder等。各模块由于模型结构、数据的差异性,在相同并行策略下效率存在差异,单一并行策略难以满足大规模多模态模型的训练需求。

现有技术面临的挑战
:::::::::::::::::::

1. **传统并行技术的局限性**
   - 统一并行策略组合(DP/TP/PP/SP/CP)无法充分利用不同模态模块的计算特性
2. **显存与计算资源平衡**
   - 计算、显存负载分配不均衡,导致资源利用率低下
   - 局部资源瓶颈影响整体训练性能

2. 概述
-------------

Hetero Parallel(异构并行)是 MindSpeed-MM 中用于多模态模型的一种异构并行技术,它允许模型中的不同模块(如图像编码器、音频编码器、文本解码器)使用不同的并行配置(例如DP/TP/CP/SP/PP),从而更灵活地利用计算资源并优化性能。

注意:目前暂未识别到编码器使用独立PP并行的场景,编码器部分暂不支持PP。

当前部署方案为全共卡异构并行:

.. image:: ../_static/features/hetero-parallel/共卡部署.png
   :alt: 共卡部署
   :scale: 50%

未来计划支持局部共卡异构并行。

.. image:: ../_static/features/hetero-parallel/局部共卡部署.png
   :alt: 局部共卡部署
   :scale: 50%

3. 核心设计
-------------

- **模块级并行配置**:为多模态模型中的不同组件(image_encoder、audio_encoder、text_decoder)设置独立的并行策略
- **动态并行状态切换**:在不同模块的前/反向传播过程中自动切换并行状态
- **数据高效传递**:实现不同并行组之间的数据分发与收集机制
- **灵活的流水线并行**:支持异构流水线并行(文本解码器部分独立编排流水,灵活复用现有大模型流水线调度策略),优化多模态模型的训练流程

4. 关键实现文件
------------------

4.1 核心实现: ``hetero_parallel.py``
::::::::::::::::::::::::::::::::::::::::

路径: ``MindSpeed-MM/mindspeed_mm/utils/hetero_parallel.py``

主要功能:

- 定义了支持的异构并行模块列表
- 实现了模型前/反向传播前后的并行状态切换钩子
- 提供了数据在不同并行组之间传递的核心函数
- 管理不同模块的并行状态

4.2 流水线并行补丁: ``hetero_pipeline_patches.py``
::::::::::::::::::::::::::::::::::::::::::::::::::::::

路径: ``MindSpeed-MM/mindspeed_mm/patchs/hetero_pipeline_patches.py``

主要功能:

- 扩展了标准流水线并行机制以支持异构并行
- 实现了多模块之间的流水线执行逻辑
- 管理不同模块之间的数据流转

4.3 VLM模型集成: ``vlm_model.py``
::::::::::::::::::::::::::::::::::::

路径: ``MindSpeed-MM/mindspeed_mm/models/vlm_model.py``

主要功能:

- 在多模态模型中集成hetero parallel功能
- 根据配置初始化不同模块的并行设置
- 管理模块间的并行状态切换

5. 核心功能实现
-----------------

特性工作原理如下图:

.. image:: ../_static/features/hetero-parallel/全流程.png
   :alt: 全流程
   :scale: 50%

1. **并行初始化**:解析模型配置,为每个模块创建独立的并行状态快照

2. **模型初始化**:创建多模态模型,注册模块前向钩子
3. **前向传播**:
   - 模块前向钩子触发,切换到对应模块的并行状态
   - 收集/分割数据以适应目标模块的并行配置
   - 执行模块前向计算
   - 模块后向钩子触发,恢复原始并行状态
4. **流水线执行**:在异构流水线模式下,多个模块按顺序执行,实现高效的流水线并行

主要模块实现方式在本章节中的子章节展开。

5.1 并行状态管理
:::::::::::::::::::

.. code-block:: python

    # 支持的异构并行模块列表
    _HeteroParallelModules = ['image_encoder', 'audio_encoder', 'text_decoder']

    # 并行状态字典,用于存储各模块的并行状态快照
    _ParallelStatesDict = {}

    # 初始化各模块的并行状态并保存快照
    def initial_modules_mpu(reuse_module, args):
        # 实现模块并行状态的初始化和快照保存

    # 切换并行状态
    def change_parallel_state(module):
        # 在不同模块(module)之间切换并行状态

5.2 模块前向钩子
:::::::::::::::::::

为不同模块注册前向传播前后的钩子函数,实现并行状态的自动切换和数据处理:

.. code-block:: python

    # 图像编码器前向传播前钩子
    def image_encoder_forward_pre_hook(module, input):
        # 实现并行状态切换和数据收集

    # 图像编码器前向传播后钩子
    def image_encoder_forward_hook(module, input, output):
        # 实现数据收集和并行状态切换

    # 音频编码器前向传播前钩子
    def audio_encoder_forward_pre_hook(module, input):
        # 实现并行状态切换和数据收集

    # 音频编码器前向传播后钩子
    def audio_encoder_forward_hook(module, input, output):
        # 实现数据收集和并行状态切换

5.3 数据并行组操作
::::::::::::::::::::

.. code-block:: python

    def all_gather_dp_group(tensor, pad_token_id=None, cat_dim=0, pad_dim=1, remove_padding=False):
        # 在数据并行组内收集张量
        # 支持对不同长度的序列进行填充和去填充
        
    def split_tensor_dp_group(tensor, split_dim=0, pad_dim=1, chunk_seq_lens=None):
        # 在数据并行组内分割张量
        # 支持按指定长度分割

5.4 异构流水线并行
:::::::::::::::::::::

.. code-block:: python

    def hetero_pipeline(pipeline_meta_list, forward_backward_func_list, *, ...):
        # 实现多模块的异构流水线并行执行
        # 管理不同模块之间的数据流转和并行状态切换

6. 配置与使用
---------------

6.1 权重切分
:::::::::::::::::

xP(encoders) + TP(text decoder) 切分
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code:: bash

    # 7b
    mm-convert  Qwen2_5_OmniConverter hf_to_mm \
      --cfg.mm_dir "ckpt/mm_path/Qwen2.5-Omni-7B" \
      --cfg.hf_config.hf_dir "ckpt/hf_path/Qwen2.5-Omni-7B" \
      --cfg.parallel_config.llm_pp_layers [[28]] \
      --cfg.parallel_config.vit_pp_layers [[32]] \
      --cfg.parallel_config.audio_pp_layers [[32]] \
      --cfg.parallel_config.tp_size 4 \
      --cfg.parallel_config.vit_tp_size 1 \
      --cfg.parallel_config.audio_tp_size 1

xP(encoders)+ PP(text decoder) 切分
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code:: bash

    # 7b
    mm-convert  Qwen2_5_OmniConverter hf_to_mm \
      --cfg.mm_dir "ckpt/mm_path/Qwen2.5-Omni-7B" \
      --cfg.hf_config.hf_dir "ckpt/hf_path/Qwen2.5-Omni-7B" \
      --cfg.parallel_config.llm_pp_layers [[12, 16]] \
      --cfg.parallel_config.vit_pp_layers [[32, 0]] \
      --cfg.parallel_config.audio_pp_layers [[32, 0]] \
      --cfg.parallel_config.tp_size 1 \

    mm-convert  Qwen2_5_OmniConverter base_to_hetero \
      --cfg.mm_dir "ckpt/mm_path/Qwen2.5-Omni-7B" \
      --cfg.mm_heterp_dir "ckpt/mm_heterp_dir/Qwen2.5-Omni-7B" \

6.2 异构并行启用方式
::::::::::::::::::::::::

通过命令行参数启用:

.. code:: bash

    --hetero-parallel

6.3 模块并行配置
:::::::::::::::::::::

**text decoder并行配置**:复用默认配置方式 - 通过启动脚本(xx.sh)中GPT_ARGS下的参数配置,开启PP时需额外在model_xx.json中配置切分方式,具体参照PP配置文档(TODO:**xxxxxx**)。

**模态编码器并行配置**:(model_xx.json)中为不同编码器设置独立的并行参数(默认示例为全DP配置):

.. code:: json

    {
      "image_encoder": {
        "vision_encoder":{
          ...,
          "pipeline_num_layers": [32],
          ...
        },
        "tp": 1,
        "pp": 1, # 当前只支持pp为1
        "cp": 1 
      },
      "audio_decoder": {
        "audio_decoder":{
          ...,
          "pipeline_num_layers": [32],
          ...
        },
        "tp": 1,
        "pp": 1, # 当前只支持pp为1
        "cp": 1 
      }
    }

6.4 编码器超量计算
:::::::::::::::::::::

当前支持在text decoder开启PP后,在保证相同GBS下,编码器使用大MBS,text decoder使用小MBS,通过缓冲区暂存特征数据,实现不同模块的高效计算,充分利用计算资源。

开启方式:

.. code:: bash

    --hetero-encoder-mbs-scale 4 # encoder mbs 为 decoder 的4倍

7. 技术优势
----------------

1. **资源利用率优化**:根据不同模块的计算特性选择最优并行策略
2. **灵活的扩展性**:支持为新模块添加独立的并行配置
3. **性能提升**:通过异构并行和流水线优化,提高多模态模型的训练效率
4. **兼容性好**:与现有并行框架无缝集成,易于使用
5. **场景收益**:特性在Qwen 2.5 Omni模型,典型场景下端到端性能收益20%以上(与整网共并行策略相比)。

8. 应用场景
---------------

- **大规模多模态模型训练**:当前特性已支持Qwen 2.5 Omni模型
- **资源受限环境**:需要灵活分配计算资源的场景
- **性能优化**:针对特定模块进行并行策略优化