ZeRO / DeepSpeed
系统 · SC 2020 · arXiv 1910.02054v3

ZeRO:不复制模型状态,靠三处分片把 1T 模型摊到 1024 张卡

数据并行最浪费的地方不是算力,而是每张卡都存一份完整的优化器状态、梯度和参数。 ZeRO 把这三类状态依次切开:Stage 1 分优化器状态,Stage 2 再分梯度,Stage 3 连参数也分。 Stage 3 的显存按 Nd 下降,代价从 Stage 3 开始显现:论文算出每步通信量是 baseline DP 的 1.5 倍。

Samyam Rajbhandari, Jeff Rasley, Olatunji Ruwase, Yuxiong He · Microsoft arXiv 1910.02054v3 ↗ DeepSpeed @ f47dc3e ↗
TL;DR · 30 秒抓住核心

混合精度 Adam 下,每个参数要背 16 字节。 2Ψ 是 fp16 参数,2Ψ 是 fp16 梯度,剩下 12Ψ 是优化器状态:4Ψ fp32 主权重、4Ψ momentum、4Ψ variance(§3.1)。 1.5B 的 GPT-2 因此要 24 GB 才能放下模型状态,而 fp16 权重本身只有 3 GB。

ZeRO 不换优化器,也不改模型,只改“谁存哪一份”。 Stage 1 把 12Ψ 的优化器状态切成 Nd 份,每卡只留 1/Nd; Stage 2 再把梯度切成 1/Nd;Stage 3 把参数也切成 1/Nd,用到哪层再临时 all-gather 回来。 模型状态从 16Ψ 一路降到 16Ψ/Nd。

省显存不是免费的。 论文的通信账里,Stage 1/2 与 baseline DP 同为 2Ψ;Stage 3 需要前向、反向各一次参数 all-gather,再加一次梯度 reduce-scatter, 总计 3Ψ,是 baseline 的 1.5 倍(§1、§7.2.1、§7.2.2)。DeepSpeed 当前实现还在这条主线之外加了 CPU/NVMe offload、prefetch 与 bucket 化通信。

最多 4× · 8× · Nd×
论文口径(混合精度 Adam):N_d 足够大时,Stage 1/2/3 每卡模型状态相对 baseline DP 的压缩上限
1.5×
Stage 3 每步通信量 / baseline DP:参数 gather 2Ψ + 梯度 reduce-scatter Ψ = 3Ψ
15.6 GB
1T 模型、N_d=1024 时每卡模型状态(Stage 3,混合精度 Adam)
01

显存不是被权重吃掉的,而是被每个参数的三份账本吃掉的

先建立一笔账:混合精度 Adam 下,一个参数在每张卡上要占 16 字节,其中权重只占 2 字节。

论文把训练显存分成两类:model states(优化器状态、梯度、参数)和 residual states(activations、临时 buffer、碎片)。大模型训练里,前者是主项。 Adam 在混合精度训练中除了 fp16 参数和梯度,还要保留 fp32 主权重、一阶矩 momentum 和二阶矩 variance(§3.1):

16Ψ = 2Ψ fp16 参数 + 2Ψ fp16 梯度 + 12Ψ 优化器状态
K=12:4Ψ fp32 主权重 + 4Ψ momentum + 4Ψ variance;Ψ 为参数个数,单位为字节(§3.1,混合精度 Adam)
2Ψ

参数

前向和反向都要用的 fp16 权重。一张卡上没有它,这一步就算不了。

2Ψ

梯度

反向传播逐层产生;baseline DP 要对所有参数做一次全量 all-reduce。

12Ψ

优化器状态

fp32 主权重、momentum、variance 各 4Ψ。Adam 的“记忆”比权重本身贵 6 倍。

1.5B 参数的 GPT-2,按 16Ψ 计算至少需要 24 GB 模型状态;而只存 fp16 权重只要 3 GB。 论文指出,这样的模型在 32 GB 的 V100 上仍然训练不了,因为 residual states 还要占地方。 1.5B GPT-2 在 seq 1024、batch 32 下,activations 约 60 GB;activation checkpointing 能把它压到约 8 GB,代价是 33% 的重算;此外还有约 6 GB 的 fp32 梯度融合 buffer。 碎片也会让显存在“30% 以上仍空闲”时分配失败(§3.2)。

