📑 本页目录(点开跳转)
02 · Transformer 原理
⏱ 36 分钟 | ⭐⭐ 12 个方向里最值得补的一块
🎯 一句话
Transformer 做的事只有一件:让每个词去「看」句子里的其他词,然后根据看到的东西更新自己的含义。这个动作叫注意力(Attention),堆几十层,就成了大模型。
🧠 核心概念一:注意力(Attention)
先看它解决什么问题
句子:「那只猫没抓到它,因为它跑得太快」
第二个「它」指谁?猫还是老鼠?
→ 要理解「它」,必须看句子里的其他词
→ 而且不同的词,重要程度不一样
注意力 = 给每个词分配「该看谁、看多重」的权重。
机制:Q、K、V 三个角色
每个词会生成三个向量,用一个图书馆的类比:
Query (查询) = 「我想找什么」 ← 我这个词的需求
Key (键) = 「我是什么」 ← 每个词的索引标签
Value (值) = 「我的实际内容」 ← 每个词的信息
过程:
① 我的 Q · 每个词的 K → 算出「我和它有多相关」(打分)
② 分数过 softmax → 变成加起来等于 1 的权重
③ 按权重加权求和所有 V → 得到我的新表示
💡 这就是「查字典」:用需求匹配索引,取出内容,按相关度混合
公式(看不懂跳过,下面有人话):
$$\text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V$$
💡 人话翻译:
QK^T= 每个词和每个词的相关度矩阵(就是推荐/搜索里算相似度的那个点积,一模一样)÷√d_k= 防止维度高时数值过大导致 softmax 变得极端softmax= 把分数变成权重(加起来 = 1)×V= 按权重把大家的内容混合起来🔗 如果你做过推荐或搜索:这里的「点积衡量相关度」和矩阵分解里的用户向量·物品向量是同一个思想。 区别只有一个:推荐取 top-k 是"硬选",注意力用 softmax 是"软选"且可微。
🧮 用那个句子走一遍
「那只猫没抓到它,因为它跑得太快」
↑ 处理这个「它」
① 「它」的 Query 大致在问:「我是个代词,我指的是谁?」
② 和每个词的 Key 打分(数字是示意):
那只 0.1
猫 2.8 ← 名词,可能是指代对象
没 0.1
抓到 0.9 ← 动作,提供了语义线索
它 0.3
因为 0.2
跑得 1.5 ← 「跑得快」这个属性很关键
太快 1.4
③ softmax 后 → 「猫」拿到最大权重
④ 加权求和各词的 Value
→ 「它」的新向量里,混进了大量「猫」的信息 ⭐
💡 经过这一层之后,「它」这个 token 的表示,
已经不再是通用的"它",而是"指代猫的那个它"
🔑 这就是"更新自己的含义"的具体过程。 堆几十层,每层都做一次这种融合——词的表示会越来越"上下文化"。
多头注意力(Multi-Head)
一组 QKV 只能捕捉一种关系。所以并行做 多组(如 32 组),每组关注不同的东西:
头1:关注语法主谓关系
头2:关注指代(「它」指谁)
头3:关注远距离的上下文
... (实际上每个头学什么,是自动涌现的,不是人指定的)
↓
把所有头的结果拼起来 → 过一个线性层混合
🧠 核心概念二:一层 Transformer 长什么样
输入向量
│
├──────────────┐
▼ │
多头注意力 │ ← 词之间交流信息
│ │
▼ │
+ 残差相加 ◄──────┘ ← 保留原信息,防止深层退化
│
LayerNorm ← 数值归一化,训练更稳
│
├──────────────┐
▼ │
前馈网络 FFN │ ← 每个词独立地"深加工"(参数量的大头!)
(放大4倍再压回) │
│ │
▼ │
+ 残差相加 ◄──────┘
│
LayerNorm
│
▼
输出(喂给下一层)
堆 32~120 层,就是一个现代大模型。
💡 反直觉的事实:参数量的大头在 FFN(约 2/3),不是注意力。注意力负责「词之间交流」,FFN 负责「存知识」——研究表明事实性知识主要存在 FFN 里。
🔑 一句话记住这个分工
注意力:横向的 —— 【token 之间】交换信息 ← "谁和谁有关系"
FFN: 纵向的 —— 【每个 token 单独】深加工 ← "这个东西是什么"
⭐ 一层 = 一次"看看别人" + 一次"自己想想"
堆 32~120 层 = 反复这两件事
💡 这个分工解释了三件事:
| 现象 | 解释 |
|---|---|
| MoE 为什么只替换 FFN | 因为 FFN 是参数大头,稀疏化它收益最大;而注意力必须每个 token 都参与 |
| 模型"记住"了某个事实存在哪 | 主要在 FFN 里(可以被定位甚至编辑,这是"模型编辑"研究的基础) |
| 为什么加长上下文不增加"知识量" | 上下文影响的是注意力这一侧,FFN 里的知识是训练时固化的 ⭐ |
🧱 每个零件为什么必须存在
| 零件 | 拿掉会怎样 |
|---|---|
| 注意力 | token 之间无法交流,退化成对每个词独立处理 |
| FFN | 只剩加权平均,没有非线性变换能力(🔗 ML 基础第 7 章的异或实验) |
| 残差连接 | 几十层根本训不动——梯度连乘会消失 ⭐ |
| LayerNorm | 数值尺度层层漂移,训练不稳定 |
| 位置编码 | 打乱词序结果完全一样(注意力是置换等变的) |
🔗 《机器学习基础》第 14 章会告诉你: 这些零件除了位置编码,全都是 Transformer 之前就有的。 Transformer 的贡献是组合方式,不是发明新零件。
🧠 核心概念三:因果掩码(为什么它只会「续写」)
预测第 4 个词时,能看到的:
位置1 ✅ 位置2 ✅ 位置3 ✅ 位置4 ❌ 位置5 ❌
→ 不许偷看未来
→ 训练目标就是「根据前面所有词,猜下一个词」
→ 这叫 自回归(Autoregressive)
这解释了一切:为什么模型是「一个字一个字往外蹦」的、为什么它本质上是个超级续写机、为什么第 1 章说预训练完的模型不会当助手。
🔗 做过序列推荐的话:SASRec 用的是一模一样的因果掩码——序列推荐和语言模型是同一套机制。
🧠 核心概念四:KV Cache(连接到你已知的痛点)
生成第 100 个词时,前 99 个词的 K 和 V 其实上一步已经算过了。缓存起来别重算——这就是 KV Cache。
没有 Cache:生成 n 个词,计算量 O(n²),慢到不能用
有 Cache:每步只算新词的 QKV,快几十倍 ✅
代价:显存!
KV Cache 大小 ≈ 2 × 层数 × 头数 × 头维度 × 序列长度 × batch × 精度字节
→ 长上下文时,KV Cache 可能比模型权重还占显存 💀
🔑 这一条解释了两件你早就遇到的事: 1. 为什么长上下文那么贵 —— KV Cache 线性增长,且吃显存 2. 为什么「上下文腐烂」(智能体教程第 4 章)—— 注意力要算 n² 的两两关系,token 越多,每个词分到的注意力越被摊薄
🧮 算一个具体的数
一个 70B 模型(80 层,64 头,头维度 128,FP16):
每个 token 的 KV Cache = 2 × 80 × 64 × 128 × 2 字节 ≈ 2.6 MB
上下文 10,000 token → 26 GB 😨
上下文 100,000 token → 260 GB 💀
⭐ 模型权重才 140GB —— KV Cache 轻松超过它
💡 这就是 MQA / GQA 存在的理由: 让多个 Query 头共享同一组 K 和 V,KV Cache 直接砍到 1/8 甚至 1/64。 代价是一点点效果,换来的是长上下文变得可负担。 🔗 详见第 6 章推理优化。
⏱️ 两个阶段:Prefill vs Decode(理解延迟的关键)
① Prefill(预填充):处理你的输入
· 所有 token【并行】计算 → GPU 吃满
· 计算密集型,受【算力】限制
· 决定了"首字延迟"(TTFT)
② Decode(解码):一个一个吐 token
· 每步只算 1 个 token,但要读【全部】KV Cache
· 内存带宽密集型,受【显存带宽】限制 ⭐
· 决定了"每秒吐几个字"
🔑 这个区分解释了很多实际现象: - 为什么输出 token 比输入贵:decode 阶段 GPU 利用率极低,一次只处理 1 个 token - 为什么 batch 起来更划算:decode 是带宽瓶颈,同时处理多个请求几乎不增加时间 ⭐ - 为什么 Prefix Caching 省这么多:它直接跳过了 prefill 的重复计算
🔗 和你已知的关系
左列有的来自日常用 LLM 的经验,有的在站内别的板块讲过——碰上过哪几条就从哪几条接进来,一条没碰过也不影响读这一章。
| 你可能已经知道的 | 这一章给出的「为什么」 |
|---|---|
| 上下文越长效果越差 | 注意力是 n² 的,且长序列训练数据少、位置编码要外推 |
| 长上下文很贵 | KV Cache 随长度线性增长,显存和带宽都是瓶颈 |
| 模型一个字一个字输出 | 因果掩码 + 自回归,本质是「续写」 |
| SASRec 的因果掩码(做过序列推荐的话) | 完全一样的机制,序列推荐 = 小号语言模型 |
| 点积算相关度 | Attention 的 QK^T 就是点积 |
📦 深挖的话要学什么
真要吃透这一层,需要:
📍 每条后面标了它在哪: 本库有 = 站内已经讲透,点进去就行;要外找 = 站内没有,得去论文或别处。 (这份清单以前只列词不说去哪,指到的坑有些站内根本没挖过,按图索骥会扑空。)
- 架构细节:位置编码(绝对/相对/RoPE)、LayerNorm vs RMSNorm、激活函数(GELU/SwiGLU)、Pre-LN vs Post-LN → 📗 本库有:02b · 现代 LLM 架构的四处改动。这四项正是这一章讲的原始 Transformer 和今天任何一个开源模型之间的全部差别——不看这章,你打开 LLaMA 的代码会一次撞上三个不认识的东西
- 注意力变体:MHA → MQA → GQA(省 KV Cache)、FlashAttention(IO 感知的加速)、滑窗/稀疏注意力、线性注意力 → 📗 本库有:AI基础设施 16 · KV-Cache(MHA/MQA/GQA/MLA 四者的存储形态对比)、AI基础设施 08 · FlashAttention(为什么 IO 感知能快)
- 架构选型:为什么现在的大模型清一色是 Decoder-only?BERT 那条路输在哪? → 📗 本库有:02c · 为什么都是 Decoder-only。这一章花了大力气讲因果掩码「导致只会续写」,但没解释为什么要选这个设计——那一章补的就是这个
- MoE 架构:专家混合,用「稀疏激活」把参数量做大但计算量不涨(现代大模型主流) → 📗 本库有:02d · MoE 混合专家。本章多处把「MoE 只替换 FFN」当既定结论用,那一章讲清楚它凭什么这么做、路由怎么选专家、以及为什么必须有负载均衡损失
- 从零手写:用 PyTorch 实现一个 GPT(约 300 行),在小语料上训起来 —— 这是理解的分水岭 → 📙 半有:注意力的最小可默写实现在 ML基础 · 附录C 手撕代码速查第 2、3 题(缩放点积 + 多头);完整的 300 行训练闭环站内还没有,要外找(nanoGPT 是公认的起点)
- 可视化:看注意力图,观察不同头学到了什么 → 📕 要外找:站内没有交互式注意力可视化(BertViz 一类工具)
需要的前置:Python + 一点线性代数(矩阵乘法)+ PyTorch 基础。不需要微积分推导。
⚖️ 要不要深挖
| 你的情况 | 建议 |
|---|---|
| 只调 API 做应用 | 🟡 看完本章够了,知道 KV Cache 和 n² 就行 |
| 老觉得「知其然不知其所以然」很难受 | ✅ 强烈建议——补完这块,前面学的很多结论会突然通透 |
| 想做微调 / 训练 / 推理优化 | ✅ 必须,后面 04–07 章全建立在这上面 |
| 想进模型团队或做研究 | ✅ 必须,且要手写一遍 |
🔑 我的判断:这是 12 个方向里性价比最高的一块。它不大(手写 GPT 一个周末能搞定),但补完之后,你对整个领域的理解会从「记住了很多结论」变成「知道为什么」。
✅ 检查点
- 注意力里的 Q、K、V 各是什么角色?它和推荐系统的双塔召回什么区别?
- 用「那只猫没抓到它」那个例子说:经过一层注意力后,「它」这个 token 发生了什么?
- 注意力和 FFN 的分工是什么?这个分工解释了哪三件事?
- 五个零件里,拿掉哪个会导致"打乱词序结果不变"?拿掉哪个会导致"几十层训不动"?
- 参数量的大头在注意力还是 FFN?为什么 MoE 只替换 FFN?
- 因果掩码是干什么的?它导致了模型的什么本质特性?
- KV Cache 解决什么问题?70B 模型 10 万 token 大约要多少显存?
- Prefill 和 Decode 分别受什么限制?这解释了哪三个实际现象?
- 用这一章的知识解释:为什么长上下文又贵又容易「腐烂」?
👀 答案
- Q=我想找什么(需求),K=我是什么(索引),V=我的内容。Q·K 算相关度,softmax 成权重,加权求和 V。和双塔召回是同一个思想(点积算相关度),区别是召回取 top-k 硬选、注意力用 softmax 软选且可微。
- 「它」的 Query 在问"我指谁",和「猫」的 Key 打分最高,softmax 后「猫」拿到最大权重,加权求和时「猫」的信息被大量混进「它」的新向量——它从通用的"它"变成了"指代猫的那个它"。
- 注意力是横向的(token 之间交换信息),FFN 是纵向的(每个 token 单独深加工)。解释了:①MoE 为什么只替换 FFN(参数大头,且注意力必须每个 token 参与)②事实性知识存在 FFN 里 ③为什么加长上下文不增加知识量(上下文影响注意力侧,FFN 的知识是训练时固化的)。
- 拿掉位置编码 → 打乱词序结果不变(注意力是置换等变的);拿掉残差连接 → 几十层训不动(梯度连乘消失)。
- FFN(约 2/3)。MoE 只替换 FFN 是因为它是参数大头、稀疏化收益最大,而注意力必须每个 token 都参与不能稀疏。
- 禁止看未来的词,强制模型学"根据前文猜下一个词"(自回归)。导致模型本质是续写机,只能一个字一个字生成。
- 避免每生成一个词就重算前面所有词的 K/V。70B 模型每 token 约 2.6MB,10 万 token ≈ 260GB——比 140GB 的模型权重还大。这就是 MQA/GQA(共享 KV)存在的理由。
- Prefill 受算力限制(所有 token 并行,GPU 吃满,决定首字延迟);Decode 受显存带宽限制(每步只算 1 个 token 却要读全部 KV Cache)。解释了:①输出比输入贵(decode 时 GPU 利用率极低)②batch 起来几乎不增加时间(带宽瓶颈)③Prefix Caching 省很多(跳过 prefill 重复计算)。
- 贵:KV Cache 随长度线性增长,占显存和带宽,decode 阶段每步都要读完整个 Cache。腐烂:注意力要算 n² 的两两关系,token 越多每个词分到的注意力越稀薄;加上长序列训练数据少、位置编码要外推。
🛑 可以停在这里
⚡ 走神救援
Transformer=让每个词看其他词并更新自己。注意力用QKV:Q·K算相关度→softmax成权重→加权求和V(就是点积,和推荐的双塔同源,区别只是硬选vs软选)——⭐走一遍「猫…它」的例子:「它」的Q问"我指谁",「猫」的K得分最高,加权后猫的信息混进「它」的向量 → 它从通用的"它"变成"指代猫的它"。多头=并行多组捕捉不同关系。一层=注意力+残差+LayerNorm+FFN。⭐分工:注意力是横向的(token之间交换信息),FFN是纵向的(每个token单独深加工);FFN是参数大头(2/3)且存知识——这解释了 MoE 为什么只替换FFN、以及为什么加长上下文不增加知识量。五零件拿掉的后果:没位置编码→打乱词序结果不变;没残差→几十层训不动。因果掩码→只能续写(自回归)。KV Cache避免重算但吃显存——70B模型10万token要260GB,比140GB的权重还大,这就是MQA/GQA的理由。⭐Prefill受算力限制(并行,决定首字延迟)、Decode受显存带宽限制(每步算1个token却要读全部Cache) → 解释了输出比输入贵、batch起来几乎不增加时间、Prefix Caching 为什么省。性价比最高的一块,建议补。
下一节 👉 02b-现代LLM架构的四处改动.md
🧭 接下来三章(02b / 02c / 02d)是这一章的下游,讲的是「原始 Transformer 到今天的模型,中间改了什么、为什么」。 赶时间可以先跳到 03-Tokenizer与上下文.md,等真要读模型代码或被问到架构细节时再回来。