从零开始理解大模型(四):Attention——大模型的"阅读理解"机制

举报
AGENT魔方 发表于 2026/04/16 09:47:14 2026/04/16
【摘要】 本文为「从零开始理解大模型」系列第四篇,本系列共十篇,从 “下一个词预测” 逐步构建完整的大模型心智模型,每篇均附带可运行代码。本篇聚焦 Attention 机制,讲解大模型如何实现 “阅读理解”,带你理解其核心注意力原理。

专栏1.png

欢迎阅读「从零开始理解大模型」系列 —— 十篇文章,从"下一个词预测"到完整的大模型心智模型。每篇配可运行代码。

  • 第一篇:一切从"猜下一个词"开始
  • 第二篇:Token——大模型眼中的"字"长什么样
  • 第三篇:向量与 Embedding——把文字变成数学
  • 第四篇:Attention——大模型的"阅读理解"机制(本文)
  • 第五篇:Transformer 全景——积木怎么搭成大厦
  • 第六篇:训练——70 亿个参数是怎么"学"出来的
  • 第七篇:推理——你按下回车后的这一秒发生了什么
  • 第八篇:上下文窗口——大模型的"工作记忆"
  • 第九篇:Scaling Law——为什么"大力出奇迹"有效
  • 第十篇:从大模型到 Agent——下一个词预测如何长出手脚
* 本文实操配套代码附件,可在本公众号后台回复“大模型”获取。

作者:十一

上一篇结尾的链路图中间有一个最大的黑盒:

