2.1 Transformer
Self-Attention/Multi-Head/Position Encoding:现代AI的基石——从零理解Transformer的每一个细节
为什么Transformer彻底改变了AI?
2017年,Google发表了论文《Attention Is All You Need》(注意力就是你需要的一切)。这篇论文提出了一种全新的神经网络架构——Transformer。在这之前,处理序列数据(如文本、语音、时间序列)主要依赖RNN(循环神经网络)和LSTM。但Transformer的出现一夜之间改变了一切。
为什么Transformer如此重要?想象你正在看一部电影。在重要的情节反转时刻,你需要回忆20分钟前出现的一个细节。RNN看类似场景时,是一帧一帧地按顺序看,看到第20分钟时,它已经"忘记"了第1分钟的细节。就像用勺子挖隧道——艰辛但记忆太短。
Transformer引入了一个革命性的概念:注意力机制(Attention)。不是像RNN那样按顺序逐步处理,Transformer让每个位置都能同时看到所有其他位置。就像你读一篇文章时,如果你能随时看到全文的所有字,理解会深刻得多。
核心创新:用Attention替代了RNN和CNN,实现了全局并行计算。
此后发生了什么?BERT、GPT系列(GPT-1到GPT-4o)、ViT(将Transformer用于图像)、CLIP(图文理解)、ChatGPT、以及几乎所有现代大语言模型——全部建立在Transformer基础上。可以说,Transformer是现代AI的支柱。
让我们用一个类比来理解Transformer的设计思想:
- RNN像一个人按顺序读一本书,每次只能记得上一页的内容
- CNN像一个人只能看到窗口大小的内容,不断滑动窗口
- Transformer像一个人能同时看到整本书的所有页面,但会专注于当前最重要的部分
Self-Attention:自注意力机制详解
<p>Self-Attention(自注意力)是Transformer的核心。它的本质是:一个序列中,每个位置(每个词)都应该关注其他所有位置,并决定哪些位置对理解自己最重要。</p>
<p>可以想象成一个会议室里有10个人在开会。当一个人说话时,他需要知道谁在认真听、谁和他说的事情相关、谁在走神。自注意力就是计算"谁和谁相关"。这个相关程度叫做<b>注意力权重</b>。</p>
<p>Self-Attention分为<b>四个步骤</b>:</p>
<p><b>步骤1:生成QKV三个矩阵</b></p>
<p>对于输入序列中的每个词,我们会生成三个向量:<b>Q(Query,查询)</b>、<b>K(Key,键)</b>、<b>V(Value,值)</b>。这三个向量是通过将输入向量与三个不同的权重矩阵相乘得到的:</p>
<p>每个词的输入向量 x → 乘以矩阵 W^Q → 得到 q(查询向量)</p>
<p>每个词的输入向量 x → 乘以矩阵 W^K → 得到 k(键向量)</p>
<p>每个词的输入向量 x → 乘以矩阵 W^V → 得到 v(值向量)</p>
<p>这些权重矩阵是在训练过程中学到的。</p>
<p><b>步骤2:计算注意力分数(Score)</b></p>
<p>对于每个位置,我们计算它与所有其他位置的"匹配程度":</p>
<p><b>Score(q_i, k_j) = q_i · k_j / √d_k</b></p>
<p>其中 q_i·k_j 是点积(对应元素相乘后求和),d_k 是键向量的维度。</p>
<p>除以 √d_k 是为了防止点积结果太大导致softmax后梯度太小(这叫做<b>缩放点积注意力</b>,scale dot-product attention)。</p>
<p><b>步骤3:应用Softmax归一化</b></p>
<p>把注意力分数转成概率(权重和为1):</p>
<p><b>α_ij = softmax(Score(q_i, k_j)) = e^{Score_ij} / Σ_j e^{Score_ij}</b></p>
<p>这个 α_ij 就是位置i对位置j的注意力权重——决定i要多"关注"j。</p>
<p><b>步骤4:加权求和得到输出</b></p>
<p>每个位置的输出是所有值的加权平均:</p>
<p><b>Output_i = Σ_j α_ij · v_j</b></p>
<p>权重 α_ij 越大,位置j对输出i的贡献越大。</p>
<p><b>完整公式:Attention(Q, K, V) = softmax(Q·K^T/√d_k)·V</b></p>数值示例:Self-Attention一步一步算
假设我们有一个很短的句子"I love AI",只有3个词。每个词用维度d=4的向量表示(现实中维度通常是512或768,但这里用4简化):
I的向量:[1, 0, 1, 0]
love的向量:[0, 2, 0, 2]
AI的向量:[1, 1, 1, 1]
为了简化,我们假设Q、K、V矩阵都是单位矩阵(不改变向量),这样Q=K=V=输入向量。
步骤1:计算Q·K^T
首先计算每对词的点积(对应元素相乘后相加):
I·I = 1×1 + 0×0 + 1×1 + 0×0 = 1+0+1+0 = 2
I·love = 1×0 + 0×2 + 1×0 + 0×2 = 0
I·AI = 1×1 + 0×1 + 1×1 + 0×1 = 2
love·I = 0×1 + 2×0 + 0×1 + 2×0 = 0
love·love = 0×0 + 2×2 + 0×0 + 2×2 = 0+4+0+4 = 8
love·AI = 0×1 + 2×1 + 0×1 + 2×1 = 0+2+0+2 = 4
AI·I = 1×1 + 1×0 + 1×1 + 1×0 = 2
AI·love = 1×0 + 1×2 + 1×0 + 1×2 = 4
AI·AI = 1×1 + 1×1 + 1×1 + 1×1 = 4
结果矩阵(3×3):
[ [2, 0, 2],
[0, 8, 4],
[2, 4, 4] ]
步骤2:除以√d_k
d_k = 4, √d_k = √4 = 2
每个元素除以2:
[ [1, 0, 1],
[0, 4, 2],
[1, 2, 2] ]
步骤3:对每一行做Softmax
第一行 [1, 0, 1]:e¹+e⁰+e¹ = 2.718+1+2.718 = 6.436
→ [2.718/6.436, 1/6.436, 2.718/6.436] = [0.422, 0.156, 0.422]
第二行 [0, 4, 2]:e⁰+e⁴+e² = 1+54.598+7.389 = 62.987
→ [1/62.987, 54.598/62.987, 7.389/62.987] = [0.016, 0.867, 0.117]
第三行 [1, 2, 2]:e¹+e²+e² = 2.718+7.389+7.389 = 17.496
→ [2.718/17.496, 7.389/17.496, 7.389/17.496] = [0.155, 0.422, 0.422]
步骤4:加权求和
输出 = 注意力权重 × V矩阵
第一行(I的输出):0.422 × [1,0,1,0] + 0.156 × [0,2,0,2] + 0.422 × [1,1,1,1]
= [0.422, 0, 0.422, 0] + [0, 0.312, 0, 0.312] + [0.422, 0.422, 0.422, 0.422]
= [0.844, 0.734, 0.844, 0.734]
注意:I的输出不仅包含"I"的信息,还混合了"love"和"AI"的信息——这正是注意力的本质!
多头注意力(Multi-Head Attention)
如果只用一组Q、K、V,模型只能从一个角度衡量词与词的关系。但词的关系是多方面的。比如在"我喜欢吃红苹果"中,"苹果"的词性(名词)、功能(食物)颜色(红色)是从不同角度理解的。
多头注意力用多组平行的Q、K、V(称为"头")。每组关注关系的不同方面。
假设有h个头(GPT-3有96个头):
头1可能关注语法关系(主谓宾)
头2可能关注语义关系(同义词)
头3可能关注位置关系(距离远近)
...等等
每个头的输出拼接在一起,再乘以一个输出权重矩阵,得到最终的多头注意力输出。
实际计算:对于h个头,每个头的维度变为 d_model/h。比如d_model=512, h=8时,每个头处理64维。
计算公式:
MultiHead(Q,K,V) = Concat(head_1, ..., head_h) · W^O
其中 head_i = Attention(Q·W^Q_i, K·W^K_i, V·W^V_i)
位置编码(Position Encoding)
Self-Attention有一个重要问题:它不知道词的顺序!因为Attention计算的是词与词之间的相似度,而"猫追老鼠"和"老鼠追猫"中的词完全一样,Attention会得到相同的结果。
但顺序对语言至关重要。"我打你"和"你打我"完全相反。所以我们需要给模型注入位置信息。
位置编码就是给每个位置添加一个独特的向量,编码它在序列中的位置。有两种主要方式:
1. 正弦/余弦位置编码(原始Transformer)
使用正弦和余弦函数生成位置向量。不同位置的向量不同,而且模型可以学到位置之间的相对关系。
公式:PE(pos, 2i) = sin(pos/10000^{2i/d_model})
PE(pos, 2i+1) = cos(pos/10000^{2i/d_model})
其中pos是位置,i是维度索引。不同维度有不同的频率(波长),就像收音机的不同频道。
2. 可学习位置编码(GPT采用)
把位置编码当作模型参数,在训练中自动学习。更灵活,但无法处理比训练时更长的序列。
3. RoPE(旋转位置编码,LLaMA等)
更先进的方式,通过旋转矩阵实现。LLaMA、Qwen等模型使用。
无论哪种方式,最终都是:输入 = 词嵌入 + 位置编码
Transformer完整架构
Transformer的完整架构包括编码器(Encoder)和解码器(Decoder)两部分。但GPT系列只用解码器(Decoder-only),BERT只用编码器(Encoder-only)。
编码器层:
输入 → 嵌入 + 位置编码 → 多头自注意力 → 残差连接 + 层归一化 → 前馈网络(FFN) → 残差连接 + 层归一化 → 输出
解码器层:比编码器多一个交叉注意力层
输入 → 嵌入 + 位置编码 → 掩码多头自注意力 → 残差+归一化 → 交叉注意力(关注编码器输出) → 残差+归一化 → 前馈网络 → 残差+归一化 → 输出
关键组件详解:
前馈网络(FFN):两层全连接+激活函数。每个位置独立计算。FFN(x) = W₂·ReLU(W₁·x + b₁) + b₂
残差连接(Residual Connection):output = x + Sublayer(x)。帮助梯度直接流过深层网络,防止梯度消失。
层归一化(Layer Normalization):对每个样本单独做标准化。不同于BatchNorm对整批数据做标准化。
掩码自注意力(Masked Self-Attention):解码器中,每个位置只能看到它自己及之前的位置。通过把未来位置的Attention分数设为负无穷(softmax后变为0)实现。这是为了确保模型在生成下一个词时看不到"未来的信息"。
为什么不全都用编码器-解码器?GPT的实验证明,只用解码器(Decoder-only)+因果掩码在大规模语言建模中效果更好。只用编码器(BERT方式)适合理解任务如分类。编码器-解码器适合翻译等序列到序列任务。
练习题
Q1:假设输入有4个词,每个词用维度d=3的向量表示。Q、K、V都是单位矩阵。计算词1和词2的点积:词1=[1,0,1], 词2=[0,2,0]。然后除以√d_k(d_k=3的平方根≈1.73)是多少?
Q2:为什么Self-Attention需要除以√d_k?如果不除以会怎样?
Q3:位置编码解决了什么关键问题?
Q4:多头注意力的"头"是什么意思?为什么需要多个头?
Q5:解码器中的掩码自注意力是什么?为什么需要掩码?
查看答案
A1:点积=1×0+0×2+1×0=0,除以√3≈1.73后=0
A2:如果d_k很大(如512),点积的结果可能非常大(几百甚至上千)。Softmax对大输入会输出极度接近0或1的值,梯度趋近于0(梯度消失问题)。除以√d_k把值拉回到合理范围。
A3:Self-Attention本身无法区分词的顺序——"猫追狗"和"狗追猫"中相同的词会得到相同的注意力分数。位置编码给每个位置独特的信息,让模型知道词在序列中的位置。
A4:一个"头"就是一组Q、K、V。多个头让模型同时从不同角度理解词的关系——比如一个头看语法关系、另一个看语义关系、另一个看位置关系。GPT-3有96个头。
A5:掩码自注意力把未来位置的Attention分数设为负无穷,使得softmax后这些位置的权重变为0。这样模型在生成第t个词时只能看到前面1到t-1个词,而不能偷看后面的词。这是确保生成过程的因果性(causality)。