四张卡,为什么既没省四倍内存,也没快四倍?
增加设备之前,先说清拆的是数据、状态,还是模型计算。数据并行让多个副本处理不同样本;状态分片让设备分担原本重复保存的状态;模型并行把一次模型计算分给多台设备。它们解决不同限制,不能把总卡数直接代入一个万能加速公式。
本课用 rank 表示参与分布式计算的进程编号。最简单的教学情形是一卡一个进程,各有相同模型和优化器,取不同样本,计算本地梯度,再交换信息完成同一次更新。通信、同步和较慢进程的等待都进入训练步,故而增加 rank 也可能使一个小模型更慢。
先让两个 rank 更新成同一个参数
两个 rank 从 w=1 开始,样本数量相同、每个本地损失都按样本取平均。假设它们算出的梯度分别为 2 和 6。全体样本的平均梯度是 (2+6)/2=4;学习率 0.1 时,两边都应更新为 0.6。只交换平均损失没有这个效果,必须得到每个参数对应的聚合梯度。
all-reduce 是一种集体通信:把各进程的数组按指定运算归约,再让每个进程得到结果。底层如果执行求和,平均还需要正确的缩放;框架可能已经完成这一步,接入时必须核对约定。DDP(分布式数据并行)组织模型副本间的梯度同步,各副本仍保存模型。它不会自动把输入分成互不重复的数据,也不会自动把一份放不下的模型切成四份。
通信量有下界,步时还要看能否重叠
以理想 ring all-reduce 为例,把总梯度 G 字节分成 R 份。第一阶段经过 R−1 轮交换并归约,每个 rank 得到一份完整归约结果;第二阶段再经过 R−1 轮,把这些结果分发完整。每轮每 rank 发送 G/R,所以总发送量为 2×(R−1)/R×G,接收量同样大。这是特定算法的每 rank 流量模型,不是所有通信库、拓扑和消息尺寸都必然采用的算法。
四个 rank、2GB 梯度
每 rank 发送 2×3/4×2=3GB。如果可用的单向有效带宽为 25GB/s,忽略逐轮启动与竞争,传输下界为 3/25=0.12s,即 120ms。这里使用十进制 GB。25GB/s 不是 25Gb/s;也没有把接收再加一次、按 6GB 计算全双工链路时间。
假设同一个固定全局 batch 在一张卡上计算 400ms,在四张卡上各算 100ms,通信再花 120ms且完全不重叠,总步时就是 220ms,加速仅 400/220≈1.82 倍,效率约 45.5%。真实 DDP 可分批发送已就绪的梯度,使部分反向与通信重叠;要从时间线找未被隐藏的通信,而不是把所有通信时长机械相加。若增大的是全局 batch,则比较的是另一个实验,必须另报收敛与质量。
内存放不下,选择究竟是在分什么
沿用第 03 课明确的 16B/参数配置:参数和梯度各 2B,FP32 主副本加 Adam 历史共 12B。用 P 表示参数数目、D 表示数据并行度。ZeRO 的三个阶段逐步去掉数据并行副本之间的冗余;下表只算常驻模型状态,不含激活、临时聚合和运行时。
| 方案 | 每卡常驻字节 | 省掉的重复 |
|---|---|---|
| 普通完整副本 | 16P | 无;加卡不自动缩小每份模型状态 |
| ZeRO-1 | (4+12/D)P | 优化器相关状态分摊 |
| ZeRO-2 | (2+14/D)P | 再分摊梯度 |
| ZeRO-3 | 16P/D | 再分摊参数的常驻份额 |
10 亿参数、D=4,上述状态依次为 16、7、5.5、4GB。分片不是把通信也删掉:用到一层参数时可能要临时聚合,用完再释放;实际峰值受聚合粒度、预取与通信缓冲影响。激活主要跟输入、层结构和执行计划有关,不能跟着模型状态公式统一除以 D。先在训练状态计算器复算,再回到实际峰值验证。
TP(张量并行)把一层中的矩阵等张量运算切分到多设备,层内结果需要交换或归约。PP(流水线并行)把不同层分配给不同阶段,微批次依次经过各阶段;开始填充和最后排空时会有空闲,阶段不均衡也会造成等待。它们能与数据并行结合,却不是 ZeRO 阶段的别名:一个主要切计算,一个主要分担数据并行状态。设备互联越慢,频繁层内通信的代价越需要实测。
先证明没算错,再解释为什么没变快
第一关是正确性:固定初始参数与同一全局样本,比较单进程和多进程的一次更新;检查样本是否重复、平均是否做了两次、尾批是否等大。第二关才是扩展:分别记录计算、通信、等待与端到端步时。最后一个到达集体通信的 rank 可能在读慢存储,不一定是网络坏了。
遇到 hang(长时间互相等待),先核对所有 rank 是否进入相同次数和顺序的通信,是否有人异常退出、走了不同分支或提前用尽数据。单纯拉长超时可能掩盖协议不一致。第 04 课的恢复语义也要扩展到全组:完整 checkpoint 所有分片应属于同一步,进程不能各自拿“最新文件”就继续更新。
核心路线先用手算与浏览器模型完成,不要求真实多 GPU。选做 DDP 实验时,固定框架、设备和后端,先跑官方最小示例,再改样本分配或故意让一个 rank 晚到。CPU 或纸面实验验证的是协议与算术,不代表跨节点通信性能。
给方案附上内存、通信与验证条件
- 复算四卡通信例,并把带宽改为 12.5GB/s。说明通信量不变、时间为什么改变。
- 陌生配置:8 个 rank,梯度 4GB、有效带宽 50GB/s。报告每 rank 发送量和下界,不能只写全网总量。
- 一个模型的完整状态每卡放不下,同事建议“直接用 DDP”。用一段话否决这个理由,并列出状态分片、TP、PP 各需要追加验证的代价。
参考与验收
第一题发送仍为 3GB,下界变成 240ms。陌生配置发送 2×7/8×4=7GB,下界 140ms。DDP 复制模型状态,不能单独解决该容量问题;分片查临时聚合峰值,TP 查层内通信与切分限制,PP 查阶段平衡与气泡。验收还要注明全局 batch 是否不变、带宽是字节还是比特,以及哪些时间能够重叠。
沿两个问题读实现资料
读 PyTorch DDP 入门时,标出梯度同步发生的位置和进程快慢不一致的说明;读后画出你自己的两 rank 更新路径。读 ZeRO 论文第 5 节时,逐项核对“哪个状态分片”,用本课 10 亿参数、四副本重算,不照搬论文中的大集群性能结论。
下一步:不再更新权重,还要保留什么?
训练的状态与通信已经各有账。第 06 课转向推理:权重通常不更新,却会为每条生成中的请求维护状态。不要把训练激活、优化器状态和推理 KV 合并成一个“缓存”概念。
留下本课的产出
本课产出:并行方案与通信预算。写下条件、证据、结果和仍不确定的事。保存不代表自动通过,下面的状态由你按验收条件自检。
记录只保存在当前浏览器。