[向量₁', 向量₂', 向量₃']     ← Embedding + 位置编码(第三篇)
        │
        ▼ 进入 Transformer
        │  ← 这里面发生了什么?
        ▼
P("much") = 99.2%

我们知道 Embedding 给每个词发了一张"特征身份证",但也看到了它的局限——"bank" 不管出现在"I deposited money at the bank"还是"The river bank",查出来的初始向量都一样。模型需要一种机制来读上下文,根据周围的词判断 "bank" 是"银行"还是"河岸",修正每个向量的含义。

这个机制就是本篇的主角——Attention(注意力机制)

一、先说结论

1.png

句话总结:Attention 让每个词能"看见"上下文中的所有其他词,并决定该重点关注谁。

二、用"图书馆找书"来理解 Attention

想象你走进图书馆,要找关于"量子物理"的信息。

你不会把每本书从头到尾读一遍——你会先扫视书架上每本书的标签,大脑自动给它们打分:《量子力学入门》95 分、《经典力学》40 分、《烹饪指南》0 分。然后你把绝大部分精力花在读高分那本书的内容上。

Attention 做的就是这个过程,对应三个角色:

  • Query(Q):"量子物理"——你带着的搜索需求
  • Key(K):书架上每本书的标签——用来和你的需求做匹配
  • Value(V):每本书的实际内容——匹配成功后你真正要读的东西

完整流程:Q 和每个 K 匹配打分 → 分数高的权重大 → 按权重把所有 V 的内容加起来。

回到大模型:模型在处理 "Thank you very ___" 时,最后一个位置(要预测的位置)带着自己的 Query 去"扫视"前面所有词的 Key,发现 "Thank" 和 "very" 的分数最高,于是把它们的 Value 信息重点融合进来——这样 "much" 就获得了最高的预测概率。

三、Q、K、V 怎么来的

每个 token 经过 Embedding 后是一个向量(比如 768 维)。Attention 不直接用这个原始向量,而是通过三个权重矩阵,变换出三个不同角色的向量:

原始向量 (768维)
    ├── × W_Q ──→ Query  (64维)   "我在找什么?"
    ├── × W_K ──→ Key    (64维)   "我能提供什么索引?"
    └── × W_V ──→ Value  (64维)   "我的实际内容是什么?"

为什么要三个不同的向量?因为同一个词在不同角色下需要展示不同的面。比如 "Thank":作为 Query 时它在找和自己搭配的词;作为 Key 时它广播自己是一个感谢用语;作为 Value 时它提供"感谢"的具体语义。

W_Q、W_K、W_V 就是权重——训练时调整,推理时固定。第三篇提到的"开源权重中 Attention 占约 49%",主要就是这些矩阵。

四、Attention 的三步计算

整个过程可以用一个公式概括:

Attention(Q, K, V) = softmax(Q · Kᵀ / √d) · V

别被公式吓到——它只是下面三步的缩写。Q · Kᵀ 是第一步(打分),softmax 是第二步(归一化),· V 是第三步(加权求和),√d 是缩放因子防止数值太大。

以 "Thank you very" 为例,3 个 token 已经通过 Embedding 变成了向量,现在做 Attention。

4.1 第一步:打分(公式中的 Q · Kᵀ / √d

每个词的 Query 和所有词的 Key 做点积,得到相关度分数:

             Key
             Thank   you   very
Query Thank [ 0.8    0.2   0.3 ]
      you   [ 0.3    0.5   0.4 ]
      very  [ 0.6    0.2   0.7 ]    ← "very" 关注 "Thank" 和自己

分数怎么算的? 以 "very" 对 "Thank" 的 0.6 为例:把 "very" 的 Query 向量和 "Thank" 的 Key 向量逐元素相乘再求和(点积):

Q(very) · K(Thank) = q₁×k₁ + q₂×k₂ + ... + q₆₄×k₆₄ = 0.6

点积越大,说明两个向量方向越一致——Q 要找的和 K 能提供的越匹配。这和第三篇的余弦相似度是同一个直觉。

实际模型还会除以 √d(如 √64 = 8),防止点积数值过大导致 Softmax 输出太极端。

"打分"到底在打什么分? 这个分数同时承载了三层含义:

相似度分。 从向量数学看,点积衡量的是方向一致性。Q(very) 和 K(Thank) 方向接近 → 分数高 → 模型认为当前查询需求和这个位置的信息高度匹配。

贡献度分。 这个分数决定了对应的 Value 应该贡献多少信息。分高 → 这个位置的内容对当前上下文重要;分低 → 是"噪声",在后续加权求和时会被过滤掉。

注意力分配分。 面对一串 token,模型不可能给所有词同等关注。打分就是在回答:"为了理解当前的词,我应该花多少精力去'看'其他词?"——这就是"注意力"这个名字的由来。就像你在图书馆不会平均阅读每本书,而是把精力集中在最相关的那几本上。

4.2 第二步:归一化(公式中的 softmax

把每一行的分数转成概率(加起来等于 1):

              Key(归一化后)
             Thank   you   very
Query Thank [ 0.46   0.24  0.30 ]
      you   [ 0.27   0.33  0.40 ]
      very  [ 0.36   0.24  0.40 ]   ← 40% 注意力给自己,36% 给 "Thank"

怎么算的? 以 "very" 那行 [0.6, 0.2, 0.7] 为例,Softmax 先对每个分数取 e 的指数,再除以总和:

e^0.6 = 1.82,  e^0.2 = 1.22,  e^0.7 = 2.01
总和 = 5.05

归一化: [1.82/5.05, 1.22/5.05, 2.01/5.05] = [0.36, 0.24, 0.40]

效果:分数高的被放大,低的被压缩,加起来恰好等于 1——变成了概率分布。这和第一篇预测下一个词时的 Softmax 是同一个操作。

4.3 第三步:加权求和(公式中的 · V

用注意力分布作为权重,把所有词的 Value 向量加权求和:

"very" 的新向量 = 0.36 × Value(Thank)    ← 贡献最大
               + 0.24 × Value(you)
               + 0.40 × Value(very)

结果:"very" 的向量里融合了 "Thank" 的信息。当模型用这个新向量预测下一个词时,它"知道"前面是 "Thank you very"——所以 "much" 的概率被大幅提高。

Attention 的全部运算就是这三步:打分 → 归一化 → 加权求和。 没有循环,没有条件判断,都是矩阵乘法。

五、实际看 Attention 分数

用代码可以提取 GPT-2 真实的 Attention 矩阵:

outputs = model(input_ids, output_attentions=True)
attention = outputs.attentions[0][0, 0]  # Layer 0, Head 0
# attention.shape: [3, 3]  ← 3 个 token 之间的注意力分数

对 "Thank you very" 的实际结果(文末 attention.py 可以复现):

Layer 0, Head 0:
              Thank    you   very
  Thank │  1.000      ·      ·    ← 只能看自己
    you │  0.960  0.040      ·    ← 96% 注意力给了 "Thank"
   very │  0.670  0.180  0.149    ← 67% 注意力给了 "Thank"

几个关键观察:

"Thank" 那行只有一个 1.000。 它是第一个词,只能看自己(因果掩码),所以 100% 注意力在自己身上。

"you" 和 "very" 都重点关注 "Thank"。 这很合理——"Thank" 是这句话的核心语义(感谢),后面的词都需要从它那里获取信息来做出正确预测。

右上角全是 ·(零)。 每个词只能看自己和前面的词,不能看后面——这就是因果掩码(Causal Mask)

这就是因果掩码(Causal Mask)——推理时后面的词还没生成,模型不能偷看未来。矩阵右上角全是 0:

✓ ✗ ✗     "Thank" 只能看自己
✓ ✓ ✗     "you" 能看前 1 个词
✓ ✓ ✓     "very" 能看所有词

六、多头注意力:同时从多个角度看

GPT-2 每一层有 12 个注意力头,每个头独立做一套 Q/K/V 计算。

为什么要多个头?因为一句话需要从多个角度理解

# 提取所有 12 个头对 "very" 的注意力分布
for head in range(12):
    att = outputs.attentions[0][0, head, -1, :]  # "very" 的注意力
    max_token = tokens[att.argmax()]
    print(f"Head {head}: 最关注 '{max_token}'")

不同的头会关注不同的东西——有的头关注语法搭配("very" → "Thank"),有的关注相邻位置("very" → "you"),有的关注自身。12 个头从 12 个角度读同一句话,拼在一起就是全面的理解。

没有人告诉 Head 0 去关注语法、Head 3 去关注位置。这些分工是训练过程中自动形成的——每个头学到了一种对预测下一个词有用的关注模式。

每个头的 Q/K/V 维度是 64(= 768 / 12),12 个头的结果拼接起来还是 768 维:

Multi-Head Attention = Concat(Head₁, Head₂, ..., Head₁₂) × W_O

每个 Headᵢ = Attention(Q_i, K_i, V_i)    // 独立计算,64 维
拼接后: 12 × 64 = 768 维
W_O: 输出投影矩阵,768 × 768

完整的多头注意力分析代码见附件 multi_head.py

* 本系列文章完整实操配套代码,可在公众号后台回复“大模型”获取。

七、Attention 在完整链路中的位置

把第四篇学到的内容嵌入之前的链路:

"Thank you very"
        │
        ▼ 分词(第二篇)
[10449, 345, 845]
        │
        ▼ Embedding + 位置编码(第三篇)
[向量₁, 向量₂, 向量₃]               ← 初始向量
        │
        ▼ Attention(本篇)—— 打开的黑盒
        │  每个向量生成 Q、K、V
        │  Q · Kᵀ / √d → 打分
        │  softmax → 注意力分布
        │  加权求和 V → 融合上下文
        │  12 个头并行,结果拼接
        ▼
[新向量₁', 新向量₂', 新向量₃']      ← 融合了上下文
        │
        ▼ 还有 FFN、残差连接...(第五篇)
        ▼ 重复 12 层
        ▼ → softmax → P("much") = 99.2%

但 Attention 只是一层中的一半——另一半是 FFN(前馈网络)和"胶水"组件(残差连接、LayerNorm)。它们一起构成一个完整的 Transformer 层,堆叠几十层才是最终模型。这是下一篇的主题。

八、n² 的代价

Attention 的注意力分数矩阵是 n × n(n 为 token 数)。每个词要和所有词算相关度:

输入 100 tokens   → 100 × 100 = 10,000 次计算
输入 1,000 tokens → 1,000² = 1,000,000 次
输入 10,000 tokens → 10,000² = 100,000,000 次

长度增 10 倍,计算量增 100 倍。这就是为什么长文本慢、显存占用大、各种"高效 Attention"变体都在想办法降低 n²。第八篇(上下文窗口)会详细讲。


九、结语

Attention 不神秘。

它做的事情用一句话就能说清:让每个词看看上下文中的所有词,算出该关注谁,然后把被关注词的信息融合到自己身上。

三步运算——打分、归一化、加权求和。多个头并行,从不同角度做同样的事。因果掩码确保只能看过去,不能偷看未来。

"Attention is all you need." — Vaswani et al., 2017

2017 年那篇论文的标题恰如其分。Attention 让每个词能一次性看到整个上下文,这个简单的改变带来了革命性的效果。

下一篇,我们把 Attention、FFN、残差连接、LayerNorm 组装起来,看一个完整的 Transformer 层长什么样。


本文配套代码:attention.py(Attention 矩阵可视化)、multi_head.py(多头注意力分析)。需要 Python 3.8+、transformers、torch。


容器模仿.png

扫码回复“大模型

获取本系列文章完整配套代码


从零开始理解大模型」是「从零开始理解 Agent」的姊妹系列。Agent 系列讲"四肢",本系列讲"大脑"。建议对照阅读 专栏入口


   今日推荐 

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。