问题的核心于是变成:能不能让每张卡只存模型状态的一部分,同时保留 DP 的计算粒度和通信效率? baseline DP 的做法是每张卡都存完整 16Ψ,再用 all-reduce 同步梯度;模型越大,这份冗余越贵。

02

模型并行省显存,但把计算切碎、把通信变贵

理解 ZeRO 的取舍,先看它没有选的两条路:模型并行和流水线并行。

模型并行(MP)把每一层的参数和计算竖着切到多张卡。显存是省的,但计算粒度变细,层间还要频繁通信。 论文实测 40B 参数模型跨两个 DGX-2 节点跑 Megatron-LM,每张 V100 只有约 5 TFLOPs,不到硬件峰值的 5%。 跨节点带宽是硬约束:节点内 NVSwitch 每链路约 300 GB/s,跨节点 InfiniBand EDR 只有 12.5 GB/s。

流水线并行(PP)按层横着切,用 micro-batch 填 pipeline bubble。 它能省显存,但论文指出 PP 在 tied-weights、batch-normalization 等功能上难实现, 而且需要与流水线段数成比例的 batch size 来遮住 bubble,可能影响收敛(§2.1)。

CPU offload把状态搬到 CPU。论文引用的前作里,GPU-CPU-GPU 传输最多能吃掉 50% 的训练时间(§2.2.2)。 ZeRO 的判断是:DP 本身有更高的计算粒度、更低的通信量,问题只是“每张卡都存了完整状态”; 与其换并行方式,不如把 DP 的状态分片,同时保留 DP 的调度。

ZeRO 的赌注

不是“少算”,而是“少存”

ZeRO-DP 不改变优化器、不改变模型结构,也不改变每步的数学结果;它只改变三件事的存放位置: 优化器状态、梯度、参数。分片之后,每个 rank 仍然按 DP 的方式算自己那份数据, 只在需要时通过 all-gather / reduce-scatter 把缺的部分补齐。

03

三次切分:先优化器状态,再梯度,最后参数

三个阶段是累积的。每多切一类状态,每卡显存再降一档,但参数的“临时拼回”也更频繁。

记模型有 Ψ 个参数、DP 度为 Nd、优化器状态乘数为 K(混合精度 Adam 为 12)。baseline DP 的每卡模型状态是 16Ψ。 三个阶段的公式直接来自 §5.1–§5.3:

Stage 1(Pos):4Ψ + 12Ψ / Nd
Stage 2(Pos+g):2Ψ + 14Ψ / Nd
Stage 3(Pos+g+p):16Ψ / Nd
每卡模型状态字节数;4Ψ = 2Ψ 参数 + 2Ψ 梯度,K=12 时 12Ψ 为优化器状态;不含 residual states
Stage 1

只分优化器状态

每个 rank 只存 1/N_d 的 optimizer state,也只更新 1/N_d 的参数;step 末尾把更新后的参数 all-gather 回全量。梯度仍是全量。

Stage 2

再分梯度

梯度在反向时按 owner 做 reduce-scatter,每个 rank 只保留自己负责的那 1/N_d;参数仍全量,step 后 all-gather 更新。

Stage 3

参数也分

每个 rank 只常驻 1/N_d 参数。做某一层前向/反向之前,把该层参数 all-gather 成临时全量,算完立刻释放。

把 16Ψ 拆开看,三个阶段的“地板”很清楚:Stage 1 至少留下 4Ψ(参数+梯度),Stage 2 至少留下 2Ψ(参数), 只有 Stage 3 能把模型状态降到 16Ψ/Nd。这也是为什么论文说 Stage 3 让 DP 能训练“任意大小”的模型—— 前提是有足够多的卡来分摊(§5.3–§5.4)。

8 → 1024,按 2 的幂取
参数
15 GB
2Ψ 常驻
梯度
15 GB
2Ψ 常驻
优化器状态
90 GB
12Ψ 常驻
每卡模型状态显存
120 GB / 卡 · baseline
参数 15 GB · 梯度 15 GB · 优化器状态 90 GB
每步通信量(论文口径)
30 GB / step · baseline 2Ψ
baseline DP:梯度 all-reduce 2Ψ。

