ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Transformer架构深度解析:从自注意力到多头机制

Transformer架构深度解析:从自注意力到多头机制 1. 从序列建模的痛点说起1.1 为什么Transformer之前“慢半拍”在Transformer出现之前主流的序列建模工具是RNN、LSTM和GRU尤其是LSTM在机器翻译、语音识别等任务上统治了很多年。它们的核心思路是“按时间步逐个处理”:第t个词进来隐藏状态 h_t 由 h_{t-1} 和当前输入共同计算。这种循环结构天然适合变长序列但副作用也很明显——不能并行。训练的时候必须等前一个时间步算完才能算下一个时间步GPU利用率上不去模型规模一扩大训练成本直线上升。另一个痛点是长距离依赖。虽然LSTM设计了门控机制来缓解梯度消失但序列长度超过几百甚至几千后早期信息很容易被“稀释”。你可以把LSTM想象成一个人逐字转述一句话传到后面除非专门做记忆强化否则开头说了什么早就含糊了。对于需要跨长距离建模的任务比如文档级情感分析、长句翻译这就很吃力。还有一点RNN系的模型在编码双向语义时往往要用两个方向拼接增加复杂度。当时整个社区都在期待一种更直接、更适合并行、能长距离建模的新架构Transformer正是在这种需求下登场的。1.2 Attention机制:从“软对齐”到“全部注意力”Attention思想其实在2014年的Bahdanau Attention中已经出现那时候主要用作RNN的辅助模块解码每一步时通过比对当前隐状态和所有源端隐状态算出一个权重分布告诉模型“该重点关注源句的哪个词”。这种方式本质上是建立一个软对齐让模型不依赖单点信息而是动态加权所有输入。Transformer把这个思想彻底推到底——不走循环直接把“注意力”当主角。论文《Attention Is All You Need》提出一个简洁的编码器-解码器架构编码器是N层自注意力加前馈网络解码器也类似但靠掩码自注意力保证自回归。整个网络不再有循环单元输入的所有词在计算时是同时参与的因此可以大规模并行训练。这不仅是性能提升更是一种建模哲学的转变序列中的每个元素不是按顺序“继承”前文信息而是直接与所有元素交互由模型自己学出该注意谁。这个设计带来的直接好处有两个。第一任意两个位置的交互路径长度为1长距离依赖不再是问题至少在理论上如此第二所有位置共享计算逻辑优化难度大大下降再配合硬件张量并行训练效率成倍提升。后来大家发现这套架构在几乎所有序列任务上都能碾压RNN系模型陆续延伸出BERT、GPT、ViT等一大批模型。读完Transformer再回头看之前的LSTM你会觉得很多繁琐的调参技巧其实都是在和结构缺陷赛跑。2. Transformer整体架构拆解2.1 从宏观到微观:一张架构图背后的逻辑Transformer的标准结构是“编码器-解码器”N6层堆叠每层里有两个核心子层多头自注意力子层和位置逐列前馈网络子层。每个子层外面都套了残差连接和层归一化。解码器比编码器多一个交叉注意力子层同时第一个自注意力子层要加因果掩码。如果只看宏观编码器负责把源序列编码成连续的语义表示解码器在生成目标序列时逐步从这个表示里“取信息”。这个框架并非新概念但内部实现完全变革了。编码器中每一层输出的维度是 d_model论文里取512。你输入一批形状为 [batch_size, seq_len] 的词索引经过embedding层变成 [batch_size, seq_len, d_model]然后加上位置编码送入堆叠的Transformer块输出还是同样形状。解码器的输入是目标序列训练时整体输入生成阶段则是逐步自回归。“层”里面具体做了什么我建议这样记自注意力负责“交换信息”让每个位置的向量融合整个句子中其他位置的信息前馈网络再对每个位置独立做一次非线性变换相当于在全局交互之后做局部加工。残差连接保证深层梯度畅通层归一化让训练更稳定。理解了这条主线Transformer在你眼里就不再是一团黑盒。2.2 自注意力机制:让每个词看到整个句子自注意力是Transformer最重要的组件。输入一个序列每个位置生成三个向量Query、Key、Value。这三个向量是输入向量分别乘上三个可学习矩阵 W_Q、W_K、W_V 得到的。你可以把它们类比成检索系统Query是你想查的内容Key是每个文件的内容标签Value是文件正文。注意力分数就是Query和所有Key做点积越大说明越相关对这些分数做softmax归一化得到权重最后用权重对Value做加权求和就是当前位置在这个句子中的“聚合语义表示”。具体到第 i 个位置的输出公式Attention(Q, K, V) softmax(Q K^T / √d_k) V这里 Q 的第 i 行代表第 i 个词的查询向量K 的第 j 行代表第 j 个词的键向量Q_i 与 K_j 的点积就是词 i 对词 j 的关注程度。除以 √d_k 是为了防止点积过大导致softmax进入饱和区。d_k 是每个头的维度论文取64。如果 d_k 是64√d_k8如果维度是512不缩放的话点积数值容易很大softmax算出来近似one-hot梯度很小学习效率会明显下降。这个缩放不是玄学是一个实打实的让训练稳定的手段。用生活类比再说一次你要在一堆文档里找“如何训练模型”的资料Query是你输入的关键词Key是每篇文章的标题Value是文章内容。你先算关键词与每个标题的匹配度再做归一化最后按匹配度的比例从各篇文章提取信息。自注意力就是做了一堆这样的检索只不过Q、K、V都是从输入自己生成的所以叫“自”注意力。2.3 多头注意力:为什么要切成多个头单一注意力虽然能建模所有位置两两之间的关系但“只抓一种关系”其实不够。比如一句话里动词和主语有语法关系代词和指代对象有语义关系如果只用一组Q/K/V模型很难同时兼顾多种关系模式。多头注意力把 d_model 维向量分成 h 个头论文 h8每个头在 d_k d_model / h 维度上独立做注意力再拼接起来做一次线性变换。每个头相当于让模型从不同子空间里去观察序列关系。有的头可能倾向于捕捉邻近词之间的局部依赖有的头可能学到远距离指代关系。实验里这种分工不一定规范但经验表明多头确实比单头效果更好。从训练角度看多头还相当于给模型提供了多个“特征视图”比单头在相同参数下更丰富。注意这里有个很容易混淆的细节多头注意力输入还是同一个序列每个头有自己的 W_Q、W_K、W_V但它们不增加太多参数量因为每个头的维度是原来的 1/h总维度不变。计算上其实相当于把大矩阵分块并行算前向速度甚至可以更快。我用过单头四层的Transformer做小规模翻译任务效果明显比多头差当时只靠经验确定是注意力表达能力不足。3. 关键公式与实现细节3.1 从公式到张量形状如果你要手写Transformer最先要搞明白的就是形状。假设输入 X 是 [batch_size, seq_len, d_model]其实embedding后是 [batch_size, seq_len] 查表得到把 X 分别乘上 [d_model, d_model] 的 W_Q、W_K、W_V得到 Q、K、V三个矩阵的形状都是 [batch_size, seq_len, d_model]。多头切分时把最后一维切成 h 份变成 [batch_size, seq_len, h, d_k]然后交换维度成 [batch_size, h, seq_len, d_k]。之后对每个头计算注意力分数scores Q K.transpose(-2, -1) # [batch_size, h, seq_len, seq_len]再除以 √d_k做softmax最后 V得到 [batch_size, h, seq_len, d_k]concat到 [batch_size, seq_len, d_model] 再经过输出投影。这段逻辑如果你只在概念上理解听懂了真动手时经常会报维度不匹配。我当时在PyTorch里写这个步骤踩过一个bugtranspose之后没有用contiguous()再view导致内存布局不对结果张量值全乱了。这个坑在写自定义Transformer时会遇到直接用框架自带的 nn.MultiheadAttention 很少暴露但理解这些细节对你调参很有帮助。3.2 位置编码:给Transformer“编号”自注意力机制本身是置换等变的——把输入顺序打乱输出的集合不变只是顺序跟着动。语言是顺序敏感的“我爱你”和“你爱我”完全不是一回事所以必须给每个位置注入顺序信息。Transformer用的是正弦余弦位置编码PE(pos, 2i) sin(pos / 10000^(2i/d_model)) PE(pos, 2i1) cos(pos / 10000^(2i/d_model))这种编码有几个特性。第一不同位置的编码向量是唯一的第二相邻位置编码差异相对平滑第三它能让模型很容易通过线性变换学习到相对位置关系因为对于固定偏移kPE(posk) 可以表示为 PE(pos) 的线性函数。这一设计在当时非常巧妙不用额外参数就能处理变长序列。如果你用BERT类预训练模型会发现它们用的其实是可学习位置嵌入不是正弦编码。两者区别在于正弦编码是固定函数好处是可以外推到训练时没见过的更长序列可学习嵌入在固定max_len内更灵活但超出长度就要另外处理。如今很多模型也采用RoPE旋转位置编码等新式位置编码更利于外推。理论篇里要把基础搞清楚后面理解RoPE、ALiBi这些变体就容易多了。3.3 残差连接、层归一化和前馈网络每个子层输出都要做LayerNorm(x Sublayer(x))这就是Post-LN结构。残差连接让深层梯度直接回传这是深层Transformer能够训练的关键。如果去掉残差18层以上的Transformer大概率会出现训练不稳定甚至发散。层归一化则对不同特征维度做归一化它和BatchNorm不同BN是对每个特征通道跨batch归一化LN是对每个样本内部的特征归一化所以LN不受batch size影响更适配变长序列的NLP任务。前馈网络FFN在每个位置独立运作是一个两层的MLPFFN(x) max(0, x W1 b1) W2 b2中间维度 d_ff 论文取2048是 d_model 的4倍。ReLU激活这里引入了非线性让模型能够学到更复杂的特征交互。从参数数量看FFN占了Transformer总参数的2/3远多于注意力部分。很多“大模型关掉FFN层会怎样”的消融实验发现FFN可以看作隐式存储知识的地方注意力则负责信息路由。理论篇先记住这个分工。3.4 解码器中的掩码细节解码器有两个特殊设计。第一个是因果掩码causal mask也叫自回归掩码。解码器在预测第t个词时不能看到第t个词之后的目标序列否则就是作弊。实现上是在注意力分数矩阵的上三角不包括对角线填充一个很大的负数比如-1e9softmax之后权重就近乎0。自己训练GPT类模型时一定要加这个掩码不然损失会“泄露”。第二个是交叉注意力。解码器第二层子层的Q来自解码器自身K和V来自编码器最后一层输出。这样解码器能动态地从编码好的源信息中提取生成所需内容。比如翻译时生成英文单词时模型会通过交叉注意力去对齐源文本中对应的中文词语。训练时解码器输入是完整的目标序列shifted right预测每个位置的词推理时则用已经生成的词作为输入一步步往下走直到输出结束符。4. 训练机制与核心参数4.1 训练目标:预测下一个词Transformer最典型的使用方式是自回归语言建模给定前t个词预测第t1个词的概率分布。损失函数是交叉熵。对于机器翻译输入是源语言序列输出是目标语言序列模型在训练时看到完整的目标序列计算每个位置的预测误差。推理时用beam search或贪心搜索逐步生成。这里必须理解训练和推理行为的差异。训练时是“teacher forcing”当前预测不依赖自己的输出直接用真实token作为下一步输入效率高、收敛稳。推理时模型自己的预测可能偏离训练分布一个错误会逐步累积这叫exposure bias。这也是很多翻译系统要加“课程学习”或“序列级强化学习”的原因。理论篇你可以先记住这个差异以后跑推理做生成时就会意识到为什么有时候训练loss很低、生成效果却一般。4.2 优化器与学习率:warmup不是玄学Transformer训练有一个非常经典的学习率方案先用warmup把学习率从0线性增加到峰值然后按倒数平方根衰减。论文给的公式是lrate d_model^(-0.5) * min(step^(-0.5), step * warmup_steps^(-1.5))warmup_steps 论文取4000。这样做的意义是训练初期参数还没平稳过大的学习率容易让训练发散先warmup让梯度统计量稳定下来再逐渐降低学习率帮模型收敛到更优的局部极小。很多人不用warmup直接训练大Transformer结果loss震荡严重不一定是因为模型写错了可能只是学习率策略不对。实际训练中Adam优化器的beta2也要注意论文用0.98比常见默认值0.999更小因为beta2太大会让梯度的指数移动平均值对早期梯度变化反应太慢配合warmup更容易不稳。这些数值都是论文里被验证过有效的设定我在复现时直接用这些默认值很少翻车。4.3 正则化手段:Dropout与Label SmoothingTransformer训练还有一个容易忽略的细节——正则化。每个子层输出后、残差相加前要施加Dropout论文rate取0.1。这个位置是关键如果放在残差之后信息流可能会被打断放在残差之前更符合原始实现。embedding和位置编码相加之后也建议做一次Dropout帮助位置信息不被过拟合。标签平滑也是paper里明确提到的训练技巧label_smoothing默认0.1。它会避免模型对训练集过自信降低logits输出极端值对泛化有帮助。我自己用1.2亿参数的GPT类小模型时开了label smoothing后生成句子的多样性有明显改善困惑度也略有下降。但要注意如果后续要做蒸馏或者严格概率输出label smoothing会略微扭曲概率分布需自行取舍。5. 理论进阶与常见误区5.1 复杂度分析:为什么长文本那么“贵”自注意力的时间和空间复杂度是 O(n^2)n是序列长度。每个位置要和其他所有位置算注意力所以n个位置共 n^2 个分数。这对于短序列还好序列拉到4096、8192甚至更长后显存和计算量都急剧上升。这也是为什么后来涌现了Sparse Attention、Longformer、FlashAttention等优化方案。你需要记住Transformer的“强”和“贵”都源于同一件事——全局两两交互。处理长文本时常见的工程优化包括FlashAttention用分块和在线softmax把中间矩阵写回显存的量降下来滑动窗口注意力只在局部做注意力或者是用稀疏模式。理解了原始复杂度你看到这些变体就不会觉得突兀。理论篇只需抓住这个复杂度瓶颈后续学优化方案就有了出发点。5.2 位置编码真的是“绝对”的吗严格说原始的sinusoidal位置编码是绝对位置编码即每个绝对位置有一个对应向量。但由于位置编码与词向量相加后进入注意力层注意力分数的计算里会包含位置之间的点积项模型可以因此学习到相对位置关系。后来的RoPE直接做了相对位置编码效果更好泛化到长文本的能力也更强。不少人以为Transformer只有绝对位置编码其实它是靠绝对位置编码“间接”表达了相对位置。位置编码还有一个容易混淆的点它和embedding到底怎么融合两种常见方式是相加和拼接。原始Transformer是相加因为它们维度可以相同但如果你希望保留更独立的语义可以选择拼接。大多数论文和实战都采用相加这样可以不增加维度计算代价更小。可学习位置嵌入等价于把位置信息也当作可训练参数模型自己会权衡如何组合。5.3 解码器到底比编码器难在哪很多人跑BERT很熟但第一次做自回归生成时会发现明显更难。核心原因就是解码器要自回归预测错误会累积而且训练和推理行为不一致。编码器可以一次看到整个输入做双向attention解码器只能看到左侧上文。因为信息量受限模型很难充分捕获“右侧”的全局信息。这也是为什么做生成任务要专门引入“采样温度”、“top-p”这些解码策略本质上都是和概率分布打交道。从理论角度看解码器内部的交叉注意力在编码-解码模型中天然起到“翻译”或“信息抽取”作用。对于纯自回归GPT类模型没有交叉注意力而是堆叠多层masked self-attention。后者更简单学习难度也更大——所有信息都来自左侧。理解了这种结构差异你看到“prefix LM”、“encoder-decoder”等不同架构时就不会一头雾水了。5.4 常见理论误区速查我整理一下自己在面试学生和看论文时反复见到的几个误区误区一Q、K、V来自不同输入。其实自注意力里Q、K、V都来自同一个输入X只有交叉注意力中K、V来自编码器Q来自解码器。误区二残差连接是x sublayer(x)之后再做LayerNorm。注意力子层和FFN子层各自包裹了残差和LayerNorm顺序是x - attn - dropout - x - LN不是LN在attn之前。误区三位置编码加在每层输入上。实际上是在最底层的embedding后加一次然后后续层共享这个位置信息因为有残差信息会传下去而不是每层都加。误区四多头注意力中每个头独立输出一个d_model向量。其实是每个头输出d_k维拼接后才是d_model总参数量与单头差不多。这些细节在初学者中非常普遍我在复现时也踩过尤其是LayerNorm的位置放错后收敛性能差异明显。6. 为什么Transformer这么能打6.1 并行计算带来的宏观优势Transformer能够统治NLP一个决定性因素是它的训练效率——所有token同时参与计算彻底抛弃了循环迭代。这种“并行化友好”让模型能够充分利用现代GPU的张量核心batch规模可以拉大训练步数可以缩短。对比LSTM同等算力下Transformer可以在更短时间内达到更好效果这是工程上最直接的胜利。并行能力还带来一个连锁反应模型规模可以不停放大。从1亿参数到千亿参数Transformer都能稳定扩展RNN系模型在扩展时很难利用大规模并行算力。可以说没有Transformer的并行特性就不会有大语言模型时代。同时这种架构天然的“通用计算”能力也让它跨领域适应良好——后来ViT把图像切块作为序列也一样取得了效果。6.2 注意力机制的通用性自注意力本质上是对集合元素之间交互的建模。它的假设是“所有位置之间都可能有关系”而不是像CNN那样假设局域性或者RNN那样假设时间延续。这种通用性让Transformer可以处理文本、图像、音频、点云甚至推荐系统里的行为序列。你不需要像CNN那样设计复杂的卷积核或感受野只需要把数据切成token就能套用同一个框架。这种“少人工归纳偏置”的特点是一把双刃剑。好处是很强的表达能力和灵活性模型可以从数据里学出适合自身任务的结构坏处是需要很大数据量来训练因为缺少内建的结构先验。这也是为什么早期Transformer在一些小数据任务上反而打不过CNN。理论篇里要看到Transformer更像是一种通用可编程的“记忆检索机”而先验信息要靠数据和训练策略引导。6.3 从NLP到多模态的扩展Transformer的出现统一了NLP领域繁杂的模型同时向视觉、语音、多模态等领域快速扩展。ViT把图像划分成16x16的patch每个patch拉直成一个token然后跑标准的Transformer编码器效果可以和CNN/SOTA模型持平甚至更好。Whisper里也用Transformer做语音识别。大模型时代CLIP、GPT-4V这类多模态模型也都是在Transformer基础上融合不同模态的token序列。掌握Transformer理论实际上就拿到了理解这些模型的一把“万能钥匙”。理论篇讲的这些概念比如注意力、位置编码、掩码、残差结构会以各种变体出现在所有后续模型中。如果你能熟练推导自注意力的形状变化、说清各子层的作用那么面对新模型时很容易把它的核心组件映射回Transformer的框架里学习曲线会平缓很多。7. 写在最后的一些体会第一次读“Attention Is All You Need”的时候我并没有完全理解为什么要用那么多头、为什么位置编码选正弦函数甚至觉得论文有些地方写得过于简洁。直到自己动手实现并训练了一个小翻译模型才意识到每一步设计背后都有它要解决的问题——缩放点积是防止softmax饱和多头是给模型多个关系子空间残差和LayerNorm则是为深层网络的稳定优化兜底。纸上得来终觉浅对Transformer这种结构极其规整模型而言尤为如此。对新人我有个建议不要只满足于会调用nn.Transformer或transformers库。试着从零实现一个极小的两层Transformer在玩具数据集上训练你会深入掌握每个张量形状的变化和每个子层的意义。之后再看GPT、BERT、ViT的代码会有一种豁然开朗的感觉。一些库的默认实现其实做了很多优化比如FlashAttention它会让你忽略原始的注意力计算但理解原始版本永远是理解优化版本的前置条件。最后再分享一个我在调试中常用的小技巧把注意力分数矩阵可视化看看到底哪些token之间分数高。你会发现很多有趣的现象比如句子的句法结构会隐含在注意力模式里甚至能看到某些头专门负责“捕捉相邻词”。多花点时间观察这些可视化结果比单纯盯着loss曲线能帮你建立更直观的模型直觉。
返回列表