如果说 Transformer 是 LLM 的骨架,那么 Attention 就是它的“信息路由系统”。
LLM 每生成一个 Token,本质上都在回答一个问题:当前这个 Token,应该从上下文中的哪些位置获取多少信息?
从最初的 MHA,到 MQA、GQA,再到 FlashAttention、PagedAttention,Attention 的演进史其实也是一部非常典型的工程优化史:
在模型质量、显存占用、内存带宽、计算吞吐和上下文长度之间寻找平衡。
对 LLM 应用开发者来说,理解 Attention 的意义并不只是为了面试。RAG 为什么会“看不见”某段文档?为什么 Context Window 越长,显存涨得越快?为什么 vLLM 能同时服务几百个请求?为什么同样是 32K 上下文,不同模型的显存需求可以差很多?
这些问题,最终都绕不开 Attention。
1. 引言:Attention 的本质——一场高维空间的“信息路由”
很多教程讲 Attention,第一句话通常是:
Query、Key、Value,分别是查询、键和值。
这句话没错,但对于工程开发者来说,它几乎没有解释力。
我们换一个场景。
1.1 把 Attention 想象成图书馆检索
假设你走进一个巨大的图书馆。
你现在手里有一个问题:
“Transformer 中为什么需要位置编码?”
这个问题就是你的Query(Q)。
图书馆里有成千上万本书,每本书都有自己的索引标签:
“位置编码”
“Transformer”
“Attention”
“RNN”
“CNN”……
这些索引标签就是Key(K)。
而真正的书籍内容,就是Value(V)。
于是整个检索过程变成:
当前问题 │ ▼ Query(Q) │ ┌────────┼────────┐ ▼ ▼ ▼ Key 1 Key 2 Key 3 │ │ │ 相似度 相似度 相似度 │ │ │ └────────┼────────┘ ▼ Attention 权重分配 │ ┌────────┼────────┐ ▼ ▼ ▼ Value 1 Value 2 Value 3 │ │ │ └────────┼────────┘ ▼ 加权信息融合Q 决定“我现在想找什么”,K 决定“每条信息是什么”,V 决定“真正取回来什么内容”。
所以:
Q:当前 Token 的信息需求
K:上下文中每个 Token 的可匹配索引
V:上下文中真正被读取的信息
Attention:计算当前 Token 应该从哪些 Token 获取信息
这就是 Attention 最重要的物理直觉:
Attention 不是简单地“看上下文”,而是在上下文中动态选择信息。
1.2 为什么需要 Q、K、V 三套表示?
最经典的 Scaled Dot-Product Attention:
Attention(Q,K,V)=softmax(QKTdk)VAttention(Q,K,V) = softmax \left( \frac{QK^T}{\sqrt{d_k}} \right)V
可以拆成三个步骤。
第一步:Q 和 K 做匹配
S=QKTdkS = \frac{QK^T}{\sqrt{d_k}}
得到的是:
“当前 Token 和上下文中每个 Token 有多相关?”
第二步:Softmax 转成权重
A=softmax(S)A = softmax(S)
于是:
Token A 0.05 Token B 0.10 Token C 0.70 Token D 0.15变成了一组概率式权重。
人话就是:
“这次查询,我主要关注 Token C。”
第三步:对 V 加权求和
Output=AVOutput = AV
最终得到的是:
融合上下文后的新表示。
因此 Attention 真正完成的是:
根据当前上下文需求,对已有信息进行动态路由和融合。
1.3 Attention 在 LLM 中到底干什么?
以一句话:
“小明把书放到桌子上,因为它太重了。”
当模型处理“它”时,需要判断“它”更可能指向:
小明?
书?
桌子?
模型并不是通过某条硬编码规则解决这个问题,而是通过多层 Attention,让当前 Token 与上下文中的相关 Token 建立不同强度的信息连接。
因此可以把 Transformer 理解成:
一个不断进行上下文信息交换的高维信息路由网络。
这也是为什么 Attention 对 LLM 如此重要。
但问题来了:
如果上下文只有 100 个 Token,这套机制非常舒服;如果上下文变成 100K、1M Token 呢?
工程问题马上出现。
2. 架构演进:从 MHA 到 GQA,一场显存与速度的妥协
Attention 第一个巨大的工程问题不是算力,而是:
KV Cache 太大。
理解这个问题之前,需要先理解 MHA、MQA 和 GQA。
2.1 MHA:每个 Query Head 都有自己的 K/V Head
经典 Multi-Head Attention:
Multi-Head Attention(MHA)会把隐藏层拆成多个 Attention Head。
例如:
num_attention_heads = 32那么通常意味着:
Q Head:32 K Head:32 V Head:32结构可以理解为:
Q1 ─── K1 ─── V1 Q2 ─── K2 ─── V2 Q3 ─── K3 ─── V3 ... Q32 ── K32 ── V32优点非常明显:
每个 Head 都拥有独立的 K/V 表示,表达能力强。
但缺点也非常明显:
推理阶段,每个请求都要缓存大量 K/V。
这就是 KV Cache。
2.2 为什么 KV Cache 会成为显存杀手?
假设:
Layers = 32 KV Heads = 32 Head Dim = 128 Context = 8192 dtype = FP16单个 Token 的 K/V 数据量大约是:
2×L×HKV×d2 \times L \times H_{KV} \times d
其中:
2:K + V
$L$:Layer 数
$H_{KV}$:KV Head 数量
$d$:Head Dimension
对于整个上下文:
MemoryKV∝2×L×HKV×N×dMemory_{KV} \propto 2 \times L \times H_{KV} \times N \times d
其中 $N$ 是序列长度。
关键点来了:KV Cache 与上下文长度 $N$ 成正比。
所以:
8K → 1 倍 16K → 2 倍 32K → 4 倍 64K → 8 倍 128K → 16 倍这就是为什么:
长上下文推理首先遇到的往往不是模型参数显存,而是 KV Cache 显存。
2.3 MQA:让所有 Query Head 共享 K/V
于是研究人员想到:
Q Head 必须这么多吗?
K/V 能不能共享?
于是出现:
Multi-Query Attention(MQA)
结构