Attention Is All You Need(Vaswani et al., 2017)是现代大语言模型的架构起点:论文本身处理的是机器翻译这个 seq2seq 问题,提出完全舍弃循环(RNN)与卷积、只用 attention 机制堆叠出来的 Transformer。今天绝大多数大语言模型都是这个架构的变体,但多数是只保留 decoder、且在细节上做了不少调整的版本,跟原始论文本身并不完全相同。这篇文章先把原始论文的架构拆到最细,再说明现代 decoder-only LLM 具体改了哪些地方。

1. 为什么要用 Attention 取代 RNN

RNN 处理序列时,每个时间步的计算都依赖前一步的隐藏状态,本质上是串行的:处理第 tt 个词元之前,必须先算完第 t1t-1 个。这代表 RNN 没办法在序列长度这个维度上并行,训练长序列时 GPU 的并行运算能力用不上,也让长距离依赖必须靠隐藏状态一路传递,容易随距离增加而衰减。

Transformer 的核心想法是:让序列中任意两个位置之间的信息传递,都只需要一步 attention 计算,不需要沿着中间的位置逐步传递。训练阶段(有完整的目标序列可用时)所有位置的 attention 可以完全并行计算;序列变长也不会增加位置之间传递信息所需的步数。代价是自注意力的计算量随序列长度呈平方增长,这也是后续大量长序列优化工作要解决的问题,但不是这篇论文本身要处理的范围。

2. Scaled Dot-Product Attention

2.1 Query、Key、Value 与张量形状

Attention 的输入是三组向量:Query(QQ)、Key(KK)、Value(VV)。单一个 attention head 里,若序列长度为 nnQRn×dkQ\in\mathbb R^{n\times d_k}K,VRm×dkK,V\in\mathbb R^{m\times d_k}Rm×dv\mathbb R^{m\times d_v}mm 是被查询的序列长度;self-attention 里 m=nm=n,encoder-decoder cross-attention 里 mm 是 encoder 输出的序列长度)。计算公式是:

Attention(Q,K,V)=softmax(QKdk)V\operatorname{Attention}(Q,K,V)=\operatorname{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V

QKQK^\top 算出每个 query 对每个 key 的相似度分数,形状是 n×mn\times m;对每一行做 softmax,得到每个 query 位置对所有 key 位置的注意力权重;权重乘上 VV,等于用这组权重对 value 向量加权平均,输出形状是 n×dvn\times d_v

2.2 为什么要除以 dk\sqrt{d_k}

Q,KQ,K 的每个分量都近似独立、均值 0、方差 1,QKQK^\top 里每个内积是 dkd_k 项乘积的和,方差会随 dkd_k 线性增长。dkd_k 较大时,内积数值本身会变得很大,把 softmax 推进梯度极小的饱和区间,训练会变得困难。除以 dk\sqrt{d_k} 把内积的方差重新拉回常数量级,这是论文里明确指出、也是”scaled”这个字的来源。

3. Multi-Head Attention

单一个 attention head 只能学到一种相似度度量方式。Multi-head attentionQ,K,VQ,K,V 各自用 hh 组不同的线性投影 WiQ,WiK,WiVW_i^Q,W_i^K,W_i^V 投影到较低维度,并行算出 hh 个 head 的 attention 输出,再把 hh 个输出串接起来,用另一个线性层 WOW^O 投影回原本的维度:

MultiHead(Q,K,V)=Concat(head1,,headh)WO,headi=Attention(QWiQ,KWiK,VWiV)\operatorname{MultiHead}(Q,K,V)=\operatorname{Concat}(\text{head}_1,\dots,\text{head}_h)W^O,\quad \text{head}_i=\operatorname{Attention}(QW_i^Q,KW_i^K,VW_i^V)

原始论文的 base model 用 dmodel=512d_{\text{model}}=512h=8h=8,每个 head 的 dk=dv=dmodel/h=64d_k=d_v=d_{\text{model}}/h=64。切成多个较窄的 head 而不是用一个宽 head,让模型可以同时关注不同子空间里不同种类的关系(例如某些 head 倾向捕捉语法结构、某些倾向捕捉指代关系),而总计算量跟单一个全宽度 head 大致相同。

4. Encoder 与 Decoder 的区块结构

4.1 Encoder block:两个子层

每个 encoder block 由两个子层组成:self-attention,接着是 position-wise feed-forward network(见第 5 节)。每个子层外面都包一层残差连接加上 Layer Normalization。原始论文用的是 post-LN:先计算子层本身,加回残差,再做归一化,也就是 LayerNorm(x+Sublayer(x))\operatorname{LayerNorm}(x+\operatorname{Sublayer}(x)),顺序是”子层 → 残差相加 → LayerNorm”,归一化在残差相加之后。base model 叠 6 层这样的 encoder block。

4.2 Decoder block:三个子层,含 cross-attention

Decoder block 比 encoder 多一个子层,总共三个:第一个是加了 causal mask 的 self-attention(见 4.3),第二个是 encoder-decoder cross-attention——这个子层的 QQ 来自 decoder 上一个子层的输出,KKVV 来自 encoder 最后一层的输出,让 decoder 每个位置都能查询整个输入序列——第三个是跟 encoder 相同的 FFN。三个子层同样各自包一层残差加 post-LN。

4.3 Causal mask:确保训练和推理一致的因果性

Decoder 在生成第 tt 个词元时,只能依赖第 11t1t-1 个已生成的词元,不能看到未来的词元。训练时用 teacher forcing,一次把整个目标序列输入,若不做任何处理,self-attention 会让每个位置都看得到后面的词元,等于作弊。解法是在 QKQK^\top 算完、做 softmax 之前,把上三角(对应”查询位置早于被看的位置”)的分数设成 -\infty,softmax 之后这些位置的权重会变成 0,等于强制每个位置只能关注自己与更早的位置。这个机制让训练时的并行计算跟推理时”一次只能看到已生成部分”的限制维持一致。

5. Position-wise Feed-Forward Network

FFN 对序列中每个位置独立且相同地套用同一组参数的两层全连接:

FFN(x)=max(0,xW1+b1)W2+b2\operatorname{FFN}(x)=\max(0,xW_1+b_1)W_2+b_2

输入输出维度都是 dmodeld_{\text{model}},中间隐藏层维度 dffd_{ff} 通常远大于 dmodeld_{\text{model}}(base model 用 dff=2048d_{ff}=2048,是 dmodel=512d_{\text{model}}=512 的 4 倍),激活函数是 ReLU。FFN 不是把 attention 的 Q,K,VQ,K,V 转换成输出维度——attention 子层自己的输出投影 WOW^O 才是做这件事——FFN 的角色是在每个位置上,对 attention 子层算出的结果做一次非线性的逐位置转换,让模型在混合了跨位置信息之后,还有能力对单一位置的表示做更复杂的非线性处理。现代模型常把 ReLU 换成 GELU、SwiGLU 等变体,但”逐位置的两层非线性转换”这个角色没有变。

6. Embedding 与位置编码

6.1 输入嵌入、权重缩放与输出层共享权重

输入词元先查表转成 dmodeld_{\text{model}} 维的嵌入向量,论文里这个嵌入会再乘上 dmodel\sqrt{d_{\text{model}}},让嵌入的数值量级跟后面加上去的位置编码相当。输出端把 decoder 最后的表示投影回词表大小、再做 softmax 预测下一个词元,这个投影矩阵跟输入嵌入表共享同一组权重(weight tying),减少参数量,也让输入输出的向量空间维持一致的几何关系。

6.2 原始论文的正弦位置编码

Self-attention 本身对输入顺序不敏感——打乱输入位置,attention 算出来的加权平均结果不会变——所以需要额外注入位置信息。原始论文用固定(不学习)的正弦函数:

PE(pos,2i)=sin ⁣(pos100002i/dmodel),PE(pos,2i+1)=cos ⁣(pos100002i/dmodel)PE_{(pos,2i)}=\sin\!\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right),\quad PE_{(pos,2i+1)}=\cos\!\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)

