跳转至

第 5 章 模型内并行:TP、SP 与 CP

开场问题:70B 模型的单层 FFN 权重就有 1.7 GB,ZeRO-3 在前向那一刻仍要把整层 AllGather 到一张卡上。分片救不了"单层本身放不下"。

5.1 张量并行在切什么

TP 不是把不同层放到不同卡,而是把同一个矩阵乘法切开,让多张卡各算一部分。

以 FFN 为例,Y = X @ W1,把 W1 按列切成 N 份分给 N 张卡,每张卡拿到完整的 X 和 1/N 的 W1,各自算出 Y 的 1/N 列。接着 Z = Y @ W2,把 W2 按行切成 N 份,每张卡用自己手上那 1/N 列的 Y 乘对应那 1/N 行的 W2,得到完整形状的部分和,最后一次 AllReduce 把 N 份部分和加起来就是正确结果。

这套"先列切、再行切、中间不通信"的组合是 Megatron 张量并行的基本单元。注意力部分同理:QKV 投影按头切(每张卡负责若干个头),输出投影按行切,中间的注意力计算各头独立、完全不需要通信。

5.2 为什么 TP 不能跨机

数一下通信:每个 Transformer Block 的前向有两次 AllReduce(注意力后一次、FFN 后一次),反向再两次。通信量正比于 batch × 序列长度 × hidden,与参数量无关、与层数成正比。

把这个数除以 NVLink 带宽(几百 GB/s 量级)和跨机网络带宽(通常低一个数量级),结论自己就出来了:机内尚可接受,跨机则每层都要在关键路径上等网络。所以"TP 的大小不超过单机卡数"这条铁律不需要死记,算一遍就明白。

5.3 序列并行是 TP 的补丁

开了 TP 之后还有个漏网之鱼:LayerNorm、Dropout、残差这些操作没法按 hidden 维切(它们需要完整的特征向量),于是每张卡都保存了一份完整序列的激活,这部分显存没省下来。

序列并行把这些区域按序列维切开,每张卡只处理一部分 token。配合的通信变化是:原本 TP 边界上的 AllReduce 被拆成 ReduceScatter + AllGather 两步,通信总量不变,但中间那段的激活显存降到 1/N。

它不是用来训长文本的,只是给 TP 补激活显存的漏。这个术语在中文资料里被大量误用。

5.4 上下文并行才是长序列的解法

训 128K 上下文时,单条样本的注意力本身就放不下一张卡。CP 把序列的不同位置分到不同 GPU,每张卡只持有自己那段的 Q、K、V。

问题是注意力需要每个 query 看到所有 key。两条主流实现:Ring Attention 让各卡的 K/V 分块在环上传递,每收到一块就和本地 Q 算一次局部注意力,再用在线 softmax 合并,通信可以和计算重叠;Ulysses 在头维度做 AllToAll,把"按序列切"临时转成"按头切",算完再转回来,实现简单但并行度受头数限制。两者也可以混合使用。

和数据并行的区别要说清楚:DP 是不同卡处理不同样本,CP 是不同卡处理同一条样本的不同片段。

5.5 变长样本与 Packing:一个不报错的坑

预训练语料长短不一,按最长样本 padding 会浪费大量算力,所以工业做法是把多条短样本拼进一个固定长度的序列(packing)。

但拼接之后必须让 attention mask 阻止样本之间互相看见,位置编码也要为每条样本重新从 0 开始。实现错了不会崩溃、不会 OOM,只是模型学到了跨样本的虚假依赖,loss 降得慢一点。 几百卡跑了一周才发现这种问题,代价极高。

同类的还有 loss 归一化:变长数据下按样本数平均和按有效 token 数平均不等价,前者会系统性地给短样本更大权重。

5.6 什么时候用哪个

按约束选,不要按"框架支持"选:单层放不下 → TP(且不出机);单样本放不下 → CP;只是训练状态总量放不下 → 优先 FSDP;开了 TP 之后激活还是紧 → 加 SP。

核心问答

Q:TP 为什么必须留在机内? 每层两次 AllReduce 都在关键路径上,跨机带宽和延迟会直接乘进 step 时间。

Q:SP 和 CP 到底差在哪? SP 切的是 LayerNorm/Dropout 这些非 TP 区域的激活,注意力仍在单卡内完整计算;CP 连注意力本身都跨卡,是真正用来训长序列的。

Q:长上下文为什么不能只靠激活重计算? 重计算省的是"保存下来的激活",但单次注意力计算的峰值显存和中间张量仍然放不下。

Q:TP 开到 16 会怎样? 首先它跨机了,其次每张卡分到的矩阵已经很小,GEMM 效率下降、通信占比上升。TP 有明确的收益拐点。

Q:Packing 为什么可能悄悄毁掉训练? mask 或位置处理错误时不会有任何报错信号,只表现为收敛变慢,而这很容易被归因成"数据质量不好"。