Transformer 关键概念整理:自注意力推导、交叉注意力、位置编码、归纳偏置
1 注意力机制的思想
人类通过全局扫描,对整图中的重点进行注意力权重的分配,重视重点内容,抑制其他内容,以此达到最佳的,综合的阅读浏览效果。注意力模型可以看作一种通用的思想,本身并不依赖于特定框架,这点需要注意。[1]
Transformer 用到的这一个注意力叫做Scaled Dot-Product Attention
注意力机制本质就是使用\(Q\)和 \(K\) 来计算出"注意力权重\(s_{ij}\)",然后利用注意力权重对V的分量进行加权求和\(a_{ij}\times v_{ij}\)。从机制上看,注意力机制聚焦的过程体现在权重系数上,权重越大表示投射更多的注意力在对应的值上,即权重代表了信息的重要性。
2 transformer解决哪些问题?
Transformer 主要解决了 RNN 的序列串行计算难并行,以及 长距离依赖/梯度消失导致的"越长越记不住" 两个问题。
- 传统的RNN难以并行,有时间顺序递归依赖的问题,是串行工作的。
- 在 Transformer 的 encoder/self-attention 里,同一层计算每个位置的输出 \(o_{i}\) 不需要先算出\(o_{i-1}\),而是一次性用整段序列的QKV做矩阵运算得到所有位置的输出 \(O\)。因此可以把所有 token 的计算打包成大矩阵乘法交给 GPU/TPU 并行执行。
- Transformer 的 decoder 在推理生成时仍然要逐 token 生成(因为下一个 token 依赖上一个输出),但训练时用 teacher forcing + masked self-attention 可以把目标序列整体并行送入,从而训练阶段仍能并行得很好。
- RNN会把每一步都会把"到目前为止的所有历史"压进一个固定维度的隐藏状态\(h_{t}\)。当序列很长、信息很多时,新输入不断覆盖/改写这个状态,早期细节很容易被稀释掉——于是看起来像"越往后越记不住前面"。 RNN 训练用 BPTT(沿时间反传),梯度要跨很多步连乘,容易指数级变小(消失)或变大(爆炸)。一旦梯度消失,模型就很难学会"第 1 个词影响第 100 个词"这种长距离关系,于是表现为"学不会长期记忆",而Transformer 为什么能缓解?Transformer 的核心变化是:不再强迫把历史压缩到一个递归状态里,而是让每个位置在每一层都能"直接访问"其它位置的信息(用注意力做加权汇聚)。在自注意力里,第 \(i\) 个位置的输出是
这意味着:如果第 \(i\) 个 \(token\) 需要用到很远的第 \(j\) 个 token,模型可以让 \(a_{ij}\) 变大(但也会造成权重矩阵过大,计算量过大),直接把 \(v_j\) 的信息搬过来,而不必依赖"信息在很多步的递归传递中不丢失"。
- Transformer 不是"彻底解决长序列" 它缓解了"递归传递导致的遗忘",但仍受上下文窗口和计算、内存 \(O(n^2)\) 的限制(序列越长注意力矩阵越大,因为从图论的角度)。因此才会有长上下文、稀疏注意力、线性注意力、缓存等大量后续工作。
3 transformer自注意力机制完整推导
(缩放点积的多头注意力,的其中一个单头自注意力的计算)
为什么想到多头注意力,作者的解释是:CNN虽然要多层卷积才能把两个像素卷到一起提取出长程的特征,但是CNN做了一个很好的事情,就是多通道的输出,可以学到更多的模式,我现在也想学更多的模式,于是考虑了多头注意力,可以模拟卷积神经网络多输出通道的一个效果。
引用:《Attention is all you need》论文解读及Transformer架构详细介绍
本节手算例子 vs 真实维度对照。真实场景的一套配置是:512个token,\(d_{model}=1024\),depth=6,16个头,\(d_{head}=64\),mlp维度=4096。
1024维没法在纸面上摆出具体数字,所以下面的推导仍用 \(d_{model}=4\)、2个头、4个token("我爱水课")手算,每一步旁边标注真实场景对应的数字:
| 手算例子 | 真实场景 |
|---|---|
| \(d_{model}=4\) | \(d_{model}=1024\) |
| 2个头,每头 \(d_{head}=2\) | 16个头,每头 \(d_{head}=64\) |
| 4个token | 512个token |
| \(X\): \(4\times4\) | \(X\): \(512\times1024\) |
3.1 ① 输入 (Input)
输入矩阵 \(X\) (\(4 \times 4\)):
多头注意力机制,算单个头注意力需先线性投影:意思是,不能直接用原始\(X_{4 \times 4}\)输入,要乘上一个\(W_Q,W_K,W_V\),变成\(Q,K,V\),并且顺便把维度降低,比如要用两个头,那么就把输入降到4/2=2维度,一个token从四维向量变成两维向量。
- \(d_{model} = 4\) (总维度),真实场景 \(d_{model}=1024\)
- 映射到 \(d_{head} = 2\),真实场景 \(d_{head}=64\)
- 总维度 \(d_{model}=4\),要用 2 个头;真实场景总维度 \(d_{model}=1024\),用16个头
- 公式:\(d_{head} = d_k = d_v = \frac{d_{model}}{\text{头数}} = \frac{4}{2} = 2\),真实场景:\(\frac{1024}{16} = 64\)
- (即:单头里,每个字的向量只有两个维度;真实场景单头 \(d_{head}=64\) 维)
3.2 ② 选取权重矩阵 (Random Initialization)
我们需要 \(W_Q, W_K, W_V\) (维度均为 \(4 \times 2\),真实场景里是 \(1024\times64\),16个头就是16组这样的权重),权重矩阵来自随机初始化(这里假设为下面三个矩阵):
3.3 ③ 计算 Q, K, V
根据公式:
再计算打分矩阵 \(S\):
3.4 ④ 打分矩阵 S 详解
含义:\(s_{ij} = q_i \cdot k_j\)
解读:
\(i=1\) ("我"),并 \(s_{11}=1\) \(\rightarrow\) 我和我相关。
\(s_{13}=1\) \(\rightarrow\) 我和水相关。
得到的打分矩阵,的每一行,仍然对应了一个token,表示这个字和其他字之间的相关程度。真实场景里这个矩阵是 \(512\times512\)(512个token互相之间的相关度打分)。
3.5 ⑤ Softmax 与 加权求和
由原公式(论文里这个缩放分母写作 \(d_k\),就是本文用的 \(d_{head}\),同一个量):
- \(S = QK^T\) 已得。
- 缩放:除以 \(\sqrt{d_{head}}\) (\(d_{head}\) 是单头特征维度)。
- 归一化:\(A = \text{softmax}(\frac{S}{\sqrt{d_{head}}})\) (注意:对行取 Softmax)。
对\(S\)的每一行,做softmax,softmax的作用包含两步,一步是取e指数,还有一步就是归一化。每一行,取softmax,注意处理的单元是每一行。
假设S打分矩阵的第二行是(0,5,2),这里我不是用的上面\(4 \times 4\)的打分矩阵,而是一个token是三维的词向量,注意不要搞错,这部分是后面补充的解释。
对第2行 \([0,5,2]\) 做 softmax (按行归一化)
\(\exp(0) = 1, \quad \exp(5) = 148.413, \quad \exp(2) = 7.389, \quad \text{总和} = 156.802\)
所以
\(a_{21} = 1/156.802 \approx 0.0064\)
\(a_{22} = 148.413/156.802 \approx 0.9465\)
\(a_{23} = 7.389/156.802 \approx 0.0471\)
为了完整性,也给出一个三行的权重矩阵,最终一个经过\(softmax\)后的\(A\)矩阵是这样的:
- 加权:\(Z = A \cdot V\)。
注意力权重矩阵 A:
- 解读:\(i=1\) ("我"),分配了 0.4 给"我",0.4 给"水"。
求得 Head 1 的输出 Z:
3.6 ⑥ 输出投影 (Output Projection)
现有 Head 1 的输出隐藏状态 \(Z_1\),加上 Head 2 的隐藏状态 \(Z_2\)(本例只手算2个头做演示;真实场景是16个头,每头输出 \(512\times64\),16个头拼接才是 \(512\times1024\))。
拼接 (Concat):
投影 (Projection):
真实场景里 \(Z_{concat}\) 是 \(512\times1024\),\(W_O\) 是 \(1024\times1024\),输出 \(O\) 是 \(512\times1024\)。
最终输出矩阵 O:
(列含义:属性1, 属性2, 属性3, 属性4)
核心理解
- 想看谁和谁关系好 \(\rightarrow\) 去看 A 矩阵 (Attention Weights)。
- 想看现在这个字变成了什么 \(\rightarrow\) 看 O 矩阵 (Output)。
位置对应原则
无论 Transformer 怎么乘,第一行仅属于输入序列的第一个字。
3.7 ⑦ 残差连接 (Residual Connection)
Transformer 能够堆叠几十层的核心原因。我们将注意力层的输出 \(O\) 加回到原始输入 \(X\) 上。
最开始的输入 \(X\):
最终计算:
结论:\(X_{final}\) 就是一个输出,依然是 \(4 \times 4\);真实场景里 \(X_{final}\) 是 \(512\times1024\),形状和输入完全一致,这也是残差连接"形状不变才能相加"的体现。
它可以被传到下一层 (LayerNorm / FFN) 继续使用,真实场景里 FFN 是 \(1024\to4096\to1024\),这套残差+FFN结构重复6层(depth=6)。
4 自注意力机制细节的补充
- Q表示我现在缺什么,K表示我能提供什么,V表示那你需要,那我应该给多少。
- 向量堆叠,一般说是纵向的堆叠\(\begin{pmatrix}x_{1}\\x_{2}\\x_{3}\end{pmatrix}\)但实际上,纵向横向拼接都可以,关键是只要统一就可以。在NLP领域,序列长度\(n\)一般就放在第一个维度,第\(i\)个token就是第\(i\)行
- 权重是由参数矩阵,\(Q\)和\(K\)作用产生的
- 可以通过把矩阵画成灰度图,可以看到在哪些维度上更强了
- 自注意力:\(QKV\)都来自自己同一个\(X\)
- 自回归:在生成/预测序列时,当前位置的输出以之前已经生成(或观测到)的序列值作为条件,一步步往后推。
- 点积注意力(multiplicative / dot-product)和加性注意力(additive / Bahdanau)最早都常用在 seq2seq/NMT 里;今天主流大模型/Transformer 基本用的是(缩放)点积注意力,因为更高效。
- 残差相加(Add)时,两个张量的形状必须一致;在 Transformer 里通常就是要求最后一维是同一个 dmodeldmodel,并且序列长度/批大小也一致,才能逐元素相加。
- 文章训练,用的是adam优化器,对于学习率是几乎没有调整的,adam对于学习率本身也并不敏感,所以也没什么好调的,比较省力。学习率的选择,向量越长,越宽,那么学习率应当越低。
- \(QK^T\):矩阵乘法算的是"每两个位置的相似度"
5 并行指的是什么?
前面提到,"传统的RNN难以并行,有时间顺序递归依赖的问题,是串行工作的" ,然后提到transformer并行。那这块并行到底说的是什么呢?
传统RNN的结构:
无论是训练还是推理,都是躲不开把前面的维度信息塞进一个定长向量的。
而transformer因为QKV矩阵乘法那一块,是可以并行计算的,在训练阶段实现了并行。
但transformer的Decoder 推理阶段:生成第 N 个词,要用到前面已经生成的第 1 到 N-1 个词作为输入(自回归),所以翻译任务在推理时只能一个词一个词往外吐,没法一次把整句话都生成出来。训练阶段因为答案已知(teacher forcing),可以把整个目标序列一次性喂进去并行训练,推理阶段绕不开这个"一步依赖上一步"的限制。所以起码在这个输出层面上,Transformer还是串行的。
另外,并行也不是指的多头注意力那可以把一个1024维向量拆成16个多头并行计算,多头的目的主要是为了学到更丰富的特征。
6 为什么选择自注意机制作为transformer的核心?
自注意力,不是transformer首创,在先前模型中,自注意力的效果并不好,但是transformer敢大量使用,甚至是做成一个完全基于自注意力的原因有以下三点:
- 自注意力机制,每层的总计算复杂度比RNN有优势
- 可并行化的程度高,最小的串行操作数
- 长程依赖的路径短,RNN的最大路径是\(n\),而自注意力机制能在一层内任意链接两位置,卷积要堆叠多层,所以自注意力机制的依赖路径短,路径短,对中间要经过的步骤越短,学习长程依赖更容易。
7 为什么transformer要做三套投影,而不是一套?
把输入\(X\),嵌入之后,不直接使用,还需要进行一个线性变换,即经过\(QKV\)的三个权重矩阵 \(W_{Q},W_{K},W_{V}\)。目的是为了让模型分别学习到怎么计算相关性(由QK决定),以及要聚合传递什么内容(由V决定)。
而映射到更多子空间和学到更丰富的模式,是由多头注意力决定的。使用多头,能学到更丰富的模式。学习到丰富的模式,不是三套投影干的活。
如果不线性变换,直接使用embeding的输入:
- "如果只靠输入 embedding(查表得到的向量)再配一个点积相似度,注意力的可学习参数会很少",提取和表示的能力也十分有限。
- 如果直接对原始的embedding做自注意力操作,则计算的相似度结果是个对称矩阵,对角向上的值一定是最大的。因为每个字/词必定最关心自己,这样背离了自注意力操作的初衷。
8 交叉注意力机制与自注意力的区别
- 自注意力机制是在同一序列内部做注意力机制,\(QKV\)全来自同一个序列,而交叉注意力机制,则多是跨序列跨模块检索(decoder->encoder)
- 自注意力更侧重于序列内部信息的融合,交叉注意力则是对外部记忆查询,去搜索别人的序列,找到原文,然后生成自己。
- 自注意力机制有时(论文里是 decoder 的 self-attention 为了保持自回归,必须 mask 掉未来位置)需要mask防止看到未来,而交叉注意力则不需要
- 交叉注意力常用与翻译,摘要和对话,类似ChatGPT,等模型常常用到(多模态图文),自注意力机制适合同一序列的上下文建模(分类,抽取,编码理解,视觉transformer)。
9 self-attention为什么要除根号\(d_{k}\)
主要原因是:缩小点积范围,能够确保softmax梯度不至于过小,保持一定的稳定性。
数学解释:假设 \(q_i\) 和 \(k_i\) 相互独立,且服从标准正态分布(均值为 0,方差为 1)。
9.1 ①方差的累积
点积公式为:
第一步,先算单个分量 \(q_ik_i\) 的方差。 方差的定义式:
把 \(X = q_ik_i\) 代进去,需要 \(E[q_ik_i]\) 和 \(E[(q_ik_i)^2]\) 两项。因为 \(q_i\) 和 \(k_i\) 相互独立,独立变量乘积的期望等于期望的乘积,两项都能拆开算:
其中 \(E[q_i^2]\) 由定义式反解得到,\(E[q_i^2] = \text{Var}(q_i) + (E[q_i])^2 = 1 + 0 = 1\),\(k_i\) 同理。所以:
所以每个分量方差 \(=1\)。
第二步,把 \(d_k\) 个分量累加起来。 依据是相互独立的随机变量相加,方差等于各自方差之和:
\(S\) 的方差 \(= d_k\)。\(d_k\) 越长,方差越大。
9.2 ②数值范围示例
本文对照的真实配置里单头 \(d_k = 64\),则标准差(Standard Deviation):
那么 \(q \cdot k\) 的打分大概率落在 \([-3\sigma, +3\sigma]\) 中:
这已经远远超出 Softmax 的梯度敏感区(\([-3,+3]\))。
9.3 ③对 Softmax 梯度的影响
Softmax 公式及其导数:
如果输入 \(x_i\) 过大(如 24),那么 \(S(x_i) \to 1\),导致导数 \(\approx 0\)。
反之,输入过小,那么 \(S(x_i) \to 0\),导致导数 \(\approx 0\)。
9.4 ④结论
\(\therefore\) 必须要缩放。
不然,大量数值落入softmax的饱和区,对于训练就失效了(梯度消失)。
缩放(除以 \(\sqrt{d_k}\)),可以把方差范围拉回 1,让它仍在 Softmax 的梯度敏感区(通常是\([-3,+3]\)或严格点\([-2,+2]\))。
由于方差\(\text{Var}(cX) = c^2\text{Var}(X)\),这里 \(c = \frac{1}{\sqrt{d_k}}\):
代回 \(d_k=64\) 的例子,缩放后标准差从 8 变回 1,打分范围从 \([-24,+24]\) 收到 \([-3,+3]\)。