pospos 是序列中的位置,ii 是维度索引。这组编码直接加到输入嵌入上。选择正弦函数的原因之一是它让任意固定偏移量 kk 的位置编码 PEpos+kPE_{pos+k},可以写成 PEposPE_{pos} 的线性函数,理论上让模型更容易学会依赖相对位置的模式,也让模型有机会处理比训练时看过的序列更长的输入。

现代 decoder-only LLM 多半不用这种绝对位置编码,改用 RoPE(旋转位置编码)ALiBi 之类的相对位置方法,直接把位置信息算进 attention 分数本身,而不是加在输入嵌入上;这类方法对长序列外推的效果通常优于原始论文的正弦编码,是原始论文之后才发展出来的做法,并不是原始 Transformer 架构的一部分。

7. 训练与推理的差异

原始论文的训练用 teacher forcing(每一步都输入真实的前文词元,而不是模型自己上一步的预测)、Adam 优化器搭配一套先线性升温再依步数衰减的学习率排程、对每个子层输出和嵌入加总后做 dropout,以及 label smoothing(ϵls=0.1\epsilon_{ls}=0.1,让目标分布不是硬性的 one-hot,避免模型对预测过度自信)。评估翻译质量时使用 beam search。

推理阶段没有真实的目标序列可以参考,decoder 必须自回归:每次只生成一个词元,把它接到已生成的序列后面,再喂回模型生成下一个。原始论文的解码方式只有贪心搜索和 beam search;top-k、top-p(nucleus sampling)、temperature 这类采样策略,是后来的研究(大致在 2018 年之后,随开放式文本生成的需求发展出来)针对开放式生成任务提出的方法,不属于 2017 年原始论文的内容,但是现代 decoder-only LLM 推理时的标准做法:temperature 缩放 logits 分布的尖锐程度,top-k 只在概率最高的 kk 个词元里采样,top-p 则取概率总和达到 pp 的最小候选集合再采样,贪心解码(等于每次都选概率最高的词元)可以看成 top-k=1 或 temperature 0\to0 的特例。

8. 从原始论文到现代 decoder-only LLM

现代大语言模型(GPT 系列及其后继者)多半只保留 decoder,拿掉 encoder 和 cross-attention,靠 causal self-attention 同时处理输入与生成,这也是”decoder-only”名称的由来。除了架构的取舍,常见的具体差异还包括:把 post-LN 换成 pre-LN(LayerNorm 放在子层之前,残差路径上不经过归一化,x+Sublayer(LayerNorm(x))x+\operatorname{Sublayer}(\operatorname{LayerNorm}(x))),这让深层模型的训练稳定性明显提升,是超大模型能叠到几十甚至上百层的重要原因之一;位置编码换成 RoPE 或 ALiBi;FFN 的激活函数换成 GELU 或 SwiGLU。这些改动大多是 2017 年之后、为了训练更深更大的模型而陆续提出的工程改良,理解原始论文的每个组件在做什么,是判断这些改良各自解决了什么问题的前提。