想象你是一座藏书阁的主人,阁中藏有一卷 100 米长的竹简《大模型录》,需要四位书生合作把它“抄录”下来。一个书生从头抄到尾,书桌根本摊不开——这就好比一块 GPU 的显存放不下超长序列的中间结果。四位书生各负责 25 米,每人桌面上的笔记瞬间少了一大半,这就是“序列并行”(Sequence Parallel)最朴素的思想。可长卷中有几处“总批注”(LayerNorm)必须通读全卷才能落笔,四位书生需要把各自段落拼起来看一眼,写完批注再拆回各自段落继续抄。本文就用这个“分读长卷”的故事,讲清楚序列并行如何切分序列、省下显存,以及它与张量并行、ZeRO 的配合,让你在训练超长上下文模型时心中有数。
序列并行是吃透分布式训练的最后一块拼图。掌握它,你就掌握了长上下文训练中“激活显存”的最优解法,无论是调参、做工程还是面试,都能直接受益。
可视化演示
动态演示序列并行Sequence P的核心机制,通过全局/局部/动态/静态四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
序列并行Sequence P:AI换脸 AI换脸技术原理与深度伪造基础 DeepFaceLab训练流程 核心机制可视化 序列并行Sequence P整体架构与关键组件
由来与动机
藏书阁的竹简越编越长,从 1 万字写到 12.8 万字,一个书生的书桌彻底放不下了。于是四人采用“张量并行”:每人负责一种注疏方式——甲注音、乙释义、丙句读、丁点评,相当于把每个字的特征维度(hidden size)切成四份。可问题是,四人仍然要把 100 米长的竹简从头到尾通读一遍,每读一个字都要在自己的本子上写下批注(激活值)。一百米读下来,每个人的桌面都堆满了笔记,显存照样爆掉。
这时有人提出一个关键观察:既然四个人都要逐字读完全文,与其每人读全卷、各注一行,不如按地段分工——甲读 0 到 25 米,乙读 25 到 50 米,丙读 50 到 75 米,丁读 75 到 100 米。这个思路就是序列并行:把序列长度维度 S 切成 N 份,每张卡只需处理属于自己的 S/N 个 token。它与张量并行并不冲突,而是互补——张量并行走“纵向”切 hidden,序列并行走“横向”切序列,两者叠在一起,桌面上的笔记才会真正变少。
Transformer 单层激活的显存占用可以表示为:
1. b(batch size):一次喂给模型几条样本,相当于同时抄录几卷竹简。
2. s(sequence length):每条样本的 token 数,也就是长卷的字数,序列并行要切分的正是它。
3. h(hidden size):每个 token 的隐藏特征数,张量并行沿它切分。
4. 整体含义:三个因子中,只有 s 可以被“按地段分工”直接缩小 N 倍,这正是序列并行省显存的起点。
分地段读长卷让每人的笔记量直接变为原来的四分之一,但这只是故事的开始。真正让序列并行成立的,是长卷中“总批注”与“逐段注疏”的分工,我们接着看。
核心原理
四位书生分好地段后,遇到了新麻烦:竹简上有些地方要求“先通读全卷再写总批”。Transformer 里的 LayerNorm 和 Dropout 就是这样的环节——LayerNorm 需要统计整个 token 的特征分布(均值、方差)才能做归一化,在张量并行下,它必须先把被切碎的 hidden 维拼回完整向量才能计算。而 MLP、Attention 中的矩阵乘法,则是“逐 token 独立”的:每个位置的向量变换只跟它自己那一列有关,完全可以各算各的。
序列并行的核心布局因此诞生:在 LayerNorm 之前,四张卡通过 AllGather 把各自手里的序列段拼成完整长卷,统一做完 LayerNorm/Dropout 之后,再用 ReduceScatter 把序列切回四段,各自继续在本地做 MLP 与 Attention 的矩阵乘法。也就是说,每一层 Transformer 只在这两个“总批”点发生跨卡通信,其余时间大家埋头各读各的段。
这个数据流可以用一个简洁的式子表示,第 i 张卡的输出来自对完整序列做 LayerNorm 后再切回第 i 段:
1. Concat(X₁, …, X_N):AllGather 的数学表达,把 N 张卡各自持有的序列段沿序列维度首尾相接。
2. LN(·):对拼接后的完整序列做 LayerNorm,统计量在隐藏维上计算,需要看到全部序列段。
3. 下标 i:归一化完成后,按第 i 卡负责的序列位置切回第 i 段,得到本地结果。
4. 整体流程:每次 LayerNorm 前后各发生一次全卡通信,之后 MLP 和注意力投影都在本地段上完成,无需跨卡。
四书生的抄书流程从“每读一字都要对注释”变成了“只在总批处碰头”,计算效率与显存占用同时得到改善。那么,笔记到底是怎样变少的?下一节细看激活值切分。
激活值切分
在张量并行时代,四人各写各的注疏,可每当遇到总批(LayerNorm),每个人都要把整卷 100 米的内容抄一份到自己的本子上——四张卡各存一份完整序列的激活,冗余极其严重。序列并行改变了笔记的存放方式:总批写完,立刻按地段拆开,甲只保留 0 到 25 米的批注,乙只保留 25 到 50 米,以此类推。四份完整笔记变成四段互补笔记,总量恰好等于一份。
这意味着每张卡的激活内存从“完整序列”降为“完整序列除以卡数”。对于超长序列训练,这是决定成败的优化。具体到存储量上:
1. M_TP:张量并行模式下,LayerNorm/Dropout 的输出每卡都存一份完整拷贝,所以是 b×s×h 量级。
2. M_SP:序列并行模式下,每卡只存自己负责的序列段。
3. N(卡数):切分的份数。四卡省四倍,八卡省八倍,卡越多,单卡激活越小。
4. 对比结论:SP 不是把显存“优化”掉,而是把原本重复存储的冗余去掉,让每卡只保留必要的那一份。
四人的桌面终于清爽了:每人不再摊开 100 米的长卷,而是只摊自己那 25 米。不过,拼卷和拆卷势必产生传纸条的通信开销,这些开销与原来的张量并行相比如何?我们来看通信设计。
通信设计
四位书生传纸条的方式发生了变化。张量并行时,每读完一个字,四个人就要互相核对一次注疏——对应每层 Transformer 的两次 AllReduce,通信频繁且每次都要传整个序列。序列并行把传纸条集中到了两个节点:LayerNorm 前需要 AllGather(每人把自己 25 米的内容报给大家,拼出 100 米全卷),LayerNorm 后需要 ReduceScatter(把总批结果按地段拆回各人)。每层也恰好是两次汇聚加两次拆分。
很多人误以为序列并行是为了“省通信”,其实严谨的说法是:总通信量没有下降,与张量并行同阶。它的真正收益在于消除了重复计算和冗余存储——LayerNorm 和 Dropout 不再由四张卡各自全量算一遍,而是每卡只算自己那四分之一。通信结构则变得与数据并行更相似,方便系统调度和重叠。
通信量对比可以写成:
1. C_TP:张量并行每层需要 2 次 AllReduce,每次通信量为 b×s×h。
2. C_SP:序列并行每层需要 2 次 AllGather 与 2 次 ReduceScatter,合并后同样是 b×s×h 量级。
3. AllGather:把各卡序列段收集拼接,属于“大家把拼图交出来”。
4. ReduceScatter:对拼接结果做规约后再按序列段切分回各卡,属于“拼完再拆”。
传纸条的次数和字数没有减少,但四人本子上重复的笔记没了。这是理解序列并行最重要的心态转变:它不省通信,省显存、去冗余。接下来,把显存账本摊开,看看它和 ZeRO 如何各管一摊。
ZeRO配合
抄书除了桌上要摊笔记(激活值),几位书生脑子里还得记住“校勘表”——哪些字是通假字、哪里要加批注、明天修改哪些地方,对应模型训练中的参数、梯度和优化器状态。校勘表非常占地方,于是 ZeRO 出场了:它把这张表切成 N 份,每人只记四分之一,用的时候再互相借阅。序列并行则专管桌上的笔记(激活值),两者各管一摊,互不干扰又完美互补。
实际工程中,显存账本大致由三块构成:模型状态(ZeRO 负责切分)、激活值(序列并行负责切分)、以及少量碎片和通信缓冲。把三项都压到最低,才能装下超大模型和超长序列:
1. Φ_model(模型状态):参数、梯度、优化器状态三者合称,ZeRO 将它们在卡间切分。
2. N_ZeRO(ZeRO 切分度):ZeRO-1/2/3 分别切优化器、梯度、参数,切分度越高每卡越省。
3. M_激活 / N_SP:序列并行将激活值按序列段切分到 N 张卡上。
4. C(常数项):激活重计算、通信缓冲、显存碎片等固定开销,不属于任一并行策略直接管辖。
四位书生的“脑内校勘表”和“桌上笔记”都瘦了身,长卷终于可以继续加长。那么,把 100 米竹简换成 500 米,序列并行还能撑住吗?下面看长上下文优势。
长上下文优势
当竹简从 100 米加长到 500 米,四位书生又不够用了——但这次解法很直接:从四人扩到十六人,每人仍然只读 25 米。序列并行让激活显存随卡数线性摊分,因此显存允许的序列长度上限,可以随 GPU 数量的增加近乎线性地扩展。这正是 128K、1M 上下文模型能够训练出来的底层原因之一。
当然,序列并行的优势不是孤立的:它需要 FlashAttention 这类技术把注意力的 O(S²) 计算量和 KVCache 降下来,需要 ZeRO 把模型状态摊分掉,甚至需要流水并行把层数切分到不同设备上。业界实践中,Megatron-LM 的 Sequence Parallelism 与张量并行深度融合,DeepSpeed 的 Ulysses 也采用类似的序列切分思路——它们共同支撑起长上下文大模型的训练与推理。
在“激活显存受限且序列并行开启”的理想条件下,可训练的最大序列长度与卡数成正比:
1. s_max(单卡):单张卡在有限显存内能处理的最大序列长度,受激活大小限制。
2. N_GPU(卡数):序列并行划分的份数,卡数翻倍,每卡负担的序列段减半。
3. 线性关系:卡数越多,能拼出的完整序列越长,这就是长上下文训练的可扩展性。
4. 前提条件:该式成立需要激活重计算、注意力优化等措施保证激活不成为新的瓶颈。
从 100 米到 500 米,十六位书生接力诵读,每个人桌上的笔记依然只有 25 米。序列并行让“长”不再是显存的天花板,而是通过堆卡就能换来的线性扩展能力。
AI 应用场景
长上下文大模型预训练
阶段:模型预训练 / 分布式训练
解决的问题:预训练 128K 上下文的大语言模型时,单卡显存无法容纳超长序列产生的激活值。传统做法只能被迫缩短序列,或使用大量激活重计算拖慢训练速度。序列并行将激活值按序列段摊分到多卡,配合张量并行,让 128K 甚至 1M 上下文的预训练成为可能。
具体案例:某团队在 64 张 A100 上训练一个 7B 参数的医疗大模型,输入为 100K 字的完整病历。开启 Megatron 式序列并行后,单卡激活显存下降约 75%,训练吞吐提升 40% 以上,模型能够直接阅读整本医学教材。
长文档大模型微调
阶段:模型微调 / 指令微调
解决的问题:企业私有知识库微调常涉及数万字的合同、论文、法规,序列一长显存立刻告急。序列并行让微调阶段也能塞进长序列,无需把文档截断成碎片导致上下文丢失。
具体案例:某法律科技公司在微调法律问答模型时,把单条样本从 4K 扩展到 32K token,采用张量并行 + 序列并行 + LoRA 的组合,8 卡 80G 显存即可完成训练,长条款的问答准确率提升了 22%。
多卡序列并行推理
阶段:模型推理 / 在线服务
解决的问题:超长序列生成(如生成整本小说、完整代码仓库)时,激活值和 KVCache 占用同样随序列线性增长。序列并行与张量并行结合,可以把序列维切到多卡,推理单卡放不下的超长请求。
具体案例:某 AI 写作平台使用 4 卡并行推理方案,将单次生成长度从 8K 扩展到 32K 字,用户写长篇小说的体验显著提升,显存峰值仅为原来的 30%。
Python 演示
import numpy as np def layer_norm(x, eps=1e-5): """Standard LayerNorm over the last dimension.""" mean = x.mean(axis=-1, keepdims=True) var = x.var(axis=-1, keepdims=True) return (x - mean) / np.sqrt(var + eps) def simulate_sequence_parallel(): B, S, H = 2, 128, 64 # batch, sequence length, hidden size N = 4 # number of GPUs chunk = S // N # local sequence length per GPU rng = np.random.default_rng(42) X = rng.standard_normal((B, S, H)).astype(np.float32) W = rng.standard_normal((H, H)).astype(np.float32) # Baseline: tensor parallelism stores full sequence activation per GPU act_tp = X.nbytes # Sequence parallelism: activation split along sequence dim act_sp = X[:, :chunk, :].nbytes # Forward with SP: each GPU computes MLP on its own segment local_outs = [] for i in range(N): x_local = X[:, i * chunk:(i + 1) * chunk, :] local_outs.append(x_local @ W) # AllGather: concatenate segments to recover full sequence gathered = np.concatenate(local_outs, axis=1) # LayerNorm needs the full sequence (global statistics on hidden dim) normed = layer_norm(gathered) # ReduceScatter: split back to segments for the next stage after_sp = [normed[:, i * chunk:(i + 1) * chunk, :] for i in range(N)] print("每卡激活字节数(TP):", act_tp) print("每卡激活字节数(SP):", act_sp) print("SP 节省比例: {:.1%}".format(1 - act_sp / act_tp)) print("SP 前向结果形状:", normed.shape, "分段数量:", len(after_sp)) print("结果数值有效:", bool(np.isfinite(normed).all())) if __name__ == "__main__": simulate_sequence_parallel()
chunk = S // N计算每张卡分到的序列段长度,这是序列切分的起点。act_tp = X.nbytes张量并行基线:每卡都持有完整序列的激活,显存占用全额计算。act_sp = X[:, :chunk, :].nbytes序列并行下每卡只持有自己那一段激活,字节数直接除以卡数。x_local @ W每张卡在本地序列段上独立完成矩阵乘法,全程无需跨卡通信。np.concatenate(local_outs, axis=1)模拟 AllGather:沿序列维把四个分段拼回完整序列。layer_norm(gathered)LayerNorm 必须拿到完整序列才能计算隐藏维上的均值与方差。normed[:, i * chunk:(i + 1) * chunk, :]模拟 ReduceScatter:归一化结果按段切回各卡,供下一阶段继续本地计算。
常见误区
误区我以为序列并行和张量并行相比,通信次数更少、传的数据量更小,所以训练会快很多。看到通信对比图里 SP 的通信点少,就以为它省通信。
正解序列并行并没有减少通信量。张量并行每层 2 次 AllReduce,序列并行每层 2 次 AllGather + 2 次 ReduceScatter,总通信量同阶(都是 O(b×s×h) 量级)。SP 的真正收益是消除 LayerNorm/Dropout 的重复计算和冗余激活存储。训练变快主要来自显存压力下降带来的更大 batch、更少重计算,而不是通信本身变少。
为何常见:很多示意图只画“通信点数量”,让人误以为通信点少就是通信量少。实际上 AllGather 单次传输的数据包大小决定总量,不能只看次数。
误区既然序列并行这么厉害,是不是可以只做序列并行,完全不用张量并行?我以为它们是两条独立的路线,选了 SP 就不用 TP 了。
正解在主流实现(如 Megatron-LM)中,序列并行是张量并行内部的一个补充模块——先做列并行/行并行切分 hidden,再在 LayerNorm 边界处把张量布局从 hidden 维切分切换为序列维切分。两者必须配合使用,SP 依赖 TP 的权重切分和 f/g 通信原语,TP 依赖 SP 消除 LayerNorm 区域的激活冗余。
为何常见:初学者看到“两种并行”就以为是二选一的关系,忽略了它们在同一个 Transformer 层内深度嵌套的事实。
误区序列太长显存放不下,那把 batch 从 8 减到 2 不也一样吗?反正都是让每卡数据量变少,何必费劲切序列?
正解切 batch(数据并行)与切序列(序列并行)效果完全不同。数据并行每个 GPU 都要处理完整序列,单序列的激活峰值并不会下降;序列并行切的是序列长度维度,直接让每个样本的激活按段分布到各卡。训练超长单条样本(如 100K 文档)时,batch 可能只有 1,此时只有序列并行能解决显存问题。
为何常见:从“数据量变小”的直觉出发,容易把两个不同维度的切分混为一谈。
误区我看文章说序列并行让序列长度随卡数线性扩展,那我不断加卡,不就能训练 10M token 的超长上下文了?
正解序列并行只解决激活显存这个维度。超长上下文还受注意力 O(S²) 计算量、KVCache 显存、通信带宽、训练稳定性等多重约束。128K 乃至 1M 上下文的实现,需要 FlashAttention、KVCache 压缩/卸载、ZeRO、流水并行等一整套技术协同,并非单靠序列并行。
为何常见:线性扩展的公式容易让人忽略其成立的前提条件,把单一技术当成万能银弹。
面试题
Q1. 序列并行主要切分的是哪个维度?
- A.batch批次维
- B.序列长度维← 正确答案
- C.隐藏特征维
- D.网络层数维
答案:B。序列并行顾名思义沿序列长度维度切分,把一段长序列分到多张卡上,每卡只处理 S/N 个 token。batch 维是数据并行的领域,隐藏维是张量并行的领域。
Q2. 张量并行下 LayerNorm 激活存在什么痛点?
- A.每卡冗余存完整序列← 正确答案
- B.计算速度提升有限
- C.梯度无法反向传播
- D.数值精度明显下降
答案:A。张量并行把 hidden 维切分后,LayerNorm 的输出在每张卡上都保留完整序列的拷贝,造成 N 倍冗余。序列并行通过按序列段切分消除这份冗余,让每卡只存自己负责的序列段。
Q3. 序列并行中把各卡序列段拼回完整序列的通信原语是?
- A.AllReduce规约
- B.AllGather汇聚← 正确答案
- C.ReduceScatter切分
- D.Broadcast广播
答案:B。AllGather 将各卡持有的序列段沿序列维度拼接成完整序列,供 LayerNorm 使用。ReduceScatter 的作用相反,把完整序列规约后再切回各卡;AllReduce 是张量并行中的全量规约,不涉及序列维拼装。
Q4. 关于序列并行通信量的正确说法是?
- A.相比TP通信量减半
- B.相比TP通信量翻倍
- C.与TP通信量同阶← 正确答案
- D.完全不需要通信
答案:C。序列并行每层用 2 次 AllGather + 2 次 ReduceScatter 替代张量并行的 2 次 AllReduce,总通信量是同阶的。SP 的核心收益不在省通信,而在省激活显存和消除重复计算。
Q5. 序列并行主要解决哪类显存问题?
- A.权重参数显存
- B.梯度张量显存
- C.中间激活显存← 正确答案
- D.优化器状态
答案:C。序列并行切分的是前向计算产生的中间结果(激活值),包括 LayerNorm 输出、MLP 中间结果、Dropout 掩码等。参数、梯度与优化器状态属于模型状态,由 ZeRO 或张量并行负责处理。
Q6. ZeRO 与序列并行在显存优化上的分工是?
- A.ZeRO管模型状态← 正确答案
- B.二者管模型参数
- C.二者管激活值
- D.ZeRO管激活值
答案:A。ZeRO 系列策略负责切分参数、梯度和优化器状态等“模型状态”,序列并行负责切分前向计算的激活值。两者正交互补,叠加使用能同时压缩显存账本中的两大项。
Q7. 序列并行对长上下文训练的核心价值是?
- A.摊分激活支持更长序列← 正确答案
- B.降低注意力计算复杂度
- C.减少模型总参数量
- D.提升单卡计算速度
答案:A。序列并行把激活值沿序列维摊分到多卡,使可训练序列长度随卡数线性增长,这是长上下文训练的显存基础。降低注意力复杂度靠 FlashAttention 等算法,减少参数量与单卡速度并非 SP 的目标。
Q8. 以下哪些是序列并行(Megatron-SP)的特性?
- A.激活沿序列维切分← 正确答案
- B.LN不再全量重复计算← 正确答案
- C.完全消除跨卡通信
- D.可作为TP的补充使用← 正确答案
答案:ABD。SP 的激活沿序列维切分、LayerNorm/Dropout 不再每卡全量重复计算、作为 TP 的内嵌补充共同使用,这三点都正确。但 SP 仍有跨卡通信(AllGather/ReduceScatter),“完全消除”是错误说法。
Q9. 在序列并行中,LayerNorm 的处理方式是?
- A.先拼接完整序列← 正确答案
- B.只看本卡序列段
- C.按隐藏维算统计← 正确答案
- D.完全省去该层
答案:AC。SP 中 LayerNorm 需要先 AllGather 拼接完整序列,才能基于完整输入计算统计量;归一化的均值与方差按 hidden 维计算,与序列段无关。LayerNorm 不会被省去,也不能只看本地序列段。
Q10. 训练 128K 超长上下文,以下哪些技术能缓解显存或计算压力?
- A.优化注意力计算← 正确答案
- B.切分序列降显存← 正确答案
- C.ZeRO分模型状态← 正确答案
- D.单纯增大batch
答案:ABC。FlashAttention 等注意力优化降低 O(S²) 计算与 KVCache 开销,序列并行摊分激活显存,ZeRO 切分模型状态,三者协同支撑长上下文。单纯增大 batch 只会加剧显存压力,与长上下文需求背道而驰。