推断 精度切到 fp32、优化器切到 SGD 时,页面按同一字节记账替换 b 与 K;论文 Table 1 只给出混合精度 Adam(K=12)的组合, 这些扩展组合不在表内。默认组合(fp16/bf16 混合 + Adam)直接取自 §5.1–§5.3 的公式。 压缩比按所选精度/优化器的 Stage 0 记账:baseline = (2b + K)Ψ,b 为参数字节(混合精度 2、fp32 4), K 为优化器状态乘数(Adam 8、SGD+m 4、SGD 0;混合精度另加 4 字节 fp32 主权重)。 本模拟只算 model states,未覆盖 activations、临时 buffer、碎片,也未模拟通信 overlap 与网络拓扑。

手算对账:两组参数如何得到读数 与上方交互同口径

显示规则:≥100 GB 取整;10–100 GB 最多保留 1 位小数;<10 GB 最多保留 2 位小数; 整数不补零(例如 31.0 显示为 31,1.50 显示为 1.5)。GB 按 109 字节,不按 GiB。 分项先算后舍入,总计按未舍入值算,所以分项显示值相加可能与总计差 0.01–0.04。

A. Ψ = 7.5B,N_d = 64,fp16/bf16 混合,Adam(b=2,K=12;公式 §5.1–§5.3)

Stage 1:4×7.5 + 12×7.5/64 = 30 + 1.40625 = 31.40625 → 31.4 GB

Stage 2:2×7.5 + 14×7.5/64 = 15 + 1.640625 = 16.640625 → 16.6 GB

Stage 3:16×7.5/64 = 1.875 → 1.88 GB

通信:baseline / Stage 1 / Stage 2 为 2×7.5×2 = 30 GB;Stage 3 为 3×7.5×2 = 45 GB。

Stage参数梯度优化器状态总计通信(论文)Stage 1(含 step 末 gather)
015159012030—
115151.4131.43045
2150.231.4116.630—
30.230.231.411.8845—

单位 GB。Stage 1 的 45 GB 按论文 §5.1 / DeepSpeed 实现里 step 末的一次参数 all-gather 计入(+Ψ);论文 §1、§7 的总账对 Stage 1 记 30 GB(见 04 节)。

B. Ψ = 128B,N_d = 64,fp16/bf16 混合,Adam(b=2,K=12;公式 §5.1–§5.3)

Stage 1:4×128 + 12×128/64 = 512 + 24 = 536 GB

Stage 2:2×128 + 14×128/64 = 256 + 28 = 284 GB

Stage 3:16×128/64 = 32 GB

通信:baseline / Stage 1 / Stage 2 为 2×128×2 = 512 GB;Stage 3 为 3×128×2 = 768 GB。

Stage参数梯度优化器状态总计通信(论文)Stage 1(含 step 末 gather)
025625615362048512—
125625624536512768
2256424284512—
3442432768—

A、B 两组的 Stage 1/2/3 总计分别对应 Table 1 的 7.5B 行与 128B 行(31.4 / 16.6 / 1.88,536 / 284 / 32),可逐项对照。

C. 扩展组合(论文 Table 1 未覆盖;按同一记账外推)

记账口径:b 为参数字节(混合精度 2、fp32 4),K 为优化器状态乘数 (Adam 8、SGD+m 4、SGD 0;混合精度另加 4 字节 fp32 主权重),Stage 0 = (2b + K)Ψ。

C1. Ψ = 7.5B,N_d = 64,fp32,SGD + momentum(b=4,K=4): Stage 0 (2×4+4)×7.5 = 90 GB; Stage 1 2×4×7.5 + 4×7.5/64 = 60.46875 → 60.5 GB; Stage 2 4×7.5 + (4+4)×7.5/64 = 30.9375 → 30.9 GB; Stage 3 (2×4+4)×7.5/64 = 1.40625 → 1.41 GB; 压缩比 90 / 1.40625 = 64×。

C2. Ψ = 1.5B,N_d = 8,fp32,SGD(b=4,K=0): Stage 0 (2×4+0)×1.5 = 12 GB; Stage 1 2×4×1.5 + 0 = 12 GB; Stage 2 4×1.5 + 4×1.5/8 = 6.75 GB; Stage 3 (2×4+0)×1.5/8 = 1.5 GB; 压缩比 12 / 1.5 = 8×。

C 组不在论文 Table 1 内,是本页按 (2b + K)Ψ 的 Stage 0 记账外推;与交互模拟使用同一公式,显示值可直接对照。

04

论文总账:Stage 1/2 的通信量与 DP 持平,Stage 3 多 50%

