【大模型训练】zero1 与zero2
它在ZeRO-1的基础上增加了梯度分区(Gradient Partitioning, P_g)。其核心思想是用Reduce-Scatter替代All-Reduce,确保每个GPU只收到它需要的梯度分区,从而实现显存和通信的双重优化。 什么意思,举例说明
好的,我们来用一个非常直观的例子来详细解释这句话的含义,特别是显存和通信的双重优化。
“它在ZeRO-1的基础上增加了梯度分区(Gradient Partitioning, P_g)。其核心思想是用
Reduce-Scatter替代All-Reduce,确保每个GPU只收到它需要的梯度分区,从而实现显存和通信的双重优化。”
场景设置
- GPU数量: 4个 (GPU 0, 1, 2, 3)
- 总梯度: 假设我们有一个巨大的、扁平化的梯度张量
G_full,它有 400MB。 - 分区: 每个 GPU 负责更新其中 100MB 的参数,因此也只需要 100MB 的梯度。
- GPU 0 需要
G_part_0(前100MB) - GPU 1 需要
G_part_1(第二个100MB) - GPU 2 需要
G_part_2(第三个100MB) - GPU 3 需要
G_part_3(最后100MB)
- GPU 0 需要
backward()之后: 每个 GPU 都计算出了一个基于本地数据的、完整的、400MB 的梯度。我们称之为g0,g1,g2,g3。- 目标: 计算全局平均梯度
G_avg = (g0 + g1 + g2 + g3) / 4,并将正确的分区发送给对应的 GPU。
1. ZeRO-1 的方式:使用 All-Reduce
All-Reduce 操作可以被看作是两步的结合:Reduce + Broadcast。
步骤 1: Reduce (归约/求和)
- 所有 GPU 将它们的本地梯度 (
g0,g1,g2,g3) 发送到一个集合点(逻辑上的)。 - 通信系统将它们相加,得到一个 400MB 的总梯度
G_sum = g0 + g1 + g2 + g3。 - 通信量: 每个 GPU 发送 400MB。总发送量 =
4 * 400MB = 1600MB。
步骤 2: Broadcast (广播)
- 现在,这个 400MB 的
G_sum需要被分发回每一个 GPU。 - 通信量: 系统需要将 400MB 的数据发送给 4 个 GPU。总接收量 =
4 * 400MB = 1600MB。
All-Reduce 结束后的状态:
- 每个 GPU (0, 1, 2, 3) 都拥有了一份完整的、400MB 的
G_sum(或G_avg,如果除以了4)。
ZeRO-1 的问题:
- 显存浪费: GPU 0 只需要前 100MB 的梯度来更新参数,但它却被迫在显存中存储了完整的 400MB 梯度。浪费了 300MB 的显存。
- 通信浪费:
Broadcast阶段向 GPU 0 发送了完整的 400MB 数据,但其中 300MB 是它根本用不上的。通信带宽被浪费在了传输这些“无用”数据上。
2. ZeRO-2 的方式:使用 Reduce-Scatter
Reduce-Scatter 操作非常聪明,它将 Reduce 和 Scatter (散发,一种定向广播) 结合在一起,省去了中间那个完整的广播步骤。
只有一个步骤: Reduce-Scatter
- 所有 GPU 将它们的本地梯度 (
g0,g1,g2,g3) 发送出去。 - 通信系统在后台进行求和,得到临时的
G_sum。 - 关键区别: 系统不会将完整的
G_sum广播回去。相反,它会立即切分G_sum,并只将每个 GPU 需要的那一部分发送给它。- 将
G_sum的前 100MB (G_part_0) 发送给 GPU 0。 - 将
G_sum的第二个 100MB (G_part_1) 发送给 GPU 1。 - … 以此类推。
- 将
- 通信量: 从每个 GPU 的角度看,它发送了 400MB,但只接收了 100MB。总通信量大约与一个
Reduce操作相当,比All-Reduce几乎少了一半。
Reduce-Scatter 结束后的状态:
- GPU 0 只拥有 100MB 的
G_part_0。 - GPU 1 只拥有 100MB 的
G_part_1。 - … 以此类推。
ZeRO-2 的优势 (双重优化):
-
显存优化:
- 在梯度同步完成后,GPU 0 的显存中只需要存储 100MB 的梯度分区,而不是像 ZeRO-1 那样存储完整的 400MB。
- 直接节省了 300MB 的梯度存储显存。
-
通信优化:
Reduce-Scatter的总通信量大约是All-Reduce的一半。因为它省去了向所有 GPU 广播完整梯度张量的步骤。- 网络带宽被更有效地利用,只传输每个 GPU “真正需要” 的数据。
总结:一个生动的比喻
想象一个四人小组合作写一本书,书有四章。每个人都写了所有四章的草稿。
-
All-Reduce(ZeRO-1 的方式):- 收集和合并 (Reduce): 四个人把他们各自写的四章草稿全部交给一个编辑。编辑把所有版本合并,形成一本包含四章最终稿的书。
- 复印和分发 (Broadcast): 编辑把这本完整的、包含四章最终稿的书,复印四份,然后每人发一本完整的。
- 问题: 张三只需要负责校对第一章,但他却收到了一整本书,浪费了纸张(显存),也浪费了分发时的时间和精力(通信带宽)。
-
Reduce-Scatter(ZeRO-2 的方式):- 收集、合并和分拆 (Reduce-Scatter): 四个人还是把所有草稿都交给编辑。编辑合并成最终稿后,他没有复印整本书。
- 相反,他把书拆开,只把第一章发给张三,第二章发给李四…
- 优势: 张三只收到了他需要的第一章,不浪费纸张(显存优化)。编辑在分发时也只递送了必要的章节,节省了时间和精力(通信优化)。
这就是 Reduce-Scatter 替代 All-Reduce 实现显存和通信双重优化的直观解释。它确保了每个工作单元(GPU)只处理和存储它所必需的数据,从而大大提升了大规模分布式训练的效率。