10 transformer是没有位置信息
原文:由于我们的模型不包含循环和卷积,为了让模型能够利用序列顺序,我们必须注入一些关于序列中词元的相对或绝对位置信息。
transformer使用正弦和余弦两个函数交替编码(偶数维用sin,奇数维用cos),编码后的位置信息是一个和输入向量一样维度的向量,两个向量在各维度上直接一一相加。
拼接,实际上是另一种的相加,实际上从线性运算层面,除了权重是否共享,相加与拼接最后算出来的数学形式是一样的,所以拼接也可,直接各自维度对应相加也没问题。我们担心语义会改变,是因为我们习惯了低维的空间,高维空间512维的情况是完全不同的。而位置编码(Positional Encoding)是人为设计的(正弦余弦函数),它的数值分布规律非常特别。在极高维的空间中,位置向量所在的"方向"和语义向量所在的"方向",大概率是垂直(正交)的。想象一下,人声和背景声,如果给人声加上背景声,会导致音乐听不清吗?大概率还是能分离出来的。
11 什么是归纳偏置(Inductive Bias)?
李沐说transformer使用了一种比较弱的归纳偏置,什么是归纳偏置?
简单来说,就是一种假设,比如CNN假设信息是局部的。而transformer的假设太弱了,所以需要更多的数据去训练。机器学习的本质上就是利用数据进行归纳总结,如果你能给一个先验知识,那么需要模型训练到的就简单的多。偏置,指的是,在没看数据前,这个模型就已经倾向于认为,规律应该长这样
假设越强,需要搜索的越小,但不代表假设强,性能就越好,假设越强,和性能并没有直接相关,transformer就是一个反例。
12 加性注意力和点积注意力
一种叫做加性的注意力机制,它可以处理你的 Query 和你的 Key 不等长的情况。另外一个叫做点积的注意力机制。点积的注意力呢跟transformer的其实是一样的,除了我这里除了一了一个 \(\sqrt{d_k}\) 之外。所以你可以看到他的名字,他叫做 Scaled Dot-Product Attention,就是这个除那个东西。
论文他说这两种注意力机制,其实都差不多,但是他选用的是点乘,这是因为这个实现起来比较简单,而且会比较高效,因为这就是两次矩阵乘法就能算好。
13 对于Trasnformer的评价
李沐对于transformer的评价是,就像CNN一样,提供了一个端到端框架,你不需要知道怎么处理图片和文本,不需要学习特征提取或者抽sfit以及其他的预处理技巧,transformer可以调节的参数就那么几个(没什么可调的反而更加的简便),后续bert就直接把transformer拿过来,改了改多头的\(h_{head}\)或者改改几个transformer块\(N\),同一个模型在所有方面都能通用。
RNN和Transformer的关注点,都是怎么有效的提取出序列的信息。transformer的作用就是信息的汇聚。
transformer在信息的糅合性上,更好。但是由于假设太弱,需要更多的数据。虽然论文也提到了受限的自注意力,但现在用的更多的还是原始的这个版本的注意力。
transfomer和RNN其实在每层复杂度上,注意力是\(n^2 \times d\),而RNN是\(n\times d^2\),实际就是看\(n\)和\(d\)谁更大了,其实复杂度上并没有太大优势,关键是transformer在并行上有优势。
14 对语义向量C的理解
这个C 本身不是操作,而是把x送入F这个变换后,得到的中间的向量结果。
在没有 attention(注意是attention,不是transformer或者什么self-attention) 的最经典 RNN Seq2Seq 里,C可以理解成"整句 Source 的压缩摘要",它通常就取 Encoder 读完整个句子后的最后隐藏状态(final hidden state)
在没有attention之前,encoder是一个个读入x的,每次读取一个x,把前一时刻的隐藏状态 \(h_{t-1}\) 作为当前步计算 \(h_t\) 的输入之一;而模型的"输出"(比如用于预测的 logits/词分布)通常是由隐藏状态再接一层得到的,不一定等同于 \(h_t\),当取到最后一个输入的时候,\(h_m\)这个最终状态就是C,最朴素的做法就是直接取这个\(h_m\),也就是用一个压缩的方式,去存入一个固定长度的向量。
额外阅读claude的关于transformer的理解
额外阅读张俊林关于transformer的理解
The Annotated Transformer(哈佛NLP,逐行代码+公式对照原论文)
[1] https://zhuanlan.zhihu.com/p/37601161

浙公网安备 33010602011771号