数据并行(DP)

思想:将模型复制到多个GPU上;在每个GPU上,对不同的micro batches执行前向/反向传播。

  1. 准备:在每个 GPU 上都拷贝一份完整的模型参数,将一个数据 batch 均分为多个 micro-batch,分别拷贝到不同 GPU 上(DP 分片);
  2. 前向传播/反向传播:每个 GPU 上对不同的 micro-batch 分别执行前向/反向传播,计算得到梯度 G;
  3. 聚合并下发梯度(All-Reduce): 每个 GPU 将本地梯度 G 发送给单个 GPU 做聚合(或者在不同的 GPU 上聚合梯度的某些部分);再将平均后的梯度下发到所有 GPU;
  4. 本地参数更新:所有 GPU 利用下发的梯度完成本地参数更新。

实现 DP 的经典框架为参数服务器:计算 GPU 称为 Worker,梯度聚合 GPU 称为 Server

  1. Worker 和 Server 是逻辑概念,分别可以对应一块或多块物理意义上的 GPU(当前讨论的基本都是对应一张 GPU 的情况);
  2. Server 可以只做梯度聚合,也可以梯度聚合+全量参数更新一起做(当前讨论前一种情况);
  3. 实际应用中为减少通讯量,选择一个 Worker 同时作为 Server。

由第2点出发:能否将梯度聚合分布在多个 GPU 上完成呢?由此引出 Ring-AllReduce 算法。

由第3点出发:如果只选择一个 Worker 作为 Server,多机场景中 Server 将成为性能瓶颈。对于\(m\)个 Worker,将所有梯度发送到Server 需要\(O(m)\)时间;如果将 Server 数量增加到\(n\),每个 Server 只需要存储\(O(\frac{1}{n})\)个参数,总时间优化为\(O(\frac{m}{n})\).

受到通信负载不均的影响,DP一般用于单机多卡;而上述两种优化都利于将训练拓展到多机场景。

分布式数据并行(DDP)

Ring-AllReduce(环同步)

考虑下图中的4路 GPU 服务器: 假设所有梯度共需160 MB。

  1. CPU 作为参数服务器:每个GPU通过 PCIe 链路连接到主机CPU,该链路最多只能以16GB/s的速度运行。速度太慢。
  • 每个 GPU 具有6个 NVLink 连接,每个NVLink连接都能够以300Gbit/s进行双向传输;相当于每个链路每个方向约300/8/2=18GB/s。简言之,聚合的 NVLink 带宽明显高于 PCIe带宽,采用 GPU 作为参数服务器更佳。
  1. 单个 GPU 作为参数服务器:将其中3个 GPU 的梯度发送到第4个 GPU 上需要30毫秒(每次传输需要10毫秒=160MB/16GB/s)。再加上30毫秒将权重向量传输回来,总共需要60毫秒;
  2. 参数服务器分布在所有 GPU 上:将梯度分为4个部分,每个部分为40 MB,现在可以在不同的 GPU 上同时聚合每个部分。因为 PCIe 交换机在所有链路之间提供全带宽操作,所以传输需要2.5*3=7.5毫秒,(而不是30毫秒),同步操作总共需要15毫秒。

研究结果表明最优的同步策略是将网络分解成两个环,并基于两个环直接同步数据。

Ring-AllReduce 同步方法如下:给定由\(n\)个计算节点(或GPU)组成的一个环,每个节点向其左邻居发送部分梯度,直到在其右邻居中找到聚合的梯度。将梯度分为\(n\)个块,并从节点\(i\)开始同步块\(i\)。因为每个块的大小是\(\frac{1}{n}\),所以总时间是\(\frac{n-1}{n}\approx 1\)。即:增大环的大小时,聚合梯度所花费的时间不会增加

理论时间在实践中通常会差一些,因为深度学习框架无法将通信组合成大的突发传输。

Ring-AllReduce 通信量分析

假设模型参数 W 的大小为 \(\psi\),GPU 个数为 \(N\)。则梯度 G 总大小为\(\psi\),每个梯度块的大小为\(\frac{\psi}{N}\)。对于单个 GPU(只计算发送通信量):

  1. Reduce-Scatter 阶段:通信量为\((N-1)\frac{\psi}{N}\)
  2. All-Gather 阶段:通信量为\((N-1)\frac{\psi}{N}\)

即单个 GPU 总的发送通信量为\(2(N-1)\frac{\psi}{N}\)。随着\(N\)的增大,可以近似为\(2\psi\);所有 GPU 的总通信量为 \(2N\psi\)

由第3点出发考虑通信开销:Server 需要和每一个 Worker 完成梯度传输;当 Server 和 Worker 不在同一台机器上时,Server 的带宽将成为计算效率瓶颈。由此引出分布式数据并行(DDP)。

如果所有梯度发送到单个 GPU 上做梯度聚合,该 GPU 承载的通信量为\(N\psi\),其他 GPU 承载的通信量为 \(N\psi\),所有 GPU 的总通信量依然为\(2\psi\)Ring-AllReduce 不改变总通信量,但将通信量均分到了每一时刻的每个 GPU 上,解决了通信负载不均的问题。

参考

图解大模型训练之:数据并行上篇(DP, DDP与ZeRO)

12.7. 参数服务器

一文搞懂MPI通信接口的特点及原理