行业资讯
Transformer架构深度解析:从自注意力机制到大模型核心原理
1. 项目概述为什么Transformer是划时代的架构如果你在2017年之后接触过自然语言处理、计算机视觉甚至是蛋白质结构预测那么“Transformer”这个词对你来说一定如雷贯耳。它早已不是一个单纯的模型名称而是代表了一种全新的、颠覆性的深度学习架构范式。我最初接触Transformer时还在和RNN、LSTM的梯度消失问题作斗争Transformer的出现就像在密不透风的房间里打开了一扇窗让序列建模的思路豁然开朗。简单来说Transformer彻底摒弃了循环和卷积结构完全依赖一种叫做“自注意力”的机制来建立序列中任意两个元素之间的关联这种设计让它具备了前所未有的并行计算能力和长距离依赖建模能力。这个架构的核心价值在于它解决了传统序列模型如RNN的几个根本性痛点。首先是并行化。RNN必须按时间步顺序计算无法充分利用GPU的并行计算能力训练速度是瓶颈。Transformer则不同序列中所有位置的信息可以同时计算训练效率呈数量级提升。其次是长程依赖。尽管LSTM和GRU通过门控机制缓解了梯度消失但对于非常长的序列比如几百上千个词远距离的信息传递依然会衰减。Transformer的自注意力机制理论上可以一步到位地捕捉序列中任意两个位置的关系无论它们相隔多远。最后是架构的统一与扩展性。Transformer的编码器-解码器结构清晰模块化程度高这使得它能够像乐高积木一样被轻松地缩放如GPT、BERT的千亿参数模型和迁移到不同领域从文本到图像、音频、视频。如今从驱动ChatGPT的GPT系列到谷歌搜索背后的BERT再到图像领域的Vision TransformerTransformer架构已经成为人工智能特别是大语言模型时代的基石。理解Transformer不仅是理解一个模型更是理解当前AI浪潮背后的核心引擎。无论你是刚入门的新手希望搞懂这些热门技术的基本原理还是有一定经验的开发者想深入底层优化模型或进行领域适配透彻掌握Transformer的每一个细节都至关重要。接下来我将带你由浅入深拆解这个伟大架构的每一个齿轮是如何咬合运转的。2. Transformer核心思想与整体架构拆解要理解Transformer不能一上来就扎进公式和代码里必须先把握住它的核心设计哲学。它的设计完全围绕着“注意力”展开尤其是“自注意力”。我们可以用一个读书会的场景来类比传统的RNN就像一个人逐字朗读一篇文章他必须记住前面读过的所有内容来理解当前这个字而Transformer就像是一群专家同时拿到这篇文章每个人负责理解文章中的一个词但他们之间可以瞬间互相交流、提问共同形成对每个词的深刻理解。这种“瞬间交流”的能力就是自注意力。2.1 抛弃循环与卷积完全基于注意力的序列建模在Transformer之前处理序列数据的主流是循环神经网络及其变体LSTM/GRU以及在图像领域主导的卷积神经网络。RNN系列的核心是“循环”即当前时刻的状态依赖于上一时刻的状态和当前输入这导致了顺序计算的固有缺陷。CNN则通过局部感受野和权重共享来提取特征但对于序列中非局部、长距离的依赖关系需要堆叠很多层才能捕捉到效率不高。Transformer论文《Attention Is All You Need》的标题就旗帜鲜明地提出了新路径你只需要注意力机制。这里的“注意力”并非全新概念早在Seq2Seq模型中注意力机制就被用作解码器访问编码器所有隐藏状态的“桥梁”以解决信息瓶颈问题。而Transformer的革命性在于它将这种“桥接式”的注意力升级为构建模型主体的“自注意力”和“交叉注意力”完全摒弃了循环结构。这种设计带来了几个立竿见影的好处极高的并行度序列中所有位置的表示可以同时计算极大加速了训练过程。卓越的长距离依赖建模自注意力机制允许序列中任意两个位置直接交互无论距离多远信息传递只需一步。模型可解释性增强通过可视化注意力权重我们可以直观地看到模型在处理一个词时更“关注”序列中的哪些其他词这为理解模型决策提供了窗口。2.2 编码器-解码器结构全景Transformer整体采用了编码器-解码器的经典框架但其内部组件全部由注意力层和前馈网络层构成。下图是其宏观结构我们可以将其分解为几个核心部分注此处应以文字清晰描述架构图因禁止Mermaid故用结构化描述代替 整个模型像一个精密的翻译工厂。左侧是编码器栈由N个原论文N6完全相同的编码器层堆叠而成。它的任务是“理解”输入序列为每个输入词生成一个富含上下文信息的向量表示。右侧是解码器栈同样由N个相同的解码器层堆叠。它的任务是“生成”输出序列在生成每一个新词时都会参考编码器提供的全部信息以及已经生成的部分输出序列。连接编码器和解码器的关键是编码器-解码器注意力层或称交叉注意力层。此外模型在输入和输出端还各有两个重要的预处理/后处理步骤输入/输出嵌入和位置编码。嵌入层将离散的词符号转换为稠密的向量而位置编码则至关重要因为自注意力机制本身不具备感知词序的能力必须显式地注入位置信息。注意很多初学者会混淆“自注意力”和“编码器-解码器注意力”。自注意力发生在编码器内部和解码器内部是序列自己和自己做注意力。编码器-解码器注意力发生在解码器层中是解码器去“注意”编码器的最终输出这是两种不同的注意力机制。2.3 核心创新点自注意力、多头注意力与位置编码理解了整体框架我们深入到三个最关键的创新组件。1. 缩放点积注意力这是注意力机制的基本计算单元。给定查询、键和值向量其核心思想是计算查询与所有键的相似度点积用相似度作为权重对值进行加权求和从而得到一个聚焦于重要信息的汇总向量。 公式为Attention(Q, K, V) softmax(QK^T / sqrt(d_k)) V这里有一个关键技巧除以sqrt(d_k)。这是因为当向量维度d_k较大时点积的结果可能进入softmax函数的梯度极小区域导致模型难以训练。这个缩放操作确保了梯度的稳定性。2. 多头注意力如果只使用一组注意力模型只能从一个“视角”去理解序列关系。这就像只用一种滤镜看世界。多头注意力机制并行地运行多组例如8组缩放点积注意力每一组都有自己的可学习参数矩阵将输入投影到不同的“表示子空间”。最后将所有头的输出拼接起来再经过一次线性变换。 这样做的好处是让模型能够同时关注来自不同位置的不同类型的依赖关系。例如在“我吃了苹果因为它很甜”这句话中一个头可能关注“它”和“苹果”的指代关系另一个头可能关注“甜”和“苹果”的属性关系。多头机制极大地增强了模型的表示能力。3. 位置编码自注意力是排列不变的打乱输入词的顺序输出序列的集合不变只是顺序变了。这显然不符合语言等序列数据的特性。因此Transformer必须显式地加入位置信息。原论文使用了正弦和余弦函数来生成位置编码PE(pos, 2i) sin(pos / 10000^(2i/d_model))PE(pos, 2i1) cos(pos / 10000^(2i/d_model))其中pos是位置i是维度索引。这种函数式编码的好处是模型可以轻松学习到相对位置关系因为sin(ab)可以表示为sin(a)和cos(a)的函数并且可以处理比训练时更长的序列。当然现在也有很多工作使用可学习的位置嵌入效果也不错但丧失了外推性。3. 编码器层深度解析从输入到上下文表示编码器是Transformer理解输入信息的核心部件。一个编码器层包含两个子层多头自注意力层和前馈神经网络层。每个子层周围都包裹着残差连接和层归一化。这种“子层 - Add Norm”的结构是稳定训练深度网络的关键。3.1 输入嵌入与位置编码的相加在进入编码器栈之前输入序列需要完成向量化。假设我们的输入是“Hello World”经过分词和词表映射后得到两个词ID。首先通过一个可学习的嵌入矩阵将每个词ID转换为一个d_model维的向量原论文中d_model512。这个向量捕获了词的语义信息。紧接着我们需要为这两个向量加上它们对应的位置编码向量。位置编码向量的维度也是d_model。这个相加操作至关重要它确保了“Hello”和“World”这两个向量不仅包含了词本身的语义还包含了“我是第一个词”和“我是第二个词”的位置信息。没有这一步模型就无法区分“猫追老鼠”和“老鼠追猫”。实操心得在实现时通常会预先计算好一个最大序列长度的位置编码矩阵。在训练和推理时根据实际序列长度切片取出对应的部分直接与词嵌入相加。注意位置编码在训练过程中是固定的不参与梯度更新如果使用正弦编码。3.2 多头自注意力子层内部运作现在携带了位置信息的向量序列进入第一个编码器层。首先遇到的是多头自注意力子层。我们以单头为例拆解其计算过程线性投影对于输入序列中的每个向量X我们通过三个不同的可学习权重矩阵W_Q,W_K,W_V分别投影得到查询向量、键向量和值向量Q XW_Q,K XW_K,V XW_V。计算注意力分数对于目标位置i的查询向量q_i我们计算它与序列中所有位置j的键向量k_j的点积score_{ij} q_i · k_j。这个分数反映了位置j的信息对理解位置i的重要性。缩放与归一化将分数除以sqrt(d_k)进行缩放然后通过softmax函数进行归一化得到权重系数alpha_{ij}。Softmax确保所有权重之和为1形成一个概率分布。加权求和用权重系数对各个位置的值向量v_j进行加权求和得到位置i的输出向量output_i sum_{j}(alpha_{ij} * v_j)。在多头设置下这个过程会并行进行h次例如8次每次使用不同的投影矩阵产生h个输出向量。将这些输出向量在特征维度上拼接起来再经过一个最终的线性投影W_O融合各头的信息得到该子层的最终输出。残差连接与层归一化自注意力子层的输出并不会直接传递给下一层。它会先与最初的输入X进行相加残差连接。这个操作有助于缓解深度网络中的梯度消失问题让模型可以学习一个“增量”。相加后的结果再送入层归一化模块。层归一化会对这个相加后向量的所有特征维度进行归一化减去均值除以标准差再缩放和平移使得每一层的输入分布保持稳定加速模型收敛。3.3 前馈神经网络子层与层堆叠经过自注意力和归一化后数据流入第二个子层前馈神经网络。这是一个简单的两层全连接网络中间包含一个ReLU激活函数。在原论文中它的定义是FFN(x) max(0, xW_1 b_1)W_2 b_2。其中第一层将维度从d_model扩展到d_ff通常是4*d_model即2048第二层再投影回d_model。这个子层的作用是为每个位置独立地进行非线性特征变换和增强。注意FFN是逐位置应用的不同位置之间的计算完全独立这进一步增加了模型的并行能力。同样FFN子层的输出也会经过一次“Add Norm”先与FFN子层的输入即上一个LayerNorm的输出做残差连接再进行一次层归一化。至此一个完整的编码器层就处理完毕了。编码器堆叠这样一个编码器层会被重复堆叠N次。每一层都接收前一层的输出作为输入。通过这种深度堆叠模型能够构建出越来越抽象、越来越复杂的序列表示。底层的注意力可能更多关注局部语法关系如主谓一致而高层的注意力可能捕捉更全局的语义和篇章逻辑。4. 解码器层深度解析自回归生成与交叉注意力解码器的任务是基于编码器的信息自回归地生成目标序列。所谓“自回归”就是在生成每一个新词时将之前已经生成的所有词作为输入。因此解码器在设计上比编码器更复杂一些它包含三个子层掩码多头自注意力层、编码器-解码器多头注意力层和前馈神经网络层。4.1 掩码多头自注意力防止信息泄露这是解码器独有的设计也是实现自回归生成的关键。在训练时我们虽然知道完整的目标序列例如翻译好的句子但为了模拟推理时逐个生成的过程我们必须确保在生成位置i的词时模型只能“看到”位置1到i-1的词而不能“偷看”到位置i及之后的词。掩码机制就是用来实现这一点的。具体操作是在计算注意力分数之后、进行softmax之前将一个未来位置的分数设置为一个极大的负数例如-1e9。这样在后续的softmax中这些位置的权重就会趋近于零。这个掩码通常是一个上三角矩阵对角线及以下为0以上为-inf。注意事项掩码的实现细节很重要。在缩放点积注意力中计算QK^T得到一个[seq_len, seq_len]的分数矩阵。我们需要生成一个相同形状的掩码矩阵并将其加到分数矩阵上。在PyTorch等框架中可以使用torch.tril或torch.triu来方便地生成这种掩码。掩码自注意力子层同样遵循“子层 - Add Norm”的结构。它的作用是让解码器在生成当前词时能够整合已生成序列的上下文信息。4.2 编码器-解码器注意力连接源与目标这是连接编码器和解码器的桥梁是真正的“翻译”或“对齐”发生的地方。在这一层中查询来自解码器上一子层掩码自注意力层的输出。它代表了当前解码位置“想知道什么”。键和值来自编码器栈的最终输出。它代表了源序列的全部信息。通过这个注意力机制解码器在生成每一个目标词时都可以有选择地聚焦于源序列中最相关的部分。例如在翻译“I love machine learning”为“我爱机器学习”时生成“爱”这个词时解码器的查询会与编码器输出的“love”对应的键产生很高的注意力分数从而将“love”的信息更多地融入到“爱”的生成中。这个子层的计算流程与普通的缩放点积注意力完全一致只是K和V的来源不同。它同样会经过残差连接和层归一化。4.3 前馈网络与输出层解码器的第三个子层同样是逐位置的前馈神经网络结构与编码器中的完全相同。经过这个子层和最后的“Add Norm”后数据流经所有N个解码器层。解码器栈的最终输出是一个与目标序列等长的向量序列每个向量的维度是d_model。为了将其转换回词汇表上的概率分布我们需要一个线性层和一个softmax层。线性层将d_model维的向量投影到vocab_size维词汇表大小。这个投影矩阵有时会与输入嵌入矩阵共享权重作为一种正则化手段可以减少参数量并可能提升性能。Softmax层将线性层的输出转换为一个概率分布。对于序列的每一个位置我们得到一个vocab_size维的概率向量其中每个值代表了对应词作为下一个词的可能性。在训练时我们使用交叉熵损失函数来最小化模型预测的概率分布与真实目标词one-hot编码之间的差距。在推理时我们使用贪婪搜索、束搜索等策略从这个概率分布中采样出最终的词序列。5. Transformer的关键技术细节与变体理解了标准Transformer的骨架后我们还需要深入其肌肉和神经探讨一些实现中至关重要的细节以及近年来涌现的重要变体。这些内容决定了模型的稳定性、效率和最终性能。5.1 层归一化与残差连接的放置顺序在原论文中残差连接和层归一化的顺序是“子层输出 - 残差相加 - 层归一化”这种模式被称为“后归一化”。后续的研究如BERT、GPT发现将层归一化放在子层输入之前即“层归一化 - 子层计算 - 残差相加”这种“前归一化”或“预归一化”的模式对于训练非常深的Transformer模型如成百上千层更加稳定。为什么在“后归一化”中梯度需要流经整个子层的非线性计算后才被归一化在极深网络中可能导致梯度不稳定。“前归一化”则先将输入归一化使得子层的计算在一个更稳定的分布上进行残差连接主要传递的是恒等映射这大大缓解了梯度问题。现在大多数大型预训练模型都采用了“前归一化”结构。5.2 注意力机制的计算与内存复杂度分析自注意力机制虽然强大但其计算和内存复杂度是序列长度的平方级O(n^2)。具体来说计算QK^T会产生一个[n, n]的矩阵其中n是序列长度。当处理长文档、长视频或高分辨率图像时序列长度可能达到数千甚至数万这个开销变得无法承受。这催生了大量的高效注意力研究旨在近似标准注意力同时降低复杂度。主要方向包括稀疏注意力不让每个位置都关注所有位置而是只关注一个局部窗口或一些预先定义的稀疏模式。如Longformer的滑动窗口注意力、BigBird的随机全局注意力。线性化注意力通过核函数将softmax注意力重写为两个向量先做点积的形式将复杂度降至O(n)。如Performer、Linear Transformer。分块/递归注意力将序列分块处理或在层次结构上计算注意力。如Reformer的局部敏感哈希注意力、Transformer-XL的片段递归机制。实操心得在选择高效注意力变体时需要权衡效率、效果和实现复杂性。对于大多数中等长度序列512或1024标准注意力仍然是首选因为其实现高度优化如FlashAttention。只有当序列长度成为明确瓶颈时才需要考虑这些高效变体。5.3 位置编码的演进从绝对到相对正弦位置编码虽然经典但其绝对位置的性质在模型遇到比训练时更长的序列时性能会下降。因此研究者提出了多种相对位置编码方法。其核心思想是让模型学习的是序列中两个元素之间的相对距离关系而不是它们的绝对位置。例如在注意力分数计算中除了q_i · k_j额外加入一个只与相对距离i-j有关的可学习偏置项。这样无论句子多长只要两个词的相对距离相同它们之间的位置偏置就是一样的模型就具备了更好的长度外推能力。像T5、DeBERTa等模型都采用了不同形式的相对位置编码。5.4 Vision Transformer当Transformer遇见图像Transformer在NLP领域的巨大成功自然引发了将其应用于计算机视觉的尝试。Vision Transformer是这一方向的里程碑。它的核心思想非常直接将一张图像分割成固定大小的图像块将这些图像块线性投影为向量序列然后加上位置编码直接送入标准的Transformer编码器进行处理最后用一个特殊的分类头输出结果。ViT的成功证明了在足够多的数据上预训练后纯注意力模型在图像分类任务上可以超越最先进的卷积网络。图像的局部性locality和二维结构2D structure并非卷积网络的专属先验Transformer可以通过学习得到。为视觉任务设计的位置编码如二维正弦编码、可学习的二维网格编码非常重要。ViT之后涌现了Swin Transformer等更高效的视觉Transformer它们引入了分层设计、滑动窗口注意力等更好地建模了图像的层次化特征和多尺度信息在检测、分割等密集预测任务上也取得了领先性能。6. 从零开始理解Transformer的训练与推理理论再完美最终也要落地到代码和实践中。这部分我们抛开框架从第一性原理出发梳理训练和推理的全流程并分享一些关键的实现技巧和调参经验。6.1 数据准备批处理、填充与掩码在实际训练中我们很少处理单条样本而是以批次为单位。由于序列长度不一我们需要进行填充将批次内所有序列补到同一长度通常是批次中最长序列的长度。填充的位置使用一个特殊的填充符号。填充带来了一个问题在计算注意力时我们不应该让有效位置去“注意”这些填充位置。因此我们需要一个填充掩码。这个掩码通常是一个布尔矩阵有效位置为False填充位置为True。在计算注意力分数时我们将填充位置的分数加上一个极大的负值-inf使其权重为0。在解码器端我们同时需要填充掩码和序列掩码。序列掩码防止信息泄露填充掩码防止模型关注无效的填充位置。在实现时通常会将这两种掩码合并使用。6.2 损失函数标签平滑与学习率预热Transformer的标准损失函数是交叉熵损失。但对于分类任务特别是词汇量很大的语言模型任务直接使用硬标签one-hot可能会导致模型过于自信泛化能力下降。标签平滑是一种有效的正则化技术。它将硬标签的1分出一小部分如0.1均匀地分配给其他所有类别。例如对于正确类别目标概率不再是1而是0.9对于其他类别目标概率不再是0而是0.1/(vocab_size-1)。这可以防止模型在训练集上过度拟合通常能带来小幅但稳定的性能提升。学习率调度对Transformer训练至关重要。最常用的策略是带预热的学习率衰减。在训练初期例如前4000步学习率从一个很小的值线性增加到设定的初始学习率。这有助于模型在初始阶段稳定地探索参数空间。之后学习率按步数的平方根倒数或余弦函数进行衰减。Adam优化器是标配其参数通常设置为beta10.9,beta20.999,epsilon1e-9。6.3 推理策略贪婪搜索、束搜索与采样训练完成后模型需要生成序列这就是推理。最简单的策略是贪婪搜索在每一步都选择概率最高的词作为输出。这种方法效率高但容易陷入局部最优生成单调或重复的文本。束搜索是更常用的方法。它维护一个大小为k的候选序列集合称为束宽。在每一步对当前所有候选序列扩展下一个词的所有可能然后只保留总概率最高的k个新序列。束搜索通过扩大搜索空间通常能生成比贪婪搜索质量更高的序列但计算量更大。对于创意文本生成如写故事、诗歌我们往往不希望总是生成最“安全”的词而是希望有一些随机性。这时可以使用采样策略如随机采样直接从概率分布中随机采样下一个词。Top-k采样只从概率最高的k个词中采样。核采样只从累积概率超过某个阈值如0.9的最小词集合中采样。这些策略通过引入随机性可以生成更多样、更有趣的文本。6.4 模型初始化与梯度裁剪Transformer的深度和注意力机制使得其训练需要格外小心。参数初始化不当很容易导致训练初期梯度爆炸或消失。常见的做法是使用Xavier均匀初始化或Kaiming He初始化。对于注意力机制中的Q,K,V投影矩阵一些工作建议使用更小的初始化方差。梯度裁剪是训练Transformer的另一个标配技巧。在反向传播计算完梯度后将所有参数的梯度向量的L2范数限制在一个最大值如1.0或5.0以内。这可以防止在训练不稳定时梯度变得过大导致参数更新步伐失控破坏模型。7. 常见问题、实战技巧与避坑指南基于我多次复现和调优Transformer模型的经验这里汇总了一些最常见的问题和实战技巧希望能帮你少走弯路。7.1 训练不稳定与不收敛这是新手最常遇到的问题。可能的原因和排查步骤检查学习率和预热学习率过高是首要怀疑对象。尝试将初始学习率降低一个数量级例如从1e-4降到1e-5并确保有足够长的预热步数如总训练步数的1%。检查梯度在训练初期打印或记录梯度的范数。如果梯度范数非常大如100或出现NaN说明有数值不稳定问题。此时必须使用梯度裁剪。检查损失曲线观察损失曲线是否平滑下降。如果损失剧烈震荡通常是学习率过高或批次大小过小。如果损失几乎不变可能是学习率过低、模型初始化不当或优化器问题。简化问题在一个极小的、可以过拟合的数据集上测试你的模型。如果模型连训练集都无法拟合说明代码实现很可能有bug。7.2 模型过拟合与欠拟合过拟合表现训练损失持续下降但验证损失在某个点后开始上升。对策增加Dropout在FFN层和注意力权重后增大权重衰减系数使用更多的数据增强或者直接获取更多训练数据。欠拟合表现训练损失和验证损失都很高且下降缓慢。对策增加模型容量如d_model,d_ff, 层数N减少正则化降低Dropout率、权重衰减延长训练时间或者检查特征工程是否到位。7.3 注意力权重的可视化与调试注意力机制的可解释性是其一大优点。可视化注意力权重是调试和理解模型行为的强大工具。工具可以使用matplotlib或seaborn绘制热力图。横轴是“键”被注意的位置纵轴是“查询”注意发起的位置。看什么对角线关注在自注意力中一个词高度关注自身是正常的但不应是唯一关注的点。语义关联查看模型是否学到了合理的语义或语法关系。例如动词是否关注其主语和宾语指代词是否关注其先行词异常模式如果注意力图呈现非常均匀或非常稀疏的异常模式可能表明模型没有学到有效信息或者存在梯度问题。7.4 内存溢出与计算优化Transformer尤其是大模型是内存和算力消耗大户。混合精度训练使用自动混合精度将大部分计算放在半精度浮点数下进行可以显著减少GPU内存占用并加速训练。这是现代深度学习训练的标配。梯度检查点这是一种用时间换空间的技术。它在前向传播时不保存某些中间激活值而是在反向传播时重新计算它们。这可以大幅降低内存消耗适用于训练层数非常深的模型。激活检查定期检查中间激活值如注意力分数、层归一化后的值是否出现NaN或Inf这有助于及早发现数值不稳定问题。7.5 一个简易Transformer代码框架要点虽然无法贴出完整代码但可以勾勒出关键部分的实现逻辑供你参考import torch import torch.nn as nn import math class MultiHeadAttention(nn.Module): def __init__(self, d_model, num_heads): super().__init__() assert d_model % num_heads 0 self.d_k d_model // num_heads self.num_heads num_heads self.W_q nn.Linear(d_model, d_model) # 实际实现中会拆分成num_heads个 self.W_k nn.Linear(d_model, d_model) self.W_v nn.Linear(d_model, d_model) self.W_o nn.Linear(d_model, d_model) def forward(self, query, key, value, maskNone): # 1. 线性投影并分头 batch_size query.size(0) Q self.W_q(query).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2) K self.W_k(key).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2) V self.W_v(value).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2) # 2. 计算缩放点积注意力 scores torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k) if mask is not None: scores scores.masked_fill(mask 0, -1e9) # 或 -float(inf) attn_weights torch.softmax(scores, dim-1) # 可选在此处加入Dropout context torch.matmul(attn_weights, V) # 3. 合并多头输出 context context.transpose(1, 2).contiguous().view(batch_size, -1, self.num_heads * self.d_k) output self.W_o(context) return output, attn_weights # 返回输出和注意力权重供可视化 class PositionwiseFeedForward(nn.Module): def __init__(self, d_model, d_ff, dropout0.1): super().__init__() self.linear1 nn.Linear(d_model, d_ff) self.dropout nn.Dropout(dropout) self.linear2 nn.Linear(d_ff, d_model) self.activation nn.GELU() # 原论文用ReLU现在GELU更常见 def forward(self, x): return self.linear2(self.dropout(self.activation(self.linear1(x)))) # 编码器层、解码器层、位置编码等组件依此类推构建最后一点体会Transformer是一个极其优雅和强大的框架但它的成功离不开海量数据、大规模算力和精巧的工程实现。理解其原理是第一步更重要的是动手实现一个简化版并在实际任务中调试和应用它。从最初的机器翻译到如今的通用大模型Transformer的潜力远未被穷尽。当你真正弄懂了它的每一个细节你不仅掌握了一个工具更获得了一种理解和构建复杂序列模型的强大思维方式。
郑州网站建设
网页设计
企业官网