分片省了显存,问题是每一步要多搬多少字节。论文把通信量拆成 all-gather 与 reduce-scatter 两笔账。

baseline DP 的梯度同步是一次 all-reduce。按 §7.1 的拆法,all-reduce = reduce-scatter Ψ + all-gather Ψ, 所以每步每卡的数据移动量是 2Ψ。Stage 2 把梯度 all-reduce 换成 reduce-scatter(Ψ), 参数更新后再 all-gather 一次(Ψ),合计仍是 2Ψ,与 baseline 相同(§7.2.1)。

baseline DP:reduce-scatter Ψ + all-gather Ψ = 2Ψ
Stage 3:前向 all-gather Ψ + 反向 all-gather Ψ + 梯度 reduce-scatter Ψ = 3Ψ = 1.5 × 2Ψ
每步每卡的数据移动量;Ψ 为参数个数,通信元素大小取梯度/权重的元素字节数

Stage 3 多出来的那一笔,是参数的第二次 all-gather:前向按层把参数拼回来,反向按逆序再拼一次。 这两次 gather 不必同时存在——某一层算完就可以释放,所以显存不会回到 16Ψ,但通信账上是实打实的 3Ψ。 论文强调这不是“把整个模型同时广播”,而是把 gather 摊到整个前向/反向过程中(§7.2.2)。

论文 §1 对 Pos 的表述是 “4x memory reduction, same communication volume as DP”, §7 的分项通信账也只把 Pos+g 记入 2Ψ、把 Pos+g+p 记入 3Ψ。 但论文 §5.1 在描述 Pos 时,自己写了 optimizer step 末尾的一次参数 all-gather; DeepSpeed 的实现(all_gather_dp_groups)与 §5.1 的流程一致。 若按字节把这次 gather 计入,Stage 1 是 3Ψ;§1 与 §7 的总账仍记 2Ψ。三处来源并列如下,本页不替原文裁决。

论文 §1 + §7

Stage 1/2 总账不增加通信

§1 与 §7 的总账:Pos、Pos+g 都不增加通信量;只有 Pp 额外做参数 gather,总计 1.5×。

论文 §5.1 / DeepSpeed

Stage 1 在 step 末再 gather 一次参数

§5.1 与 stage_1_and_2.py:3180 都在 optimizer step 末尾调用参数 all-gather。Stage 2 用 reduce-scatter Ψ + 参数 all-gather Ψ 凑成 2Ψ;Stage 1 的梯度 all-reduce 已经占满 2Ψ,这次 gather 是额外的 Ψ。

实现层面,DeepSpeed 把这些 collective 做成 bucket 化、可 overlap 的调用: Stage 2 的梯度按 partition 归并后 reduce-scatter;Stage 3 的梯度用 reduce_scatter_coalesced(deepspeed/runtime/zero/stage3.py:1948), 参数 gather 则走 deepspeed/runtime/zero/partition_parameters.py:1723 的 _all_gather, 由 coordinator 在每层 forward 前触发(deepspeed/runtime/zero/partitioned_param_coordinator.py:310)。

05

Stage 3 把参数变成按层流动的临时副本

前向用到哪层,就把哪层的参数 all-gather 回来;算完立刻释放。反向按逆序再来一遍。

ZeRO 三个阶段的分片对象对比:Stage 1 只分优化器状态,Stage 2 再分梯度,Stage 3 连参数也分 Stage 1 · P_os 只切优化器状态 优化器状态 12Ψ 1/N 梯度 2Ψ 每卡 1 份 参数 2Ψ 每卡 1 份 每卡 4Ψ + 12Ψ/N §5.1 / 实现:step 末 gather Ψ §1 / §7 总账:2Ψ Stage 2 · P_os+g 再切梯度 优化器状态 12Ψ 1/N 梯度 2Ψ 1/N 参数 2Ψ 每卡 1 份 每卡 2Ψ + 14Ψ/N 梯度 reduce-scatter Ψ 参数 all-gather Ψ → 2Ψ Stage 3 · P_os+g+p 参数也切 优化器状态 12Ψ 1/N 梯度 2Ψ 1/N 参数 2Ψ 1/N 每卡 16Ψ/N 前向 gather Ψ + 反向 gather Ψ 梯度 reduce-scatter Ψ → 3Ψ 本卡持有的 1/N 分片 其他卡的分片(不常驻) 每卡全量复制
三个阶段的分片对象对比。图中画 4 个分片表示 1/N;本页交互的 N_d 取 8–1024(Table 1 另含 N_d=1/4)。 公式来自 §5.1–§5.3;Stage 1 的 2Ψ 是论文 §1/§7 的总账;论文 §5.1 与 DeepSpeed 实现在 step 末都描述了一次参数 all-gather(见 04 节)。
前向

