资讯动态

Transformer 怎么学?七个关键问题彻底搞懂核心架构

发布时间:2026/10/20 5:52:42 来源:尧图企业网站定制
Transformer 怎么学这个问题我几乎每周都要被问一次。很多人不是没看过教程而是看了一堆视频、图解、源码解读结果一提笔还是画不出完整的结构。我自己这些年带过不少新人也踩了不少坑最后得出的结论很一致作为入门者你不需要一次性刷完全部源码也没必要背下整张架构图。你只需要把 7 个关键问题真正弄懂Transformer 的核心脉络就会自动在脑子里串成一条线。这篇就把这 7 个问题从背景、原理到实战拆开揉碎讲清楚适合刚接触深度学习的小白也适合那些看了很多资料却始终没把结构串起来的读者。按顺序读每搞懂一个就往脑子里补一块拼图。1. 第一个问题为什么非要有 Transformer它到底解决了什么1.1 RNN 时代的苦串行、长依赖、训练难在 Transformer 出现之前处理文本、语音这类序列数据的主力是 RNN 和它的变体 LSTM、GRU。那时候做翻译、做文本生成大家的常规操作就是搭一个编码器-解码器框架两端都靠 RNN 一个时刻一个时刻地“读”数据。RNN 的计算逻辑说起来很直观每读一个词就更新一次隐藏状态这个隐藏状态像一个小抄本记录着截止到当前时刻的所有信息摘要然后把这个状态传给下一个时刻继续更新。问题恰恰出在“串行”和“摘要”上。串行意味着第 t 个词必须等前面 t-1 个词全部算完才能处理训练时无法并行效率极低。摘要则意味着早期信息要在每步之后被反复压缩、改写传到足够远的时刻时信息早就衰减得不像样了。这就是所谓的“长距离依赖”问题一个句子前面出现过的“它”可能要到三十个词之后才知道指代的是谁RNN 这条单向传递链几乎承载不住这种跨度。LSTM 用门控机制稍微缓解了梯度消失但本质上仍是“一条道走到黑”的线性信息传递。我在实际项目里试过用 LSTM 做长文本分类超过两百个词以后前面内容的记忆基本淡化效果非常不稳定。这种工程上的无力感是当时从业者的共同体会。1.2 注意力的出现从查词典到直接翻书注意力机制并不是 Transformer 原创的概念。早期它被用在 Seq2Seq 模型里想解决的是“信息瓶颈”问题编码器把整个源句子压成一个固定长度向量解码时信息不够用于是研究者让解码器在生成每个词时回头去源句子的不同位置上“挑选”需要的信息。这个机制效果显著但当时还是作为 RNN 辅助组件存在的。转折点发生在 2017 年。某研究团队发表了一篇题为 Attention is All You Need 的论文提出了一个相当“激进”的方案整个模型里一个 RNN 都不要完全靠注意力机制来建模序列。这个结构就是 Transformer。它带来的收益是决定性的并行计算所有位置的表示可以同时计算训练速度大幅提升。全局依赖任意两个位置之间直接建立关联信息传递路径长度为 1不再随距离增长而衰减。深层堆叠由于没有递归的时序依赖层数可以轻松叠到 12 层、24 层甚至更多容量远大于 RNN。打个生活化的比方RNN 读句子像一个人看纸质书必须逐字逐句往后读而且只允许在脑子里记摘要Transformer 则像打开一个带全文检索的电子文档读任何一个词时都能立刻跳到全文任意位置去查相关信息。差距一目了然。2. 第二个问题自注意力到底怎么算一句话理解的 K、Q、V2.1 K、Q、V 就是检索三件套自注意力是 Transformer 的最小核心单元。它的计算可以借用搜索引擎的逻辑理解。你在搜索框里输入的内容是 Query也就是“查询”系统拿 Query 去和文档库里每个文档的标题 Key 逐一做相似度匹配匹配分数越高的文档在最终展示结果 Value 里占的权重越大。对应到公式就是Attention(Q, K, V) softmax(Q × K^T / sqrt(dk)) × V其中 Q、K、V 三个矩阵并不是天然存在的它们由原始输入向量 X 分别乘上三个不同的权重矩阵 Wq、Wk、Wv 得到。这个“乘矩阵”的操作本质上就是做一次线性变换让模型可以学习从不同角度看待同一个输入。Q 负责扮演“我在找什么”K 负责宣告“我有什么”V 负责提供“真正的内容”。三者在训练中会不断调整各自的映射方式最终学到合理的关系度量。为什么叫“自”注意力因为 Q、K、V 全部来自同一个序列本身。比如输入一句“猫坐在垫子上”每个词都去和其他所有词算匹配度词与词之间两两建立关联这个过程完全不需要外部信息所以叫“自”注意力。如果你把输入里两个词的顺序对调在没有位置信息介入的情况下计算结果是一模一样的——这个特性后面讲位置编码时会再次提到。2.2 缩放和 softmax为什么简单却必须这么设计公式里有个经常被一眼带过的细节为什么要除以根号 dkdk 是每个头的向量维度。两个向量做点积时如果维度很大点积结果的数值范围也会变大。直接把这个大数值送进 softmax会导致概率分布非常尖锐——接近 one-hot 的置信度梯度极其微弱无法有效训练。除以根号 dk 相当于把点积结果拉回到一个方差适中的区间softmax 才能给出“有梯度”的分布。这个缩放是小细节但直接影响训练的稳定性。softmax 在这里做的是两件事一是把所有匹配分数转成“和为 1”的权重二是放大那些高匹配分数、压倒低匹配分数。计算完成后用这些权重对 V 做加权求和就得到当前 token 融合全句信息后的新表示。整个自注意力的计算过程极其简洁却同时实现了“找谁、关系多强、带出什么内容”三件事这也是它能成为新一代架构基石的原因。3. 第三个问题Transformer 怎么知道词的先后顺序位置编码的答案3.1 自注意力天生不认顺序这可太致命了我在用 Transformer 做文本分类的初期犯过一个特别低级的错误直接跳过位置编码跑了一个小模型结果模型表现得像一个“词袋模型”句子“我爱你”和“你爱我”被打成几乎相同的向量。问题根源就在前面提到的自注意力本质上是集合操作它只关心“有哪些词、彼此关系多大”完全不关心它们在句子里的先后位置。而自然语言里词序几乎决定语义没有位置信息模型就是一个不分语序的黑板上面写满单词却不知道谁先谁后。解决思路很直接给每个位置一个专属“身份证号”把这个编号加到对应的词向量上一起送入模型。这个“身份证号”就是位置编码。设计位置编码的难点在于编号必须能表达位置差异又不能破坏词向量已有的语义空间编号需要覆盖任意句子长度甚至超出训练见过的长度最好还能让模型容易学到相对位置关系。3.2 正余弦位置编码既能表达绝对位置也能表达相对位置Transformer 原文使用了一组正余弦函数来生成位置编码对位置 pos 的第 i 个维度奇数维度用 sin(pos / 10000^(2i/dmodel))偶数维度用 cos(pos / 10000^(2i/dmodel))。这套公式第一眼看有点劝退但拆开理解其实很优雅。不同的频率分量组合在一起相当于给每个位置产生了一个独特的“相位指纹”。两个位置的距离越远它们的指纹差异就越大更重要的是通过三角恒等式任意位置的编码可以被某个位置的编码经过线性变换推算出来这意味着模型理论上能学到相对位置关系而不只是记住一串绝对坐标。为什么不直接用可学习的位置向量可学习方案也能用很多预训练模型就是这么干的。但它的缺点是外推性差训练时定义了 512 个位置向量推理时来了一个 600 词的句子500 往后的位置就没有对应向量可用要么截断要么瞎猜。正余弦编码由于是纯函数计算理论上可以生成任意长度的编码为长度外推留出了空间。当然实际外推能力仍然有限后续很多研究在探索更复杂的位置编码方案但理解正余弦编码依然是学 Transformer 的第一块里程碑。4. 第四个问题多头注意力为什么这么强核心优势在哪4.1 单头注意力只能有一个视角多头允许模型同时看多个视角如果自注意力只有一个“头”那么整个模型只能学到一种打分模式。比如它可能擅长根据词面相似度建立关联但对句法关系、指代关系、语义角色这些不同层面的关联就无能为力了。真实语言中的关联是多维的一个词和它的修饰语之间有关系和它指代的远处名词之间有关系和句子里相关动词之间也有关系。单头注意力就像一个只看一种颜色的人信息再丰富也感受不到。多头注意力做的事情就是复制出多个“查询系统”每个头用自己独立的一组 Wq、Wk、Wv在不同的子空间里并行计算注意力。原文用了 8 个头于是模型能同时获得 8 种不同视角的关联结果。实际观察注意力权重时你会发现某个头可能明显偏向关注下一个词另一个头关注形容词和名词的搭配还有一个头在捕捉句子里的代词指代。这些视角最后拼接起来成为后面前馈网络可用的丰富表示。4.2 多头注意力的参数细节与完整流程多头注意力的计算流程并不复杂。假设模型维度 dmodel 为 512头数 h 为 8那么每个头的维度就是 dk dmodel / h 64。流程是把输入 X 分别乘上 Wq、Wk、Wv 得到三个 512 维的向量然后按头数拆成 8 份每份 64 维8 个“头”各自独立完成缩放点积注意力得到 8 个 64 维输出最后把它们拼接回 512 维再乘一个输出投影矩阵 Wo。整个过程的参数量加在一起和单头 512 维注意力基本一致计算量也近似但表达能力却得到大幅提升这也是为什么多头设计能成为标配。这里有一个取舍需要注意头数不是越多越好。头数太多每个头的维度太小表达空间受限头数太少视角单一。base 配置里 512 维配 8 头是经过验证的稳定组合大约相当于每 64 维负责一类视角。我在自己的项目里尝试过 4 头、16 头在小数据集上 16 头很容易过拟合8 头则明显更均衡。所以不要盲目追求多头先按经典配置起步再用实验数据说话。伪代码一段方便对照理解def multi_head_attention(x, Wq, Wk, Wv, Wo, h): # x: (batch, seq_len, d_model) d_model x.shape[-1] dk d_model // h # 1. 线性变换并拆分成 h 个头 Q x Wq # (batch, seq_len, d_model) K x Wk V x Wv Q Q.reshape(batch, seq_len, h, dk).transpose(1, 2) K K.reshape(batch, seq_len, h, dk).transpose(1, 2) V V.reshape(batch, seq_len, h, dk).transpose(1, 2) # 2. 每个头独立做缩放点积注意力 scores Q K.transpose(-1, -2) / math.sqrt(dk) attn_weights softmax(scores, dim-1) head_output attn_weights V # 3. 拼接所有头并做输出投影 concat head_output.transpose(1, 2).reshape(batch, seq_len, d_model) return concat Wo5. 第五个问题残差连接和层归一化Transformer 里为何必不可少5.1 残差连接让几十层网络真能训得动如果把 Transformer 比作一栋楼多头注意力和前馈网络是房间那残差连接就是贯穿每层楼的逃生通道。每个子层的输出都会和它的输入直接相加也就是 output x sublayer(x)。这个看似简单的操作让梯度的传递有了“高速公路”反向传播时梯度可以直接跳过很多层而不必逐层相乘衰减。没有残差连接深层网络会陷入退化问题层数增加反而导致训练误差上升因为梯度在深层传递时不断消失或爆炸。原版 Transformer 是 6 层但现代大规模模型动辄 24 层、32 层甚至更多没有残差连接这些架构根本训不动。从工程视角看残差连接还能让信息在不同层之间平滑流动确保底层的词汇特征和表层的语义特征能够共存。5.2 层归一化NLP 场景下比批归一化更稳定的原因Transformer 里另一个标配是层归一化Layer NormalizationLN。常见的批归一化Batch NormalizationBN在 NLP 里并不受欢迎原因有两个第一BN 依赖 batch 内所有样本的统计量训练和推理时统计量不一致在变长序列场景下padding 会让统计量被大量无意义位置污染第二NLP 任务往往 batch 不大统计量噪声高。LN 的做法是绕过 batch 维度对每个样本的每个时间步上的特征维度做归一化也就是对单条数据的所有维度计算均值和方差。样本之间互不干扰训练和推理行为一致和 Transformer 的并行计算方式天然匹配。细看现代实现LN 的摆放位置还有讲究。原版论文采用 Post-LN即先 Add 残差再 LN后来的实践更常用 Pre-LN即先 LN 再进子层最后再 Add。Post-LN 在深层上容易训练不稳Pre-LN 的训练更平顺很多大规模模型验证了这一点。我自己微调模型时默认用 Pre-LN收敛明显更稳。理解这个差异对读懂各种开源代码很有帮助。6. 第六个问题Encoder 和 Decoder 怎么配合一篇句子如何翻译成另一篇6.1 Encoder读全句、产上下文向量Transformer 的 Encoder 部分是理解所有预训练模型的基础。它由 N 个结构相同的 Block 堆叠而成每个 Block 包含两个子层多头自注意力层和位置前馈网络层每个子层外面都套着残差连接和层归一化。输入句子经过词嵌入加位置编码后依次穿过这些 Block每个 token 的表示会逐层融合越来越多的上下文信息。Encoder 输出的是一组“上下文向量”——每个位置都携带了整个句子的信息。我用一句话解释它的职责深读整句把每个词放到全句语境里重新理解一遍。它没有“逐词生成”概念而是同时处理整个句子这也是它可以并行计算的根本原因。6.2 Decoder掩码自注意力、交叉注意力和自回归生成Decoder 的结构比 Encoder 复杂一点。它除了有像 Encoder 一样的 Block 之外还多了两个关键设计掩码自注意力和交叉注意力。掩码自注意力负责保证“生成当前位置时只能看到当前位置之前的内容”否则解码器会提前偷看到未来的词训练就失去意义。具体实现是给注意力分数矩阵的上三角部分填入一个非常大的负数softmax 之后这些位置的权重趋近于 0。交叉注意力是 Encoder 和 Decoder 之间的桥梁它的 Q 来自 Decoder 自己当前层而 K、V 来自 Encoder 最后一层的输出。意思是Decoder 在生成每个词时都会问一遍“这句话里哪些信息是我现在最需要的”然后从源句子的编码结果里提取对应内容。用翻译句子来串一遍完整流程输入英文句子先经过 Encoder 获得整句表示。Decoder 端开始解码先输入一个起始符经过掩码自注意力、交叉注意力、前馈网络输出第一个目标语言单词的概率分布然后把这个单词拼回输入重复上述步骤逐个生成后面的词直到输出结束符。整个过程是自回归的——前一步的输出是后一步的输入。训练阶段可以用教师强制Teacher Forcing一次并行喂入完整目标句用掩码掩盖未来位置推理阶段则必须逐词生成这也是生成速度慢的原因之一。7. 第七个问题从理论到实操训练 Transformer 最容易翻车的几个细节7.1 参数量估算与显存账单别只盯着模型参数理解 Transformer 结构之后很多人会关心一个问题到底有多少参数以“base 配置”为例6 层、dmodel 512、8 头、前馈层 2048 维不算词表时参数约 6500 万。但如果给这个模型加上一个 3 万词的词表光词嵌入层就有 512 × 30000 1536 万参数占比已经相当可观。更要注意的是模型“参数量”和“训练时显存占用”完全是两码事训练时显存的主要消耗来自中间激活值也就是每层每个 token 的中间向量、注意力分数等这些都要留到反向传播时使用。序列越长、batch 越大激活值占用的显存就指数级增长。我踩过最典型的坑是估算模型显存时只算了参数结果一跑长文本训练直接爆显存。实践里显存充足性判断要看三块参数本身、优化器状态Adam 还要存一阶和二阶动量、中间激活值。后面这块往往比前两块加一起还大。如果你要做实际项目建议先用小 batch 打通流程再逐步加大 batch通过监控显存占用确定上限不要一开始就贪心。7.2 必须重视的四个训练细节预热、优化器、掩码与惩罚项第一学习率预热。Transformer 训练初期很不稳定如果一开始就用较大学习率loss 往往会直接飞掉。标准的做法是先用很短的时间比如 4000 步让学习率线性爬升到一个峰值再按平方根倒数衰减。这个调度策略来自原版论文的 Noam schedule现在仍是很多预训练工作的默认选择。第二优化器参数。Adam 是最常用优化器但它的 beta2 在 Transformer 上建议从默认的 0.999 调到 0.98 左右。这是因为 beta2 太大时梯度平方的滑动平均更新太慢早期会低估梯度方差导致步长偏大、训练震荡。同时建议配合梯度裁剪clip 到 1.0 左右给训练上保险。第三padding 掩码。NLP 任务里句子长度不一短句会被 padding 到最长序列长度。这些 padding 位置没有任何语义信息但如果不加掩码它们会参与 Attention 计算白白拉低其他位置的有效注意力权重。所以标准做法是在 Attention 内部构造一个 padding mask把这些位置的分数设置为负无穷同时输出端也要把它们排除在 loss 计算之外。第四正则化与标签平滑。Transformer 通常用 dropout 0.1 作为基本正则训练分类或生成任务时0.1 的标签平滑可以减少模型过度自信的问题在部分任务上稳定提升泛化效果。但这些超参数都不是越大越好dropout 过大在数据量不足时反而减弱模型容量标签平滑过大则会导致模型学不到足够强的区分信号。我的建议是先按 0.1 起步再在验证集上做小范围调优。推理阶段还有两个常被忽略的参数束搜索beam search的宽度和温度temperature。beam size 增大有助于提升生成质量但会显著拖慢速度温度小于 1 会让概率分布更尖锐、输出更保守大于 1 则更多样但容易跑偏。我在文本生成项目里的默认起点是 beam 4、temperature 0.8再按业务偏好调整。我个人在实际操作中的体会是把这 7 个问题按顺序消化一遍比闷头读十篇零散教程管用得多。前四个问题帮你打牢注意力的底层直觉第五个是工程稳定性的关键第六个让你真正看懂整个架构的数据流第七个则是从“看懂图”走向“能训练”的门槛。如果时间允许看完这篇之后最好自己动手搭一个极简版本几万参数的玩具模型就行拉一条训练曲线、观察几组注意力权重热力图这个亲手验证的过程能把所有概念焊死在记忆里远比再刷一遍文章有用。祝学习顺利咱们模型见真章。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