Longformer 用局部窗口加少量 global token 把每层注意力从 O(n²) 压到 O(n·w);StreamingLLM 发现窗口丢掉开头几个 token 会让 Llama-2-13B 的 PPL 从 5.40 冲到 5158.07,于是保留 4 个 sink 再滚动窗口;Mistral 7B 则把 W=4096 的 SWA 写进预训练,用 32 层叠出约 131K 的理论跨度。
FlashAttention 不改变注意力模式,只用分块和重算把 GPT-2 medium 的 HBM 读写从 40.3 GB 压到 4.4 GB——四件事分别作用在「算哪些对」「留哪些 KV」「把数据放在哪」三个维度上。
长上下文有三个独立的账本:注意力矩阵的计算量、推理时 KV cache 的大小、以及 HBM 与 SRAM 之间的数据搬运。 把三个账本混在一起,就会得出「FlashAttention 是稀疏注意力」或「滑窗等于长上下文」这类错误结论。
Longformer 改的是第一本账:每个 token 只算窗口内的 pair,再加少量 global token;计算 O(n·w),ℓ 层感受野 ℓ×w。
StreamingLLM 改的是第二本账:不训练、不改模型,只保留 4 个初始 token 的 KV 加最近窗口,Llama-2-13B 在 PG19 65K 上的 PPL 从 5158.07 回到 5.40。
Mistral 7B 把 SWA 写进预训练:W=4096、32 层,理论跨度 4096×32≈131K(W 与层数见 Mistral Table 1);rolling buffer 把 32K 序列的 cache 减少 8×。
FlashAttention 谁也不稀疏:它计算完整的 N×N 注意力,只是把中间矩阵留在 SRAM,靠 tiling 和重算减少 HBM 访问。
读这篇要分清「模式稀疏」「cache 策略」「IO 优化」三层;它们可以叠加,但任一层都不能单独解决另外两层的问题。
读完这节你应该知道:长上下文到底贵在哪三个地方,以及为什么「把窗口改小」只解决其中一部分。
标准自注意力先把 Q 和 K 乘成 N×N 的分数矩阵,再做 softmax、乘 V。Longformer 的 §3 直接指出,
这个操作的时间和显存都是 O(n²):序列翻倍,矩阵面积翻四倍。到了几万 token,矩阵不再是一个可以随手物化的中间量,
而是一堵墙。论文的解法不是把矩阵算得更快,而是只算其中一部分——滑窗、膨胀滑窗、global token。
推理侧是另一本账。StreamingLLM 的 §1 说,解码阶段缓存所有历史 token 的 Key/Value 会带来过高的显存占用和递增的解码延迟; 序列越长,KV cache 线性膨胀。解码又是 memory-bound 的:FlashAttention 的 §2.1 给出 A100 的层级—— HBM 带宽 1.5–2.0 TB/s,每个 SM 只有 192 KB SRAM, 108 个 SM,SRAM 带宽约 19 TB/s。 SRAM 比 HBM 快一个数量级,但小三个数量级;数据放在哪里、搬多少次,直接决定 wall-clock。
NSA 的 §1 给了一个量级:解码 64K 上下文时,softmax 注意力约占总延迟的 70–80%。 这解释了为什么长上下文优化会同时出现三条路线——改计算图、改缓存、改数据搬运。
全注意力算 N×N;滑窗只算对角线带;global token 额外连一些行和列。改变的是注意力矩阵的非零结构。
全量、最近 W 个、或 S 个 sink 加最近 W 个。改变的是decode 时能回看多远,不改变训练时的注意力公式。
标准实现把 N×N 的 S、P 写回 HBM;FlashAttention 把它们留在 SRAM,只写 O 和统计量。
三者只能各管一段。把它们当成同一件事,就会在「为什么滑窗模型仍然记不住很远的 token」或「为什么 FlashAttention 没有把长上下文变便宜」上卡住。
读完这节你应该知道:Longformer 的局部+global 解决了什么,窗口 KV cache 又为什么会在超过 cache 后崩掉。
Longformer 的 §2 把当时的长文档 Transformer 分成两类:从左到右分块处理的 ltr 路线,和定义稀疏注意力模式的路线。 前者不适合需要双向上下文的任务;后者里最接近的是 Sparse Transformer,它用 8×8 块的膨胀滑窗, 但模式固定、且依赖 BlockSparse 的 C++ 实现。Longformer 的改动是:保留滑窗,额外加任务驱动的 global attention, 并用自定义 CUDA kernel 把带状矩阵乘法做快(Longformer §3.2)。
滑窗的算术很干净。给定窗口 w,每个 token 只看左右各 1/2 w 个 token;一层注意力的计算量是 O(n×w),
随序列长度线性增长。多层堆叠会把感受野撑大:ℓ 层的顶层感受野是 ℓ×w;如果加膨胀率 d,
感受野变成 ℓ×d×w(Longformer §3.1)。Longformer 的预训练配置是 w=512、序列长 4,096,
论文说这相当于 RoBERTa 在 512 上的计算量,序列长度是 BERT 的 8 倍(Longformer §5)。
推理侧的窗口缓存是另一种做法:只保留最近 L 个 token 的 KV,cache 满了就丢最旧的。它在 cache 填满后显存恒定、解码速度稳定,
但 StreamingLLM Figure 1(b) 和 Figure 3 显示,一旦序列长度超过 cache、开头的 KV 被挤掉,语言模型会崩。
Llama-2-13B 在 PG19 65K 上的窗口注意力(0+1024)PPL 是 5158.07;
保留 4 个初始 token 后(4+1020)回到 5.40(StreamingLLM Table 1)。
每个 token 只算窗口内的 pair;global token 与所有 token 双向相连。复杂度 O(n·w),global 数量与 n 无关时仍是线性。
卡点:global token 是任务相关的先验;窗口和膨胀率是手工设计的,不是从数据里学出来的(Longformer §3.1)。
cache 填满后显存恒定,解码速度稳定;实现简单,适合流式生成。
卡点:开头 token 被挤掉后,softmax 的分母少了一大块,注意力分布整体偏移;PPL 从 5.40 量级跳到 5158.07 量级(StreamingLLM Table 1)。
所以「窗口」在训练时和推理时不是同一件事。训练时的滑窗是一个注意力模式,模型仍然看得到整个序列,只是每层只连局部; 推理时的窗口 cache 是一个淘汰策略,被淘汰的 KV 再也回不来。Longformer 解决前者,StreamingLLM 解决后者。
读完这节你应该知道:Longformer 的窗口+global 怎么定义,StreamingLLM 的 4+sink 怎么滚动,Mistral 的 SWA 怎么叠出 131K,FlashAttention 的在线 softmax 怎么在不物化 N×N 的情况下算精确注意力。
双向注意力下,第 i 个 token 的可见集合是窗口内的邻居加上 global 集合 G:
global attention 不是简单地把某些位置标成 global 就完事:论文用了两组投影 Qs, Ks, Vs 和
Qg, Kg, Vg,分别算滑窗注意力和 global 注意力,并把 global 投影初始化为滑窗投影的值(Longformer §3.1)。
这样做是为了让两类注意力有不同的表达空间,同时不破坏预训练权重的初始化。global 位置是任务相关的:分类用 [CLS],QA 用所有 question token。
StreamingLLM 不改注意力公式,只改缓存里放什么。设当前解码到第 t 个 token,cache 由两段组成:
sink 的作用不是「保留开头的语义」,而是「保留 softmax 的一个大分母项」。论文用 Llama-2-7B 的注意力图(StreamingLLM Figure 2)说明:除了最底下 2 层,
模型在几乎所有层和头上都强烈关注初始 token;把前 4 个 token 换成换行符 "\n",模型仍然关注它们,
重新引入后 PPL 也能恢复(StreamingLLM Table 1:4+1020 是 5.40,4"\n"+1020 是 5.60)。
这说明起作用的是绝对起始位置,不是 token 的语义内容。
位置重编号是容易被忽略的一步。论文举了一个例子:当前 cache 是 [0, 1, 2, 3, 6, 7, 8],正在解码第 9 个 token,
位置应赋成 [0, 1, 2, 3, 4, 5, 6, 7],而不是原始文本里的 [0, 1, 2, 3, 6, 7, 8, 9](StreamingLLM §3.2)。
RoPE 要缓存旋转前的 Key,再在每步对 rolling cache 里的 Key 施加位置变换;ALiBi 则用连续的线性偏置,而不是跳跃的偏置。
不做这一步,sink 保住了 KV,位置却对不上,模型仍然会退化。
论文在 400K token 的拼接 PG19 上给了不同模型的 PPL:MPT-7B 从 0+2048 的 460.29 降到 4+2044 的 14.99;Llama-2-7B 从 0+4096 的 3359.95 降到 4+4092 的 9.59(StreamingLLM Table 2)。4 个 sink 通常就够,继续加到 8 个只有边际收益。
Mistral 7B 把滑窗写进了预训练。Mistral 的 Table 1 配置是 n_layers=32、window_size=4096、context_len=8192;
第 k 层的 hi 只看上一层 [i−W, i] 的隐藏状态。递归下去,顶层输入可以回看 W×k 个 token;
在 W=4096、32 层时,论文给出的理论注意力跨度约为 131K(Mistral §2)。
推理时用 rolling buffer:第 i 个位置的 Key/Value 存在 cache 的 i mod W 槽位。位置超过 W 后旧值被覆盖,
cache 大小固定为 W。论文报告,在 32K 序列上这把 cache 显存减少 8×,且不影响模型质量;
在 16K 序列、W=4096 下,配合 FlashAttention 和 xFormers 的改动,相对 vanilla attention baseline 有 2× 加速(Mistral §2)。
FlashAttention 的摘要和 FlashAttention §3.1 反复强调 exact attention:它算的是完整的 softmax 注意力,不丢任何 pair。
它改的是执行顺序。标准实现把 S = QKT 和 P = softmax(S) 写回 HBM,再读出来乘 V;
FlashAttention 把 Q/K/V 切成块,逐块搬进 SRAM,用在线 softmax 累积结果,最后只把 O 和归一化统计量写回。
复杂度分析在 FlashAttention §3.2:标准注意力需要 Θ(Nd + N²) 次 HBM 访问,FlashAttention 需要 Θ(N²d²M−1),
其中 M 是 SRAM 大小。FLOPs 仍是 O(N²d),额外显存是 O(N);减少的是 HBM 访问,不是计算量。
论文在 FlashAttention Figure 2 的 GPT-2 medium 上把这一点量化得很直接:FLOPs 从 66.6 升到
75.2 GFLOPs,HBM 读写却从 40.3 GB 降到
4.4 GB,时间从 41.7 ms 降到
7.3 ms。
下面的计算器把三种参数放在一起:滑窗 W、sink 数 S、序列长度 L、层数 layers。
它算两个量:cache 大小(decode 时实际保留多少 token 的 KV)和有效感受野(顶层理论上能回看到多远)。
公式是页面按 StreamingLLM §3.2 的 cache 结构、Longformer §3.1 / Mistral §2 的层间递推做的组合模型,不声称原文给出「sink + 窗口」的感受野公式。
KV cache 4100 tokens · 占全量 12.5% · 省下 87.5% (默认 W=4096 + S=4,按 cache=min(L,W+S) 的页面公式计算)
每 token 可见 4100 keys · 有效感受野 32768 tokens(覆盖 100.0%) (ERF=min(L,W×layers+S):默认 W=4096、layers=32 时上限为 131076,再与 L 取 min;这是页面按 Longformer §3.1 的 ℓ×w 感受野递推做的组合模型,不是原文读数)
中间 28668 个 token 的 KV 不在 cache 里;每 token 只能看到 sink 与最近窗口。
口径:cache = min(L, W+S);cache 占比 = cache / L × 100%;省下 = (L − cache) / L × 100%;每 token 可见 = cache;有效感受野 = min(L, W × layers + S)。这是页面按 StreamingLLM §3.2 的 cache 结构、Longformer §3.1 / Mistral §2 的层间递推做的组合模型,不声称原文给出「sink + 窗口」的感受野公式。
组 1:W=4096,S=4,L=1,048,576,layers=32。cache = min(1048576, 4100) = 4100;占比 4100 / 1048576 = 0.390625% → 0.4%;省下 (1048576 − 4100) / 1048576 = 99.609375% → 99.6%。有效感受野 = min(1048576, 4096×32 + 4) = min(1048576, 131076) = 131076 → 131076 / 1048576 = 12.5004% → 12.5%。JS 在同样参数下输出 4100 / 0.4% / 99.6% / 131076 / 12.5%。
组 2:W=1024,S=0,L=32,768,layers=32。cache = min(32768, 1024) = 1024;占比 1024 / 32768 = 3.125% → 3.1%;省下 (32768 − 1024) / 32768 = 96.875% → 96.9%。有效感受野 = min(32768, 1024×32) = 32768 → 100.0%。JS 输出 1024 / 3.1% / 96.9% / 32768 / 100.0%。
组 1 说明:即使 cache 只占全量的 0.4%,32 层滑窗的理论感受野仍能覆盖 12.5% 的 1M 序列;组 2 说明:在 32K 序列上,W=1024 的 32 层刚好覆盖全序列,但 cache 只有 1024 token。这里的「覆盖」是理论上限,不等于模型真的检索到或利用到那些 token。
交互常量:W=4096、layers=32 取自 Mistral Table 1;S=4 取自 StreamingLLM §3.2;序列长度是页面给的可调参数。
读数随滑块变化,不是论文报告值。
读完这节你应该知道:四种方案的计算/缓存/IO 复杂度各是什么,以及为什么 Mistral 的 131K 只是理论上限。
| 方案 | 每个 query 看多少 key | 计算 / 中间显存 | KV cache | 备注 |
|---|---|---|---|---|
| Full attention | N | O(N²) 计算;O(N²) 中间矩阵 | N | 精确,但长序列不可行 |
| Longformer 滑窗 + global | w + |G| | O(N·w + N·|G|) | N(训练);推理可另配窗口 | global 是任务相关先验;ℓ 层感受野 ℓ·w |
| StreamingLLM | S + W(稳态) | O(N·(S+W)) = O(N) | S + W | 只保 sink + 最近窗口;中间 KV 丢弃 |
| Mistral SWA | W | O(N·W) | W(rolling) | 预训练时写入;槽位 i mod W |
| FlashAttention | N(精确) | O(N²·d) FLOPs;O(N) 额外显存 | N | HBM Θ(N²d²/M);不是稀疏 |
这张表里最容易被误读的是最后一行。FlashAttention 的「每个 query 看多少 key」仍是 N,它没有把矩阵变稀疏;
它把标准实现里 O(N²) 的中间矩阵从 HBM 拿掉,换成 SRAM 里的分块计算。所以它的 FLOPs 甚至可能更多:
FlashAttention Figure 2 的 GPT-2 medium 上,FLOPs 从 66.6 升到 75.2 GFLOPs,
绝对多 8.6 GFLOPs,相对多 12.9%(8.6 / 66.6);
但 HBM 从 40.3 GB 降到 4.4 GB,
绝对少 35.9 GB,相对少 89.1%(35.9 / 40.3),
比值 9.2×(40.3 / 4.4;论文摘要写 up to 9×,这里按 FlashAttention Figure 2 的表值算到 9.2×);时间从 41.7 ms 降到
7.3 ms,比值 5.7×(41.7 / 7.3)。
Mistral 的 131K 也要加限定词。论文写的是 theoretical attention span:它假设每层都把信息往前传满 W,
且模型真的学会利用这条链路。它不等于「模型能有效使用 131K 上下文」,也不等于「cache 里存了 131K 个 token」——
rolling buffer 的 cache 始终只有 W=4096。这两件事在页面上经常被混成一句「Mistral 支持 131K」。
推断(由 Mistral §2 的 W×k 递推与 rolling buffer 的 i mod W 覆盖规则对照得出)
反过来,StreamingLLM 的 cache 是 S+W,在稳态下与序列长度无关:4 个 sink 加最近窗口,序列到 4M token 时 cache 仍是常数。
这也是它能把 Llama-2 系列跑到 4M token 的原因(StreamingLLM Abstract / §4.1)。但常数 cache 的代价是中间 KV 全部丢弃,
见第 07 节。
FLOPs 衡量算术量;HBM 字节衡量数据搬运;cache token 数衡量 decode 时保留的历史。一个方案可以「FLOPs 更多、HBM 更少、cache 不变」(FlashAttention),也可以「FLOPs 更少、cache 更小、但中间信息丢失」(窗口 cache)。横比之前先确认单位。
读完这节你应该知道:训练时的注意力模式、推理时的 cache 策略、kernel 的 IO 调度如何串在一次 decode 里。
W、S+W。StreamingLLM 还要在 cache 内重编号位置;Mistral 用 i mod W 覆盖旧槽位(StreamingLLM §3.2 / Mistral §2)。S、P,用 O 和 (m, ℓ) 重算;Longformer 的稀疏 kernel 只算非零带;两者的共同点是避免把 N×N 中间量写回 HBM(FlashAttention §3.1 / Longformer §3.2)。Longformer + FlashAttention:稀疏 mask 可以交给块稀疏 FlashAttention 执行;FlashAttention §3.3 的 block-sparse 版本就是把稀疏模式映射到块上,再按稀疏率减少 IO。
StreamingLLM + Mistral SWA:两者都保留最近窗口,但 Mistral 的 SWA 是训练时的注意力模式,rolling cache 是它在推理端的自然实现;StreamingLLM 是推理时对已训练模型加 sink,不改变模型权重。
FlashAttention + 全量 KV:它不减少 cache,只减少中间矩阵的 HBM 读写;要和窗口/sink 叠加,得另外改 cache 策略。
读完这节你应该知道:每个机制在原文里的代表数字、测量条件,以及哪些数字不能直接横比。
先给一张总表,所有值都来自原文;不同论文的任务、模型、序列长度不同,只能在同一行内比较,不能跨行横比。
| 论文 / 模型 | 设置 | 指标 | 结果 |
|---|---|---|---|
| Longformer-base vs RoBERTa-base | WikiHop dev | accuracy | 75.0 vs 72.4 |
| Longformer-base vs RoBERTa-base | Hyperpartisan dev | F1 | 94.8 vs 87.4 |
| StreamingLLM · Llama-2-13B | PG19 首本书 65K;0+1024 → 4+1020 | PPL | 5158.07 → 5.40 |
| StreamingLLM · MPT-7B | 400K 拼接 PG19;0+2048 → 4+2044 | PPL | 460.29 → 14.99 |
| Mistral 7B | 32K 序列;W=4096 | KV cache | 8× reduction |
| FlashAttention · GPT-2 medium | N=1024,d=64,16 heads,batch 64,A100;前向+反向 | HBM R/W | 40.3 → 4.4 GB |
| FlashAttention · GPT-2 medium | 同上 | runtime | 41.7 → 7.3 ms |
| FlashAttention · GPT-2 medium | 同上 | FLOPs | 66.6 → 75.2 GFLOPs |
Longformer 的 Table 7 是同一 backbone 的继续预训练+微调对比,不是和全注意力长上下文模型的横比。WikiHop 上 75.0 vs 72.4, 绝对 +2.6 个百分点,相对 +3.6%(2.6 / 72.4); Hyperpartisan 上 94.8 vs 87.4, 绝对 +7.4 个百分点,相对 +8.5%(7.4 / 87.4)。 指标口径按 Longformer Table 7 的表注:WikiHop 用 accuracy,Hyperpartisan 用 F1。
StreamingLLM 的 Table 1 条件是 Llama-2-13B、PG19 第一本书 65K token;0+1024 表示 0 个 sink 加 1024 个最近 token, 4+1020 表示 4 个 sink 加 1020 个最近 token。PPL 从 5158.07 降到 5.40,相对降幅 99.9% ((5158.07 − 5.40) / 5158.07)。把 4 个原始 token 换成 4 个换行符,PPL 是 5.60, 与原始 token 的 5.40 接近;这说明起作用的是起始位置,不是语义。
Mistral 7B 在 32K 序列上把 cache 减少 8×,论文的措辞是「without impacting the model quality」;
在 16K 序列、W=4096 下,配合 FlashAttention 和 xFormers 的改动,相对 vanilla attention baseline 有
2× 加速。这两个数字都是模型实现层面的报告,不是与全注意力模型的质量对比。
FlashAttention 的 Figure 2 条件最完整:GPT-2 medium、序列长 1024、head dim 64、16 heads、batch 64、A100、前向+反向。 FLOPs 从 66.6 升到 75.2 GFLOPs, 但 HBM 从 40.3 降到 4.4 GB, 时间从 41.7 降到 7.3 ms。 论文把原因归为 HBM 访问减少,而不是 FLOPs 减少。这个例子最能说明「省搬运」和「省计算」不是一回事。
还有两个容易混的加速数字。FlashAttention 的 Figure 1 报告 GPT-2 上注意力计算最高 7.6×; FlashAttention Figure 2 的 GPT-2 medium 前向+反向是 5.7×(41.7 / 7.3)。 前者是注意力 kernel 相对 PyTorch 实现的加速,后者是 GPT-2 medium 前向+反向的 attention 时间;引用时必须带测量对象。
StreamingLLM 的 22.2× 是相对 sliding window with re-computation 的每 token 解码加速 (StreamingLLM Abstract / §4.5),不是相对 full attention 的加速。它的质量基线是「窗口 + 重算」,不是「全量 KV」。
75.2 vs 66.6 GFLOPs,时间却是 7.3 vs 41.7 ms。多出来的 FLOPs 来自反向重算;省下来的时间来自少读少写 HBM。在 memory-bound 的 attention 里,FLOPs 不是好指标。
在 400K 拼接 PG19 上,Llama-2-7B 从 4+2044 扩到 4+4092,PPL 从 9.08 升到 9.59(StreamingLLM Table 6)。论文的解释是模型不一定能充分利用更长的上下文;这也是「cache 大小」和「有效上下文」不能划等号的证据。
读完这节你应该知道:每种方法的失效边界,以及什么时候不该用。
[CLS]、QA 用 question token,都是人选的;窗口和膨胀率也是手工设计的。它降低了训练计算,但模型仍然要处理整个序列;global 数量与 n 无关且任务相关(Longformer §3.1);本页不把它当检索机制。W 个槽位;超过 W 的旧 KV 被覆盖,精确回看没有保证。Gemma 2 的 Table 10 还显示,把 local window 从 4096 降到 1024,验证 PPL 只从 1.63 到 1.64;这是 Gemma 2 特定模型和验证集上的现象,不是「窗口可以随便砍」的通用结论。Θ(Nd+N²) 降到 Θ(N²d²M−1),但没有改变二次依赖;序列继续变长,计算和 IO 仍会涨。FlashAttention §4.3 说近似注意力方法在 512–1024 之间开始和 FlashAttention 交叉;FlashAttention §5 也指出每种新模式都要写新的 CUDA kernel。它是精确注意力的加速器,不是长上下文的免费通行证。值得用滑窗:长文档分类/QA,局部证据足够;训练预算有限;能接受 global token 的任务先验。
值得用 sink + rolling:流式对话/长生成,只关心最近上下文;显存和延迟敏感;不需要回查中间细节。
值得用 FlashAttention:需要精确全注意力;GPU 上 attention 是 memory-bound;训练和推理都想减少 HBM 流量。
不要期待:用窗口或 sink 做长距离检索;用 FlashAttention 降低渐近复杂度;用 4 个 sink 恢复被丢弃的中间信息。
最后回到三个账本:Longformer 和 Mistral 改的是「算哪些对」,StreamingLLM 改的是「留哪些 KV」,FlashAttention 改的是「把数据放在哪」。 它们可以组合,但每组合一次,都要重新量一次 wall-clock、HBM 字节和有效上下文;只报其中一个数,都会把另外两个账本的代价藏起来。