按层 gather

fetch_sub_module 在子模块 forward 前触发参数 all-gather (partitioned_param_coordinator.py:310),并把后续几层放进 prefetch 队列(同文件 452–484 行)。

反向

逆序再 gather,梯度归 owner

参数按反向顺序再次 all-gather;梯度按 partition 归并后 reduce-scatter (stage3.py:1948),每个 rank 只保留自己负责的梯度分片。

更新

只动本地 1/N

optimizer step 只更新本地 fp32 参数分片与优化器状态(stage3.py:2913); 下一步 forward 需要别的层时再 gather。常驻参数上限由 stage3_max_live_parameters 控制(默认 109 个元素,deepspeed/runtime/zero/config.py:245)。

源码锚点(DeepSpeed f47dc3e):deepspeed/runtime/zero/stage3.py:152(Stage 3 类)、 :1282(_partitioned_params_swap_out,把用完的参数换出)、 :1948(reduce_scatter_coalesced); deepspeed/runtime/zero/partition_parameters.py:1723(_all_gather)、 :1765(_partition)、:1778(_partition_param 释放数据)。

06

显存随 N_d 下降,但 Stage 1/2 有地板

Table 1 是每卡模型状态的解析结果,Table 2 把“理论最大模型”与“实测能跑的模型”放在一起。

Table 1 · 不同 ZeRO 阶段与 DP 度下每卡模型状态显存(GB,十进制;混合精度 Adam,K=12;不含 residual states)
N_d 7.5B 128B 1T
P_osP_os+gP_os+g+p P_osP_os+gP_os+g+p P_osP_os+gP_os+g+p
1120120120204820482048160001600016000
452.541.330896704512700055004000
1635.621.67.5608368128475028751000
6431.416.61.885362843241872218250
25630.415.40.4751826384046205462.5
102430.115.10.1251325724011201315.6

Table 1 已完整转录。原文用加粗标出能装入 32 GB V100 的组合,本页未复刻加粗,可按数值读出。

N_d=64 时,7.5B 模型的 Stage 1 是 31.4 GB,刚好在 32 GB 以内;Stage 2 降到 16.6 GB,Stage 3 只要 1.88 GB。 同样 64 张卡,128B 模型只有 Stage 3 的 32 GB 能装下;Stage 1/2 分别是 536 GB 与 284 GB。 1T 模型更极端:N_d=1024 时 Stage 3 每卡 15.6 GB,Stage 1/2 仍有 4011 GB 与 2013 GB。 这不是线性缩放失效,而是 Stage 1 的 4Ψ 与 Stage 2 的 2Ψ 地板在模型变大时先撞墙。

Table 2 · 理论最大模型规模(左)与实测模型规模(右);实测只覆盖 ZeRO-OS(P_os)
MPGPUs Max Theoretical Model Size Measured Model Size
BaselineP_osP_os+gP_os+g+pBaselineZeRO-DP (P_os)
1642B7.6B14.4B128B1.3B6.2B
21284B15.2B28.8B256B2.5B12.5B
42568B30.4B57.6B0.5T5B25B
851216B60.8B115.2B1T10B50B
16102432B121.6B230.4B2T20B100B

Table 2 已完整转录。条件是按 MP 度 × DP 度的组合;P_os 的实测值与理论最大值吻合,说明内存分析可作为现实上界。

论文的实现叫 ZeRO-100B,只覆盖 Pos+g + ZeRO-R,不是完整 Stage 3(§10)。 它能在 400 张 V100 上跑 170B 参数,8B–100B 区间平均 15 PFLOPs,超过峰值 30%, 相对基线最多 10× 加速;不加 MP 时,128 张卡能跑 13B 参数,平均每卡超过 40 TFLOPs,而 baseline DP 最大只能跑 1.4B、每卡不到 20 TFLOPs。 Table 1 的 1T 行是解析结果,不是 1T 实测;Table 2 的实测列也只到 P_os 的 100B。把这两类数字分开看,才不会把“能装下”误读成“已经训练过”。

