注意力机制
注意力机制(Attention)的核心问题是:当模型处理某个 token 时,它应该从上下文里的哪些 token 获取信息,以及各获取多少。
一句话理解:
注意力机制 = 给上下文里的 token 分配权重,然后按权重汇总信息
例如读这句话:
小明把书放进书包,因为它很重。
这里的“它”更可能指“书”,而不是“书包”。人类理解这句话时,会自动把注意力放到“书”上。模型也需要类似能力:在处理“它”时,从前文里找到更相关的信息。
1. 从生活例子理解注意力
假设你在回答问题:
问题:小明昨天买了一台新电脑,他今天用它写代码。“它”指什么?
你不会平均关注每个词。你会更关注:
- “电脑”
- “用它写代码”
- “买了一台新电脑”
而不太关注:
- “昨天”
- “今天”
- “小明”
这就是注意力的直觉:不同上下文信息的重要程度不同。
模型做的事情也类似,只是它不会用人类语言说“我关注电脑”,而是计算一组数字权重。
2. 一个最简单的注意力例子
假设当前 token 是“它”,上下文里有三个候选信息:
小明 / 电脑 / 桌子 / 它
模型可能计算出这样的注意力权重:
| 当前 token | 关注对象 | 权重 |
|---|---|---|
| 它 | 小明 | 0.10 |
| 它 | 电脑 | 0.75 |
| 它 | 桌子 | 0.10 |
| 它 | 它自己 | 0.05 |
然后模型会按这些权重汇总信息:
新的“它”的表示
= 0.10 × 小明的信息
+ 0.75 × 电脑的信息
+ 0.10 × 桌子的信息
+ 0.05 × 它自己的信息
这样,“它”的向量表示里就会包含更多“电脑”的信息。后续模型生成答案时,就更容易回答“它指电脑”。
3. 为什么不能只看相邻词
早期序列模型常常更依赖局部顺序。但语言里很多关系不是相邻的。
例子:
那位昨天在会议上介绍新项目、并回答了很多问题的工程师,今天请假了。
“请假了”的主语是“工程师”,但中间隔了很多词。
注意力机制的优势是:它允许当前位置直接关注远处 token。
再比如:
如果明天下雨,我们就取消露营,否则按原计划出发。
理解“否则”时,需要回看前面的条件“明天下雨”。这种跨距离关系很适合用注意力建模。
4. Self-Attention 是什么
Self-Attention 指的是:同一个序列内部的 token 互相关注。
例如这句话:
猫坐在垫子上。
每个 token 都可以从句子里的其他 token 获取信息:
| 当前 token | 可能关注 |
|---|---|
| 猫 | 坐、垫子 |
| 坐 | 猫、垫子 |
| 垫子 | 坐、上 |
| 上 | 垫子 |
Self-Attention 的“Self”表示 Query、Key、Value 都来自同一个序列。
在 LLM 中,Self-Attention 让每个 token 的表示不再只是自己,而是融合了上下文后的表示。
5. Q、K、V 的直觉
注意力机制里最容易卡住的是 Q、K、V。
可以用“图书馆检索”来理解:
- Query(Q):我现在想找什么?
- Key(K):每本书的索引标签是什么?
- Value(V):这本书真正包含的内容是什么?
当你搜索“Transformer 注意力”时:
- 你的搜索词是 Query。
- 每本书的标题、标签、摘要是 Key。
- 书的正文内容是 Value。
- Query 和 Key 越匹配,就越应该读取那本书的 Value。
放到模型里:
| 组件 | 直觉 | 作用 |
|---|---|---|
| Q | 当前 token 想找的信息 | 用来匹配其他 token |
| K | 每个 token 提供的索引 | 用来被 Q 匹配 |
| V | 每个 token 提供的内容 | 被加权汇总 |
所以注意力不是直接用 Q 去拿 K,而是:
Q 和 K 算相关性
相关性变成权重
权重去加权汇总 V
6. 为什么要分成 K 和 V
一个常见疑问是:为什么 Key 和 Value 要分开?不能一个向量既做索引又做内容吗?
直觉上,索引和内容本来就不是一回事。
例如一本书:
标题:Python 入门
标签:编程、Python、教程
正文:几百页内容
你检索时先看标题和标签,不会把整本书正文拿来做匹配。但真正读取时,你需要正文内容。
对应到 Attention:
- Key 更像“我是否相关”的匹配信号。
- Value 更像“如果相关,我提供什么内容”。
把 K 和 V 分开,模型可以学习更灵活的关系:一个 token 用一种方式被检索,用另一种方式贡献内容。
7. Attention 的三步计算
标准 Attention 可以拆成三步。
7.1 第一步:计算相关性分数
当前 token 的 Query 会和所有 token 的 Key 做点积。
score = Q · K
点积越大,表示越匹配。
例如当前 token 是“它”:
| Key 对象 | 分数 |
|---|---|
| 小明 | 1.0 |
| 电脑 | 4.0 |
| 桌子 | 0.5 |
| 它 | 0.2 |
这表示“它”和“电脑”的匹配程度最高。
7.2 第二步:用 softmax 变成权重
原始分数需要变成权重,权重总和为 1。
[1.0, 4.0, 0.5, 0.2]
-> softmax
-> [0.04, 0.86, 0.02, 0.08]
softmax 会放大高分项,让模型更集中关注相关 token。
7.3 第三步:加权汇总 Value
最后用这些权重汇总 Value。
output
= 0.04 × V_小明
+ 0.86 × V_电脑
+ 0.02 × V_桌子
+ 0.08 × V_它
这样输出向量就主要吸收了“电脑”的信息。
8. Attention 公式
标准 Scaled Dot-Product Attention 写作:
这条公式可以逐段读:
| 片段 | 含义 |
|---|---|
| 每个 Query 和每个 Key 算相似度 | |
| 缩放因子,避免分数过大 | |
softmax | 把分数变成权重 |
| 乘以 | 用权重汇总内容 |
不用一开始就记公式。只要先记住:
Q 找 K,得到权重,再汇总 V
公式只是把这个过程写成矩阵计算。
9. 一个小矩阵例子
假设有 3 个 token:
我 / 喜欢 / 苹果
每个 token 都会生成自己的 Q、K、V。
Attention 分数矩阵可能长这样:
| 当前 token \ 被关注 token | 我 | 喜欢 | 苹果 |
|---|---|---|---|
| 我 | 0.6 | 0.3 | 0.1 |
| 喜欢 | 0.2 | 0.5 | 0.3 |
| 苹果 | 0.1 | 0.4 | 0.5 |
按行看:
- “我”主要关注自己。
- “喜欢”关注自己,也关注“苹果”。
- “苹果”关注自己,也关注“喜欢”。
每一行都是当前 token 对所有 token 的注意力分布。
注意力矩阵不是固定规则,而是模型根据输入动态算出来的。
10. Causal Mask 是什么
在 GPT 这类自回归模型里,模型生成下一个 token 时不能看到未来。
例如训练句子:
我 喜欢 苹果
预测“喜欢”时,模型不能看到“苹果”;预测“苹果”时,才能看到“我”和“喜欢”。
所以注意力矩阵要加 mask:
| 当前 token \ 可见 token | 我 | 喜欢 | 苹果 |
|---|---|---|---|
| 我 | 可见 | 不可见 | 不可见 |
| 喜欢 | 可见 | 可见 | 不可见 |
| 苹果 | 可见 | 可见 | 可见 |
这叫 causal mask。
它保证模型只能根据过去预测未来。
如果没有 causal mask,模型训练时就会偷看答案,生成能力会失真。
11. 双向注意力与单向注意力
不同模型使用不同注意力可见范围。
| 类型 | 可见范围 | 代表模型 | 适合任务 |
|---|---|---|---|
| 双向 Attention | 可以看左右上下文 | BERT | 理解、分类、Embedding |
| 单向 / Causal Attention | 只能看当前位置之前 | GPT、LLaMA、Qwen | 生成、对话 |
例子:
我把苹果放在桌上,因为它很甜。
BERT 在处理“它”时,可以同时看前后文。GPT 在生成到“它”时,只能看已经生成出来的前文。
这就是为什么 Encoder-only 模型更适合理解任务,而 Decoder-only 模型更适合生成任务。
12. Multi-Head Attention 是什么
单个注意力头只能从一个角度看上下文。Multi-Head Attention 就是让模型从多个角度同时看。
比如句子:
小王把蛋糕给了小李,因为他不饿。
不同 attention head 可能关注不同关系:
| Head | 可能关注 |
|---|---|
| Head 1 | “他”指代谁 |
| Head 2 | “给了”的动作关系 |
| Head 3 | “不饿”和“给蛋糕”的因果关系 |
| Head 4 | 名词之间的距离和位置 |
每个 head 都会独立计算一套 Q/K/V 和注意力权重,最后把结果拼起来,再通过 o_proj 整合。
简化流程:
输入 hidden states
-> 分成多个 head
-> 每个 head 独立做 attention
-> concat
-> o_proj
-> 输出
多头注意力的价值不是简单“多算几遍”,而是让模型可以并行学习多种关系。
13. 例子:指代消解
看这句话:
张三把钥匙交给李四,因为他要出门。
“他”可能指张三,也可能指李四,需要结合语义判断。
如果模型处理“他”,可能会关注:
| token | 注意力 |
|---|---|
| 张三 | 0.45 |
| 钥匙 | 0.05 |
| 李四 | 0.20 |
| 要出门 | 0.25 |
| 其他 | 0.05 |
这个分布表示模型更倾向于把“他”和“张三”关联起来。
但如果句子变成:
张三把钥匙交给李四,因为他忘带钥匙了。
“他”更可能指李四。注意力权重就可能变成:
| token | 注意力 |
|---|---|
| 张三 | 0.20 |
| 钥匙 | 0.15 |
| 李四 | 0.45 |
| 忘带钥匙 | 0.15 |
| 其他 | 0.05 |
注意力是动态的,同一个词在不同上下文里的关注对象会变化。
14. 例子:翻译
翻译时,生成目标词需要关注源句里的相关词。
例如:
英文:The cat sat on the mat.
中文:猫坐在垫子上。
生成“猫”时,模型应该关注 cat。
生成“垫子”时,模型应该关注 mat。
| 生成中文 token | 主要关注英文 token |
|---|---|
| 猫 | cat |
| 坐 | sat |
| 垫子 | mat |
| 上 | on |
这类跨序列注意力在 Encoder-Decoder 模型中叫 Cross-Attention。虽然现代聊天 LLM 多是 Decoder-only,但这个例子很适合理解“注意力就是对齐相关信息”。
15. 例子:代码理解
注意力机制也适合代码。
def add_tax(price):
tax = price * 0.08
return price + tax
模型处理 return price + tax 时,需要知道:
price来自函数参数。tax在上一行定义。tax和price有计算关系。
注意力可以让 return 这一行直接关注前面的变量定义,而不是只依赖相邻 token。
在长代码里,这种能力更重要:
- 函数调用要关注函数定义。
- 变量使用要关注变量声明。
- 类方法要关注类属性。
- import 要影响后续 API 使用。
16. Attention 和 KV Cache 的关系
推理时,模型是一个 token 一个 token 生成的。
如果每生成一个新 token,都重新计算所有历史 token 的 K 和 V,会很浪费。
KV Cache 的作用是缓存历史 token 的 Key 和 Value。
生成新 token 时:
新 token 生成 Q
历史 token 的 K/V 从 cache 里读取
Q 和历史 K 计算注意力
用注意力权重汇总历史 V
所以:
- Q 通常只需要为当前新 token 计算。
- K/V 会为历史 token 缓存起来。
- 上下文越长,KV Cache 越大。
- 并发越高,KV Cache 显存压力越大。
这也是为什么注意力机制和推理显存强相关。
17. Attention 的计算成本
Self-Attention 需要每个 token 和其他 token 计算关系。
如果序列长度是 ,注意力矩阵大致是 。
| 序列长度 | 注意力关系数量 |
|---|---|
| 1,000 | 1,000,000 |
| 10,000 | 100,000,000 |
| 100,000 | 10,000,000,000 |
这就是长上下文昂贵的原因之一。
虽然现代推理框架有 FlashAttention、PagedAttention、KV Cache 优化等技术,但基本事实仍然是:上下文越长,注意力相关成本越高。
18. Attention 权重能解释模型吗
Attention 权重有一定参考价值,但不能简单等同于“模型为什么这么回答”。
原因:
- 模型有很多层,每层都有不同 attention。
- 每层有多个 head。
- FFN 也会大量加工信息。
- 残差连接会混合不同路径的信息。
- 最终输出由整个网络共同决定。
所以可以说:
Attention 权重能帮助观察模型关注了哪里,
但不能完整解释模型为什么这么输出。
19. 常见误区
19.1 注意力就是找关键词
不只是。注意力可以关注关键词,也可以关注语法、位置、格式、指代、因果、代码变量等抽象关系。
19.2 权重最高的 token 一定最重要
不一定。单层单头的权重只是一部分信息。模型最终输出还会经过多层、多头和 FFN。
19.3 注意力越集中越好
不一定。有些任务需要集中关注一个证据,有些任务需要综合多个信息。过度集中可能忽略必要上下文。
19.4 长上下文就是无限记忆
不是。长上下文只是允许模型看到更多 token,不保证模型能稳定使用所有信息。长输入还会增加干扰和成本。
20. 怎么真正理解注意力
可以按这个顺序记:
- 每个 token 都会变成向量。
- 每个 token 的向量会生成 Q、K、V。
- Q 是“我想找什么”。
- K 是“我有什么索引特征”。
- V 是“我真正提供什么内容”。
- Q 和 K 算相似度。
- 相似度经过 softmax 变成权重。
- 权重加权汇总 V。
- 多个 head 从不同角度重复这个过程。
- 多层 Transformer 不断重复,token 表示就越来越包含上下文信息。
最短版本:
Q 找 K,得到权重,汇总 V。
21. 总结
注意力机制让模型在处理每个 token 时,不是只看当前位置,而是动态读取上下文中相关 token 的信息。
它的关键点是:
- Attention 是加权汇总上下文信息。
- Self-Attention 是同一序列内部 token 互相关注。
- Q/K/V 分别对应查询、索引和内容。
- softmax 把相关性分数变成注意力权重。
- causal mask 防止生成模型偷看未来。
- multi-head attention 让模型从多个角度理解上下文。
- KV Cache 让自回归推理可以复用历史 K/V。
理解注意力后,再看 Transformer、KV Cache、长上下文、多头注意力、GQA、LoRA 里的 q_proj/k_proj/v_proj/o_proj,都会更容易串起来。