跳转至

数据并行(Data Parallel)

背景知识
  • 反向传播:通过链式法则计算梯度,更新模型参数的核心算法
  • All-Reduce:所有 GPU 的梯度求和并分发的集合通信操作 → 详见
  • 流水线并行:模型按层切分,微批次流水线流动 → 详见

核心问题:如何用更多 GPU 加速训练?

数据并行(Data Parallel, DP)是最直观的并行策略:每个 GPU 处理不同 batch 的数据,同步梯度后更新参数。这样可以线性扩展训练速度(理想情况下)。

基本思想: - 每个 GPU 有完整模型副本 - 每个 GPU 处理不同 batch 的数据 - 所有 GPU 的梯度同步(All-Reduce) - 每个 GPU 独立更新参数(梯度相同,更新结果一致)


数据并行的工作流程

初始化:
GPU 0: 模型副本 A(从 rank 0 广播初始状态)
GPU 1: 模型副本 B(从 rank 0 广播初始状态)
GPU 2: 模型副本 C(从 rank 0 广播初始状态)
GPU 3: 模型副本 D(从 rank 0 广播初始状态)

训练循环:
Step 1:
GPU 0: 处理 batch 1 → 梯度 g1
GPU 1: 处理 batch 2 → 梯度 g2
GPU 2: 处理 batch 3 → 梯度 g3
GPU 3: 处理 batch 4 → 梯度 g4

Step 2: 梯度同步(All-Reduce)
所有 GPU 的梯度求和并分发:
g_avg = (g1 + g2 + g3 + g4) / 4
每个 GPU 都得到 g_avg

Step 3: 参数更新
GPU 0: 用 g_avg 更新模型副本 A
GPU 1: 用 g_avg 更新模型副本 B
GPU 2: 用 g_avg 更新模型副本 C
GPU 3: 用 g_avg 更新模型副本 D

为什么每个 GPU 独立更新参数: - All-Reduce 后所有 GPU 的梯度相同(已平均) - 每个进程独立执行优化器逻辑,灵活性更高 - 不需要参数广播步骤,减少通信开销


核心挑战

通信开销

数据并行的核心瓶颈是梯度同步。每次反向传播后需要 All-Reduce 所有 GPU 的梯度:

通信时间 = 梯度大小 / 通信带宽

问题: - 模型越大,梯度越大,通信时间越长 - GPU 越多,通信拓扑越复杂,延迟越高 - 如果通信时间超过计算时间,GPU 会空闲等待


性能优化技术

梯度分桶(Gradient Bucketing)

问题:逐参数同步梯度会导致频繁的小消息通信,带宽利用率低。

解决方案:将多个参数的梯度组织到一个 bucket 中,按 bucket 同步。

参数:[p1, p2, p3, p4, p5, p6, p7, p8]
梯度:[g1, g2, g3, g4, g5, g6, g7, g8]

Bucket 0: [g1, g2, g3, g4](25MB)
Bucket 1: [g5, g6, g7, g8](25MB)

同步时按 bucket All-Reduce,而非逐参数同步

权衡: - Bucket 越大:通信次数越少,带宽利用率越高,但通信延迟越长 - Bucket 越小:通信延迟越短,但通信次数越多,带宽利用率低

默认配置:25MB bucket,在延迟和带宽之间取得平衡。

梯度计算与梯度通信重叠

问题:如果等待所有梯度计算完毕后再同步,GPU 会空闲等待通信完成。

解决方案: 在反向传播过程中,当某个 bucket 的梯度都准备好时,立即触发异步 All-Reduce,GPU 继续计算其他层的梯度。

时间 →
GPU 0: 计算梯度 g1, g2, g3, g4(bucket 0 就绪)→ 触发异步 All-Reduce → 继续计算 g5, g6, g7, g8
GPU 1: 计算梯度 g1, g2, g3, g4(bucket 0 就绪)→ 触发异步 All-Reduce → 继续计算 g5, g6, g7, g8

注意:这是 DP 特有的"梯度计算与梯度通信重叠",与 PP 的"不同阶段计算重叠"不同

效果:通信时间被计算时间掩盖,整体训练速度提升。

梯度累积

问题:小 batch size 导致梯度噪声大,但增大 batch size 会超出显存。

解决方案: - 多次小 batch 的梯度累积到同一个 bucket - 累积到目标次数后再同步,减少通信频率

目标 batch size = 256
单卡 batch size = 32
累积步数 = 256 / 32 = 8

前 7 步:梯度累积到 bucket,不同步
第 8 步:触发 All-Reduce,更新参数

权衡: - 减少通信次数,但增加显存占用(需要保存累积梯度)

梯度压缩

问题:梯度通信量大,成为瓶颈。

解决方案: - 在 All-Reduce 前对梯度进行压缩(如 PowerSGD 低秩分解) - 通信后再解压缩

权衡: - 减少通信量,但增加计算开销(压缩/解压缩)


常见实现

PyTorch DDP(DistributedDataParallel)

特点: - 进程级并行(每个进程对应一个 GPU) - 使用 NCCL/Gloo 后端进行通信 - 自动梯度分桶和计算通信重叠 - 通过 autograd hook 拦截梯度计算

适用场景:模型能放入单卡显存,需要加速训练。

技术细节:详见 PyTorch 官方文档1

FSDP(Fully Sharded Data Parallel)

特点: - 参数、梯度、优化器状态都分片到不同 GPU - 动态重分片参数(需要时才拉取) - 显存占用远低于 DDP

适用场景:超大模型,单卡放不下。

ZeRO(Zero Redundancy Optimizer)

特点: - DeepSpeed 提出的优化器状态分片策略 - 三个阶段:分片优化器状态(ZeRO-1)、分片梯度(ZeRO-2)、分片参数(ZeRO-3) - ZeRO-3 等价于 FSDP

适用场景:超大模型,单卡放不下。


与其他并行方式的对比

并行策略 模型切分 数据切分 显存占用 通信内容 适用场景
数据并行(DP) 完整副本 不同 batch 梯度同步 模型能放入单卡
流水线并行(PP) 按层切分 微批次流水线 激活值传递 模型层数多
张量并行(TP) 层内切分 相同 batch 中间结果 注意力矩阵大
混合并行(3D) DP + PP + TP 组合策略 可调 多种通信 超大规模模型

常见问题

为什么数据并行比模型并行更常用?

数据并行实现简单,通信模式固定(All-Reduce),容易优化。模型并行需要根据模型架构设计切分策略,通信模式复杂。

什么时候使用数据并行?

  • 模型能放入单卡显存
  • 需要加速训练(更多 GPU)
  • batch size 可以线性扩展

什么时候需要其他并行策略?

  • 模型太大,单卡放不下(使用 PP 或 TP)
  • batch size 受限,无法线性扩展(使用 PP 或 TP)
  • 通信带宽受限(使用 PP 或 TP 减少通信量)

参考资料


  1. Li et al. PyTorch Distributed: Experiences on Accelerating Data Parallel Training. 2020. https://arxiv.org/pdf/2006.15704