07

它省的是模型状态,省不了激活、碎片和物理通信时间

把 ZeRO 放回完整训练系统里看:它解决了显存的主项,但剩下的项和实现代价仍然决定能不能跑、跑得多快。

不解决

residual states 仍在

Stage 1–3 只切优化器状态、梯度、参数。activations、临时 buffer、碎片仍要占显存; 论文用 ZeRO-R 的 Pa、CB、MD 处理这些项(§6),但它们不在本页的 16Ψ 账里。 显存计算器给出的数字只是 model states 的下界。

不保证

1.5× 通信不等于 1.5× 墙钟

3Ψ 是字节账。实际时间取决于 bucket 大小、prefetch 是否盖住 gather、跨节点带宽和 batch size。 论文说 Stage 3 的 gather 可以摊到整个前向/反向(§7.2.2),但没承诺在所有网络上都能完全 overlap。

Stage 1/2 有显存地板。无论 N_d 多大,Stage 1 至少留 4Ψ、Stage 2 至少留 2Ψ;只有 Stage 3 是 16Ψ/N_d。 1T 模型在 1024 张卡上,Stage 1/2 的每卡仍要 4011 GB / 2013 GB,只有 Stage 3 的 15.6 GB 落在单卡范围。 所以“ZeRO 能训练任意大小模型”这句话,准确说是“Stage 3 在卡足够多时能”。

offload 是另一条轴,不是论文 ZeRO-DP 的默认路径。 论文 §2.2.2 明确说 ZeRO 不把模型状态存到 CPU;只有 ZeRO-R 在少数情况下把 activation checkpoint 放到 CPU(§6.1)。 DeepSpeed 当前实现把 offload 做成配置项:offload_optimizer 支持 Stage 1/2/3, offload_param 只支持 Stage 3(deepspeed/runtime/zero/config.py:164–177), 设备可以是 CPU 或 NVMe(deepspeed/runtime/engine.py:1581–1588;NVMe swap 见 deepspeed/runtime/zero/stage3.py:828)。 这能进一步降显存,但把瓶颈移到 PCIe / NVMe 带宽,和论文的 1.5× 通信账是两笔不同的成本。

对照

FSDP:同一族思路的另一个实现

PyTorch FSDP 文档写明它 “is inspired by Xu et al. as well as the ZeRO Stage 3 from DeepSpeed”。 两者都把参数、梯度、优化器状态分片到 data-parallel rank,并在使用前 all-gather 参数; 差别在实现单位:FSDP 默认把 module 的参数收进内部 FlatParameter 管理, DeepSpeed ZeRO-3 以参数/分区为单位,并配 prefetch 与 stage3_max_live_parameters 限制常驻量。 这是同一设计目标下的两套工程实现,不是同一份代码。

引用:PyTorch docs, FullyShardedDataParallel(“A wrapper for sharding module parameters across data parallel workers. This is inspired by ... the ZeRO Stage 3 from DeepSpeed.”)

表 · 论文与 DeepSpeed 当前实现的差异(来源见每行;非原文表格)
维度论文(arXiv 1910.02054v3)DeepSpeed f47dc3e
Stage 1 通信 §1、§7 总账记 2Ψ(“same communication volume as DP”);§5.1 描述 step 末参数 all-gather 对应 §5.1:step 末 all_gather_dp_groups(stage_1_and_2.py:3180),按字节另计 Ψ
Stage 3 参数驻留 按层 broadcast / discard,gather 摊到前向与反向(§7.2.2) hook + coordinator fetch/prefetch(deepspeed/runtime/zero/partitioned_param_coordinator.py:310,452);stage3_max_live_parameters 默认 109 元素(deepspeed/runtime/zero/config.py:245)
模型状态 offload ZeRO-DP 不把 model states 放 CPU;只有 Pa+cpu 放 activation checkpoint(§2.2.2、§6.1) offload_optimizer 支持 Stage 1/2/3;offload_param 仅 Stage 3;CPU 或 NVMe(deepspeed/runtime/zero/config.py:164–177)
评测范围 ZeRO-100B = Pos+g + ZeRO-R,最高 170B(§10);1T 为解析 实现 Stage 0–3,含 offload、NVMe、prefetch 等工程路径

什么时候别用 Stage 3: