从 Bit 到 AGI第五章:Transformer

Attention 的复杂度

从 Bit 到 AGI · 第五章:Transformer · 为什么序列变长时 Attention 成为瓶颈

作者 David更新于 第 78 篇(共 186 篇)

Self-Attention 的核心是 n×nn \times n 的权重矩阵——序列长度 nn 每翻一倍,attention 的内存与计算大约翻四倍。这就是 O(n2)O(n^2) 复杂度。BERT 上限 512、GPT-3 2048、现代 LLM 推 128K 上下文,背后全是算法与工程在跟这个平方律搏斗。

直觉:全班两两握手的次数

nn 个人,每人要和其他 nn 人配对寒暄,大约 n2n^2 次握手。128 个 token 要算 1282=16,384128^2 = 16{,}384 对相似度;1024 个 token 要 1,048,5761{,}048{,}576 对——长度线性增,配对次数平方增。

定义

对单头 attention,设序列长 nn,dk=dmodel/hd_k = d_{\text{model}}/h。

时间复杂度(乘加各计 1 FLOP):

步骤复杂度
QKV 投影O(ndmodel2)O(n d_{\text{model}}^2)
QK⊤\mathbf{Q}\mathbf{K}^\topO(n2dk)O(n^2 d_k)
AV\mathbf{A}\mathbf{V}O(n2dk)O(n^2 d_k)
输出投影O(ndmodel2)O(n d_{\text{model}}^2)

hh 头合计,h⋅dk=dmodelh \cdot d_k = d_{\text{model}},故 attention 矩阵部分为 O(n2dmodel)O(n^2 d_{\text{model}})。

空间复杂度(存 attention 权重):O(n2)O(n^2) 每头;hh 头则 O(hn2)O(h n^2)。

相对 FFN 的 O(ndmodeldff)O(n d_{\text{model}} d_{\text{ff}}),当 nn 很大时 n2n^2 项主导。

例子

例 1:BERT-base,d=768,h=12,dk=64d=768, h=12, d_k=64

nnQK⊤\mathbf{Q}\mathbf{K}^\top FLOPs (2hn2dk2 h n^2 d_k)相对 n=128n=128
1282.5×1072.5 \times 10^71×
5124.0×1084.0 \times 10^816×
20486.4×1096.4 \times 10^9256×

例 2:内存(fp32,单层单 batch,12 头)

Bytes=h⋅n2⋅4=12⋅n2⋅4\text{Bytes} = h \cdot n^2 \cdot 4 = 12 \cdot n^2 \cdot 4
nnAttention 矩阵
51212.6 MB
4096805 MB
3276851.5 GB

训练时还需存梯度与 optimizer state,显存压力更大。

例 3:与线性层对比(n=512n=512)

  • QKV+FFN:≈O(nd2)∼109\approx O(n d^2) \sim 10^9 FLOPs
  • Attention 矩阵:≈O(n2d)∼109\approx O(n^2 d) \sim 10^9 FLOPs

n=512,d=768n=512, d=768 时二者同量级;n=8192n=8192 时 n2n^2 项碾压 nd2nd^2。

自己拖一下

三张表分别列了 FLOPs、内存、对比,但「谁在什么时候压倒谁」是一条连续曲线,拼不出来。拖 nn,看紫色那两段(n2n^2 项)怎么从一条缝长到吞掉整条:

拖 n,看紫色(n²)那两段何时吞掉整条
模型
2K
投影 274.9G(∝ nd²) vs attention 68.7G(∝ n²d) → 比值 0.25×
■ 投影/FFN (nd²)■ QKᵀ + A·V (n²d)总 343.6 GFLOPs/层
attention 矩阵显存(所有头,fp32)512.0 MB/ 层投影/FFN 主导

注意:n=2048 还没到交叉点(n=2d=8192)——这个长度下 attention 其实不是大头,FFN/投影才是。

「attention 是平方所以最贵」是个流行的误解。交叉点在 n = 2d—— LLaMA-7B(d=4096)要到 n=8192 才轮到 attention 项压过投影/FFN。 也就是说,8K 以下的上下文,大头一直是那些 nd² 的稠密矩阵乘,不是 attention。
真正让 attention 可怕的不是 FLOPs,是那块 n² 的显存:它按 h·n² 存,128K 上下文下单层就要几十 GB——这才是 FlashAttention(不存满 n×n)和稀疏注意力要解决的。

这里有个反直觉的点值得记住:交叉点在 n=2dn = 2d。LLaMA-7B(d=4096d=4096)要到 n=8192n=8192 才轮到 attention 项超过投影/FFN——也就是说,8K 以下的上下文,大头一直是那些稠密矩阵乘,不是 attention。「attention 是平方所以最贵」这句流行的话,只在长上下文下才对。真正一直可怕的是那块 O(n2)O(n^2) 的显存(拖到 128K 看那个数字),这才是 FlashAttention 要解决的。

# 粗算 BERT-base 单层 attention FLOPs
def attention_flops(n, d_model=768, n_heads=12):
    d_k = d_model // n_heads
    qkv = 6 * n * d_model ** 2
    scores = 2 * n_heads * n ** 2 * d_k
    av = 2 * n_heads * n ** 2 * d_k
    out_proj = 2 * n * d_model ** 2
    return qkv + scores + av + out_proj

for n in [128, 512, 2048, 8192]:
    print(n, attention_flops(n) / 1e9, "GFLOPs")

量化:缓解 O(n2)O(n^2) 的方向

方法思路代表
FlashAttentionIO 感知分块,不存满 n×nn \times n训练/推理标配
稀疏 Attention只算局部/跨步连接Longformer, BigBird
线性 Attention核技巧近似 softmaxPerformer
KV Cache + GQA推理不重复算 K/VLLaMA-2, vLLM
上下文缩短截断、摘要、RAG产品层

FlashAttention 不改变渐近 FLOPs,但降低 HBM 读写,实际快数倍;长上下文主要瓶颈仍是 n2n^2 量级计算。

常见误区

  1. O(n2)O(n^2) 只指 Self-Attention:Cross-Attention 是 O(ntns)O(n_t n_s),源句长 nsn_s、目标句长 ntn_t。
  2. Decoder 推理仍是 O(n2)O(n^2) 每步:无 cache 时生成第 tt token 要算 t×tt \times t;有 KV cache 后每步约 O(t)O(t),总生成 O(n2)O(n^2)。
  3. 加头数不影响 n2n^2 项:hh 与 dkd_k 成反比,hn2dk=n2dmodelh n^2 d_k = n^2 d_{\text{model}}。

小结

Attention 对序列长度 nn 是 O(n2)O(n^2) 时间与空间(权重矩阵)。BERT-base 在 n=512n=512 尚可;128K 上下文必须靠 FlashAttention、稀疏、GQA 等组合优化。下一篇看位置编码如何随长度扩展而演进。

下一篇:位置编码的变体

本页目录