0. 为什么人人都在谈 Transformer
2017 年 Google 的论文 Attention Is All You Need 提出了 Transformer。它几乎不用 RNN/CNN 的串行或局部卷积,而是靠 自注意力(Self-Attention) 一次性建模序列里任意两个位置的关系。
后来:
- BERT 等 Encoder 模型把「理解类任务」推上新高度;
- GPT 等 Decoder-only 模型把「生成」做成大模型范式;
- 图像(ViT)、多模态、推荐、代码生成……底层骨架多半还是 Transformer。
一句话:Transformer = 可并行的全局注意力 + 残差 + 归一化 + 前馈网络,再叠很多层。
1. 一张图看懂整体结构
原始 Transformer 是 Encoder–Decoder 结构(机器翻译最典型):
源句子 → [Embedding + 位置编码]
↓
N × Encoder Block
↓
编码矩阵 C(全局上下文)
↓
目标句子 → [Embedding + 位置编码] → N × Decoder Block → Linear + Softmax → 下一个词概率
1.1 三步直觉流程
-
输入表示
每个 token 的向量 = 词嵌入(语义) + 位置信息(相加,维度相同)。 -
Encoder
源序列所有位置互相可见,输出矩阵:
C∈Rn×d C \in \mathbb{R}^{n \times d} C∈Rn×d
其中 nnn 为序列长度,d=dmodeld = d_{\mathrm{model}}d=dmodel(论文默认 512)。
每一层输入输出形状一致,可叠多层。
- Decoder
按目标语序生成:训练可用 Teacher Forcing + 因果掩码 并行;推理则 自回归 逐 token 生成。
每个 Decoder 层里通常有:- 掩码自注意力:只看已生成的目标端上文;
- 交叉注意力:Query 来自 Decoder,Key/Value 来自 Encoder 的 CCC;
- FFN + 残差 + 归一化。
1.2 为什么每一层 Decoder 都要直接读 Encoder 的 CCC?
不是「只把 CCC 给第一层,再一层层往下传」。
| 角度 | 只给第一层再下传 | 每层独立读 CCC(真实设计) |
|---|---|---|
| 信息 | 全局源端信息被第一层压缩,后面易稀释 | 每层都可按当前 Query 重新取源端细节 |
| 对齐粒度 | 高层难回看细粒度词对齐 | 低层词对齐、高层语义对齐可同时学 |
| 梯度 | 回传到 Encoder 路径更长 | 多层交叉注意力提供多条直连梯度路径 |
| 分工 | 自注意与交叉注意混在一起 | 自注意纵向传目标上下文;交叉注意横向直连源端记忆 |
通俗比喻:翻译时,你要记住自己已经写出的中文(自注意),但每写一句都还应 重新对照完整英文原文(交叉注意),而不是只看上一句草稿。
2. 输入侧:词嵌入 + 位置编码
2.1 词嵌入(Token Embedding)
- 查表:
token_id → d_model 维向量 - 可预训练(Word2Vec/GloVe)或端到端训练
- 实现上常见缩放:乘以 dmodel\sqrt{d_{\mathrm{model}}}dmodel(Annotated Transformer 风格)
2.2 为什么必须有位置信息?
自注意力对位置置换近似对称:没有位置,就分不清「猫追狗」和「狗追猫」。
RNN 靠逐步读入带顺序;Transformer 并行算全局注意力,必须显式注入位置。
流程:
分词 → Token Embedding(语义) + 位置信息 → 送入 Transformer
2.3 正弦位置编码(原论文绝对位置编码)
对位置 pos\mathrm{pos}pos、维度索引 i=0,1,…,dmodel/2−1i = 0,1,\ldots,d_{\mathrm{model}}/2-1i=0,1,…,dmodel/2−1:
PE(pos, 2i)=sin(pos100002i/dmodel) PE_{(\mathrm{pos},\,2i)} = \sin\left(\frac{\mathrm{pos}}{10000^{2i/d_{\mathrm{model}}}}\right) PE(pos,2i)=sin(100002i/dmodelpos)
PE(pos, 2i+1)=cos(pos100002i/dmodel) PE_{(\mathrm{pos},\,2i+1)} = \cos\left(\frac{\mathrm{pos}}{10000^{2i/d_{\mathrm{model}}}}\right) PE(pos,2i+1)=cos(100002i/dmodelpos)
要点:
- 偶数维 sin、奇数维 cos;
- 无训练参数,公式生成固定表,与词向量 逐元素相加;
- 相对位置可用三角恒等式表达:PEpos+kPE_{\mathrm{pos}+k}PEpos+k 与 PEposPE_{\mathrm{pos}}PEpos 存在固定线性关系,模型有机会学到相对距离;
- 比「训练时最长 20、推理 21 就挂」的可学习绝对位置表,对略超长序列更友好——但长文本外推仍有限。
2.4 常见位置编码对比(科普版)
| 方式 | 怎么做 | 类型 | 长文本外推 | 代表 |
|---|---|---|---|---|
| 正弦 PE | 词向量 + 位置向量 | 绝对 | 一般 | 原版 Transformer |
| 可学习 PE | 位置向量当参数训 | 绝对 | 超出训练长度易崩 | BERT |
| RoPE | 对 Q/K 做旋转,把位置编进角度 | 相对友好 | 较好,常配合缩放/YaRN 等 | LLaMA、Qwen、DeepSeek |
| ALiBi | 注意力分数按距离加偏置衰减 | 相对 | 外推友好 | 部分开源模型 |
RoPE 直觉(别吹过头):
- 对位置 mmm 的 QQQ、位置 nnn 的 KKK 旋转后,点积主要依赖相对距离 m−nm-nm−n;
- 角度可对任意
pos计算,比固定长度绝对表更容易做长度外推; - 工程上仍常用 Linear Scaling / YaRN / 动态 NTK 等增强超长上下文,不是「装上 RoPE 就无限完美外推」。
常见误区:说「RoPE 自带强远程衰减」——衰减设计更典型的是 ALiBi;RoPE 的优势重点在相对位置与外推友好,不要和 ALiBi 混谈。
2.5 词嵌入 vs 位置编码
| 词嵌入 | 位置编码 | |
|---|---|---|
| 回答 | 这个词是什么意思 | 它在第几个位置 / 与其他词差多远 |
| 维度 | dmodeld_{\mathrm{model}}dmodel | 必须同维才能相加 |
| 融合 | 语义 + 位置 → 编码器输入 |
3. 自注意力:Transformer 的发动机
3.1 Q、K、V 是什么
对输入矩阵 X∈Rn×dX \in \mathbb{R}^{n \times d}X∈Rn×d(每行一个 token):
Q=XWQ,K=XWK,V=XWV Q = XW^{Q},\quad K = XW^{K},\quad V = XW^{V} Q=XWQ,K=XWK,V=XWV
| 名字 | 角色(检索比喻) |
|---|---|
| Query QQQ | 我现在想查什么 |
| Key KKK | 每条内容的索引标签 |
| Value VVV | 真正取出的内容 |
3.2 缩放点积注意力(Scaled Dot-Product Attention)
Attention(Q,K,V)=softmax(QK⊤dk)V \mathrm{Attention}(Q,K,V) = \mathrm{softmax}\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dkQK⊤)V
分步:
- 相似度:QK⊤QK^{\top}QK⊤,得到 n×nn \times nn×n 的分数(谁关注谁);
- 缩放:除以 dk\sqrt{d_k}dk。dkd_kdk 大时点积方差变大,softmax 易饱和 → 梯度变小;缩放稳住训练;
- Mask(可选):把不该看的位置设为很大的负数(如 −∞-\infty−∞ 或
-1e9),softmax 后权重 ≈ 0; - Softmax:对 每一行(每个 Query)归一化,得到注意力权重;
- 加权求和:权重乘 VVV,输出与输入序列等长的上下文向量。
表述修正:注意力整体 不是纯线性。WQ,WK,WVW^{Q},W^{K},W^{V}WQ,WK,WV 是线性,但 softmax 是非线性,因此整模块能做复杂的内容选择与混合。
3.3 三种注意力的 QKV 来源
| 类型 | Q | K | V | 作用 |
|---|---|---|---|---|
| Encoder 自注意力 | 来自 Encoder | 同左 | 同左 | 源句双向全局建模 |
| Decoder 掩码自注意力 | 来自 Decoder | 同左 | 同左 | 目标端只能看过去,自回归 |
| 交叉注意力 | 来自 Decoder | 来自 Encoder 的 CCC | 来自 CCC | 目标位置对齐源端信息 |
3.4 多头注意力(Multi-Head Attention)
不是只算一次注意力,而是切成 hhh 个头,各自一套投影,在子空间里学不同模式(句法、指代、短距/长距等),再拼接投影回去:
headi=Attention(QWiQ,KWiK,VWiV) \mathrm{head}_i = \mathrm{Attention}(QW_i^{Q}, KW_i^{K}, VW_i^{V}) headi=Attention(QWiQ,KWiK,VWiV)
MultiHead(Q,K,V)=Concat(head1,…,headh)WO \mathrm{MultiHead}(Q,K,V) = \mathrm{Concat}(\mathrm{head}_1,\ldots,\mathrm{head}_h)W^{O} MultiHead(Q,K,V)=Concat(head1,…,headh)WO
常见设定:dmodel=512d_{\mathrm{model}}=512dmodel=512,h=8h=8h=8,则 dk=dv=64d_k=d_v=64dk=dv=64。
输出形状与输入 XXX 一致,方便残差相加。
实现提示(代码里易错):
- 投影后 reshape 为
[batch, head, seq, d_k]再算注意力; - mask 广播维度应对齐 head,常见是在 head 维
unsqueeze,不要搞错维; - 因果 mask 用 下三角为 1(可见)、上三角为 0(屏蔽) 时,应对
mask==0填大负数。
4. Encoder 积木:Attention + Add&Norm + FFN
4.1 原论文结构:Post-LN
原版 Transformer 子层公式是:
LayerNorm(x+Sublayer(x)) \mathrm{LayerNorm}\big(x + \mathrm{Sublayer}(x)\big) LayerNorm(x+Sublayer(x))
即:先子层,再残差相加,再 LayerNorm(Post-LN)。
每个 Encoder Block 大致:
x
→ Multi-Head Self-Attention
→ 残差 + LayerNorm
→ Feed Forward
→ 残差 + LayerNorm
→ 输出(形状仍为 n × d)
4.2 现代大模型更常见:Pre-LN
x → LayerNorm → Attention → +x
→ LayerNorm → FFN → +x
残差旁路尽量「干净」直通。
LLaMA、Qwen 等多为 Pre-LN;BERT 仍是 Post-LN 一类设计。
不要写成「BERT、LLaMA 都是 Pre-LN」。
4.3 残差连接在干什么
xout=x+F(x) x_{\mathrm{out}} = x + \mathcal{F}(x) xout=x+F(x)
- xxx:旁路原始输入;
- F(x)\mathcal{F}(x)F(x):Attention 或 FFN 的变换结果(工程上是前向输出;数学上可看成相对 xxx 的修正量)。
三大作用:
- 缓解深层梯度消失:反向时有一条近似「+1」的直通路径;
- 学增量更容易:必要时 F(x)≈0\mathcal{F}(x)\approx 0F(x)≈0,等价跳过该子层;
- 保留底层信息:词义、位置等不易被多层非线性冲掉。
4.4 为什么 NLP 用 LayerNorm 而不是 BatchNorm?
| BatchNorm | LayerNorm | |
|---|---|---|
| 统计范围 | 跨 batch 样本 | 单样本特征维内 |
| 小 batch | 统计不稳 | batch=1 也稳定 |
| 变长 + Padding | 易被 PAD 污染 | 不受其他句/PAD 牵连 |
| 训练/推理 | 推理常用滑动均值 | 公式一致 |
| 典型场景 | CNN 图像 | Transformer / 变长序列 |
补充:RMSNorm 去掉均值中心化、只做均方根缩放,更快,LLaMA/Qwen 常用。
4.5 FFN:为什么常是「先升维再降维」?
原论文 FFN(逐位置、两层全连接):
FFN(x)=max(0,xW1+b1)W2+b2 \mathrm{FFN}(x) = \max(0, xW_1+b_1)W_2+b_2 FFN(x)=max(0,xW1+b1)W2+b2
形状直觉(以 768 为例):768→3072→768768 \rightarrow 3072 \rightarrow 768768→3072→768(约 4 倍)。
怎么理解:
- 注意力:主要在序列维上混合不同 token 的信息;
- FFN:对 每个 token 自己的特征 做非线性变换,提高容量;
- 升维 → 激活(ReLU/GELU/SwiGLU 等)稀疏筛选 → 降维回 dmodeld_{\mathrm{model}}dmodel 以配合残差。
注意:
- 「4 倍」是经典经验折中,不是物理定律;现代模型有 SwiGLU 等变体,中间维设定会不同;
- 原论文中间层是 ReLU;很多 BERT 实现用 GELU;当代 LLM 常用 SwiGLU 等。
5. Decoder 重点:掩码、训练并行、推理串行
5.1 因果掩码(Causal / Look-ahead Mask)
生成第 iii 个词时,不能看见 i+1,i+2,…i+1,i+2,\ldotsi+1,i+2,…。
- 在 QK⊤QK^{\top}QK⊤ 上对「未来位置」加 −∞-\infty−∞;
- Softmax 后未来权重 ≈ 0;
- 因而 ZiZ_iZi 只聚合「自己 + 过去」的 VVV。
命名别绕晕:
- 说 「下三角可见」:保留对角线及以下;
- 说 「上三角屏蔽」:把右上角未来置为不可见。
两种说法描述同一件事,写文章时固定一种并配一张示意即可。
5.2 训练 vs 推理
| 训练 | 推理生成 | |
|---|---|---|
| 目标端输入 | 完整标准答案(Teacher Forcing) | 上一步模型自己吐出的 token |
| 序列维计算 | 可并行(一次算整句各位置损失) | 必须串行自回归 |
| 防泄密 | 因果掩码 | 天然只拼已生成前缀;Prefill 仍要因果逻辑 |
| 优化 | — | KV Cache 避免重复算历史 K/V |
类比:
- 训练 = 对着标准答案整张卷子一起算分;
- 推理 = 闭卷写作文,写完上句才能写下句。
5.3 交叉注意力再强调
- QQQ:当前解码位置「我需要什么」;
- K,VK,VK,V:源端编码 CCC「原文里有什么」;
- 不需要对源端做未来掩码(源句在编码阶段已全部可见)。
最后 Decoder 顶层接线性层 + Softmax,得到词表上的下一个 token 分布。
6. 推理优化:KV Cache(只在生成时)
6.1 痛点
自回归每步都要和全部历史做注意力。若每步把历史 Q/K/V 全重算,浪费极大。
6.2 做法
- 只缓存历史的 K、V(各层各自一份);
- 新 token 只算自己的 Q/K/V,K/V 追加进缓存,Q 与 全部历史 K 做注意力。
为什么不缓存 Q?
Q 表示 当前步 的查询,步步不同;K/V 是历史内容的键值,可复用。
6.3 Prefill 与 Decode
用户 Prompt
→ Prefill:处理整段 prompt,写入 KV,得到首个生成 token
→ Decode:每次 1 个新 token,读缓存 + 追加,直到 EOS
| Prefill | Decode | |
|---|---|---|
| 输入 | 整段 prompt | 单 token |
| KV | 首次写入 | 读历史 + 追加 |
| 常见瓶颈 | 算力 | 显存带宽 |
| 指标语感 | 首字时延 TTFT | 续写流畅度 |
复杂度表述(严谨一点):
相对「每步重算全部历史」,逐步 decode 的边际成本可降到与当前长度近似线性相关;生成长度为 nnn 时,总注意力代价仍常是 O(n2)O(n^2)O(n2),只是常数与工程实现好很多。科普里说「用显存换重复计算」比绝对说「从 O(n2)O(n^2)O(n2) 变成 O(n)O(n)O(n)」更稳妥。
6.4 显存压力与常见缓解
- GQA/MQA:多组 Q 共享少量 KV 头,缓存缩小;
- KV 量化(INT8/INT4);
- PagedAttention(分页管理 KV,提吞吐);
- 滑动窗口 / 只保留近期 KV 等。
7. 分词与 OOV:大模型输入的前站
7.1 OOV(Out-of-Vocabulary)
词表外的词。纯词表 + [UNK] 会让不同新词共用一个向量,语义糊掉。
7.2 粒度对比
| 粒度 | 单元 | OOV | 序列长度 | 现状 |
|---|---|---|---|---|
| 词级 | 整词 | 易 OOV | 短 | 传统 NLP |
| 字符级 | 字/字母 | 几乎无 | 很长 | 辅助/特殊场景 |
| 子词 | 高频片段 | 极少 | 适中 | 工业标配 |
| 字节级 BBPE | UTF-8 字节 | 理论上无 | 偏长,靠合并压 | GPT 系底层思路 |
7.3 三种子词算法
| 算法 | 合并依据 | 特点 | 代表 |
|---|---|---|---|
| WordPiece | 更偏语言模型似然增益 | 常带 ## 续片标记 | BERT |
| BPE / BBPE | 高频字节对/字符对 | 生成模型常用 | GPT、LLaMA |
| SentencePiece | BPE 或 Unigram,可直接吃原始句 | 中日韩、多语言友好 | T5 等 |
原则:高频整词保留,低频/新词拆成词表内片段,在语义完整与无 OOV 之间折中。
8. 和 ResNet 的关系(一句话串起来)
ResNet 用
H(x)=F(x)+x H(x)=F(x)+x H(x)=F(x)+x
解决深度 CNN 退化与梯度问题;Transformer 把同一思想搬到 NLP:每个子层都残差,再配合 LN,才能稳稳堆 12、24、甚至上百层。
- ResNet:偏局部卷积、视觉;
- Transformer:偏全局注意力、序列与多模态。
9. 极简公式速查
位置编码(正弦)
PEpos,2i=sin (pos100002i/d),PEpos,2i+1=cos (pos100002i/d) PE_{\mathrm{pos},2i}=\sin\!\left(\frac{\mathrm{pos}}{10000^{2i/d}}\right),\quad PE_{\mathrm{pos},2i+1}=\cos\!\left(\frac{\mathrm{pos}}{10000^{2i/d}}\right) PEpos,2i=sin(100002i/dpos),PEpos,2i+1=cos(100002i/dpos)
缩放点积注意力
Attention(Q,K,V)=softmax (QK⊤dk)V \mathrm{Attention}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dkQK⊤)V
残差 +(Post)归一化
y=LayerNorm(x+Sublayer(x)) y=\mathrm{LayerNorm}\big(x+\mathrm{Sublayer}(x)\big) y=LayerNorm(x+Sublayer(x))
FFN(原论文)
FFN(x)=ReLU(xW1+b1)W2+b2 \mathrm{FFN}(x)=\mathrm{ReLU}(xW_1+b_1)W_2+b_2 FFN(x)=ReLU(xW1+b1)W2+b2
10. 一篇文章建议记住的 12 句话
- Transformer 用自注意力并行建模全局依赖,不再依赖 RNN 逐步递推。
- 输入 = 语义嵌入 + 位置信息。
- Attention 用 Q 去匹配 K,再按权重汇总 V。
- 除以 dk\sqrt{d_k}dk 是为了训练稳定。
- Multi-Head = 多组子空间注意力再拼接。
- Encoder 双向;Decoder 自注意因果掩码;交叉注意读源端 CCC。
- 残差让深层可训;LN 适配变长与小 batch。
- FFN 做逐 token 非线性扩容,常先升维再降维。
- 训练可并行(Teacher Forcing + Mask);推理自回归串行。
- KV Cache 缓存历史 K/V,加速 decode。
- 子词分词基本消灭 OOV。
- 现代 LLM:Decoder-only + Pre-LN + RoPE + GQA + 更大上下文,是同一骨架上的工程进化
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/DS1367780968/article/details/163134166



