摄影 网站 源码软件开发 系统开发 网站开发服务

张小明 2025/12/30 8:27:45
摄影 网站 源码,软件开发 系统开发 网站开发服务,营销网站建设公司有哪些,杭州建站模板系统#x1f449;学会后的收获#xff1a;#x1f448; • 基于大模型全栈工程实现#xff08;前端、后端、产品经理、设计、数据分析等#xff09;#xff0c;通过这门课可获得不同能力#xff1b; • 能够利用大模型解决相关实际项目需求#xff1a; 大数据时代#x…学会后的收获• 基于大模型全栈工程实现前端、后端、产品经理、设计、数据分析等通过这门课可获得不同能力• 能够利用大模型解决相关实际项目需求大数据时代越来越多的企业和机构需要处理海量数据利用大模型技术可以更好地处理这些数据提高数据分析和决策的准确性。因此掌握大模型应用开发技能可以让程序员更好地应对实际项目需求• 基于大模型和企业数据AI应用开发实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能学会Fine-tuning垂直训练大模型数据准备、数据蒸馏、大模型部署一站式掌握• 能够完成时下热门大模型垂直领域模型训练能力提高程序员的编码能力大模型应用开发需要掌握机器学习算法、深度学习框架等技术这些技术的掌握可以提高程序员的编码能力和分析能力让程序员更加熟练地编写高质量的代码。获取方式有需要的小伙伴可以点击文章最下方的微信名片添加免费领取【保证100%免费】transformer背景在自然语言处理领域序列建模的核心挑战是捕捉长距离依赖关系。传统循环神经网络RNN及其变体LSTM、GRU通过隐藏状态传递信息但存在严重的梯度消失问题。一文详解RNN及股票预测实战(Python)卷积神经网络CNN虽可并行计算但需多层堆叠才能捕捉长距离依赖且对序列顺序的建模能力较弱。一文弄懂CNN及图像识别(Python)Transformer摒弃循环与卷积结构基于注意力机制实现全局依赖建模同时支持全并行计算彻底解决了传统方法的核心痛点。它的出现是自然语言处理领域的突破并为大模型架构BERT、GPT等奠定了模型基础。1.2 Transformer的核心创新注意力机制经历了从加性注意力Bahdanau, 2015到乘法注意力Luong, 2015的演进最终在2017年Google团队《Attention is All You Need》中提出Transformer架构。其核心创新包括自注意力机制直接计算序列内所有位置的依赖关系高效捕捉长距离关联多头注意力多子空间并行注意力计算丰富特征表示维度位置编码通过正弦余弦函数注入序列顺序信息弥补注意力机制的顺序无关性编码器-解码器架构模块化堆叠设计兼顾特征编码与生成能力这些创新使Transformer在WMT 2014英德翻译任务中达到28.4 BLEU分数较此前最优结果提升2 BLEU且训练效率提升数倍。1.3. Transformer整体结构Transformer是一种基于注意力机制的序列建模架构核心目标是高效捕捉序列数据的长距离依赖关系同时支持全并行计算。其整体结构分为**编码器Encoder和解码器Decoder**两大模块输入经词嵌入和位置编码预处理后由编码器完成特征编码解码器基于编码特征实现目标序列的自回归生成。Transformer的核心组成可总结为“3大核心模块2个预处理步骤2个核心设计”具体如下预处理步骤词嵌入将文本token转换为固定维度的向量、位置编码注入序列顺序信息解决注意力机制“顺序无关”问题编码器模块由N个相同的编码器层堆叠而成每层含“多头自注意力层”和“前馈神经网络层”核心作用是将输入序列转换为富含语义的上下文特征向量解码器模块由M个相同的解码器层堆叠而成每层含“掩码多头自注意力层”“编码器-解码器注意力层”和“前馈神经网络层”核心作用是基于编码器特征自回归生成目标序列其他还有一些重要设计残差连接缓解深层网络梯度消失、层归一化稳定训练过程。如果不想探究太多细节Transformer工作流程看如下Gemini总结的图就可以大概了解了输入文本→转换为词向量词嵌入→添加位置信息位置编码 2. 编码阶段编码器通过“全局关注”输入序列的所有token生成包含全局语义的特征向量 3. 解码阶段解码器先“关注自身已生成的token”掩码注意力再“关注编码器的特征向量”编解码注意力逐步生成目标序列 4. 输出阶段通过线性层和softmax将解码器输出转换为最终预测结果。二、Transformer 技术原理详解我们来通过一个文本翻译实例来简单了解 Transformer 是如何工作的Transformer 由编码器和解码器两部分组成首先向编码器输入一句话原句让其学习这句话的特征再将特征作为输入传输给解码器。最后此特征会通过解码器生成输出句目标句。2.1 编码器结构与原理编码器是 Transformer 的第一个核心组件它的任务是将输入序列转换为一个语义丰富的上下文表示后面再喂给后面的解码器。编码器由N个相同的编码器层堆叠而成。每个编码器层包含两个子层多头自注意力层、前反馈层。基本的流程比较简单输入转为嵌入表示后加入位置编码然后输入到多头注意力层再通过叠加归一化再到前馈网络层。多个编码器的化依此类推。接下来逐个元素讲解下2.1.1 多头自注意力层多头自注意力层是编码器的最核心的组件多头也就是多个自注意力机制。自注意力机制简单说就是让句子里的每个词计算和其他所有词的关系从而更精准地计算自身特征。类似最终得到每个单词多少关系单词1多少的单词2… 具体步骤如下首先生成查询、键、值矩阵我们需要为输入序列中的每个元素生成三个向量查询Query, Q、键Key, K和值Value, V。这三个矩阵是通过将输入矩阵 X 分别乘以三个不同的权重矩阵 WQ、WK、WV 得到的Q X × WQK X × WKV X × WV这里的权重矩阵是通过模型训练学习得到的参数。现在让我们学习查询矩阵、键矩阵和值矩阵如何应用于自注意力机制。要计算一个词的特征值自注意力机制会使该词与给定句子中的所有词联系起来。还是以 I am good 这句话为例。为了计算单词 I 的特征值我们将单词 I 与句子中的所有单词一一关联。步骤1Q×K点积——算出“词与词的匹配度”首先计算查询矩阵Q和键矩阵K的点积。这个操作的核心目的是衡量每个词和句子中所有词的相似度.比如计算“I”的特征时就用“I”的查询向量Q_I分别和“I”“am”“good”的键向量K_I、K_am、K_good做点积。点积结果越大说明两个词的关联越紧密——比如Q_I和K_I的点积最大就说明“I”和自己的关联最紧密。同理用“am”的查询向量匹配所有键向量能知道“am”和其他词的相似度用“good”的查询向量匹配能得到“good”与其他词的关联程度。最终会得到一个“匹配度矩阵”记录每个词和所有词的关联分数。步骤2除以√d_k——让梯度更稳定第一步算出的匹配度分数可能会很大容易导致后续训练时梯度不稳定比如梯度爆炸。所以要把整个匹配度矩阵除以“键向量维度d_k的平方根”。步骤3Softmax归一化——把匹配度变成“注意力权重”经过步骤2的分数还没标准化我们用Softmax函数处理一下让每个词对应的一行分数都落在0~1之间而且一行的总和是1。这一步会把“匹配度”变成“注意力权重”——权重越高说明这个词越需要“关注”对应的那个词。比如处理后“I”对应的权重可能是[0.9, 0.07, 0.03]意思是计算“I”的特征时90%关注自己7%关注“am”3%关注“good”。步骤4权重×V——得到最终注意力特征最后一步是用步骤3得到的“注意力权重矩阵”乘以值矩阵V得到最终的注意力特征矩阵。这个过程相当于按权重整合所有词的“价值信息”形成每个词的最终特征。假设计算结果如图还是以“I”为例它的最终特征就是0.9×V_II的值向量 0.07×V_amam的值向量 0.03×V_goodgood的值向量。这有什么用呢为了回答这个问题让我们看一个例句A dog ate the food because it was hungry一只狗吃了食物因为它很饿。在这里it 这个词表示 dog。我们将按照前面的步骤来计算 it 这个词的自注意力值。假设计算过程如图 1-19 所示。图 1-19 单词 it 的自注意力值从图 1-19 中可以看出it 这个词的自注意力值包含 100% 的值向量dog。这有助于模型理解 it 这个词实际上指的是 dog 而不是 food。这也再次说明通过自注意力机制我们可以了解一个词与句子中所有词的相关程度。现将自注意力机制的计算步骤总结如下计算查询矩阵与键矩阵的点积求得相似值称为分数将除以键向量维度的平方根用 softmax 函数对分数进行归一化处理得到分数矩阵通过将分数矩阵与值矩阵相乘计算出注意力矩阵。2.1.2 前馈神经网络层前馈神经网络层对序列中的每个位置独立地应用相同的两层全连接网络。这个网络的结构很简单FFN(x) max(0, x × W₁ b₁) × W₂ b₂其中W₁和 W₂是权重矩阵b₁和 b₂是偏置向量max (0,・) 是 ReLU 激活函数。这个网络的作用是为模型引入非线性变换能力让模型能够学习更复杂的模式。需要注意的是虽然叫 “前馈网络”但它在整个结构中起到了非线性变换和特征增强的作用是不可或缺的。2.1.3 残差连接与层归一化为了确保深层网络能够稳定训练每个子层都采用了残差连接和层归一化技术。残差连接的实现非常简单就是将子层的输入直接叠加到输出上x x SubLayer(x)这种设计可以让梯度直接通过网络避免梯度消失问题。层归一化则是对每个样本的所有特征维度进行标准化使其均值为 0方差为 1。层归一化的公式为LN(y) γ × (y - μ) / √(σ² ε) β其中μ 和 σ² 是 y 在特征维度的均值和方差γ 和 β 是可学习的缩放与偏移参数ε 是防止除零的极小值。2.1.4 位置编码这里你可能会有疑问自注意力机制不区分token顺序把输入序列打乱计算结果不变但文本、时序等数据的顺序至关重要比如“我吃苹果”和“苹果吃我”完全不同。位置编码的作用就是“给每个token打一个唯一的位置标签”将位置信息注入词嵌入让模型知道token的顺序。在将输入矩阵送入编码器之前首先要将位置编码加入输入矩阵中再将其作为输入送入编码器。位置编码的核心思想是为序列中的每个位置生成一个唯一的向量表示这个向量需要包含位置的顺序信息。Transformer 使用了一种基于正弦和余弦函数的位置编码方案其公式为其中pos 是位置索引i 是维度索引d_model 是模型的维度。这种设计有几个重要的优点相对位置可表示对于任意的偏移量 kPE (posk) 可以表示为 PE (pos) 的线性函数这使得模型能够学习到相对位置关系。外推能力即使遇到比训练时更长的序列模型也能通过插值的方式生成相应的位置编码。计算高效位置编码可以预先计算不需要在训练过程中学习。在实现时我们通常会创建一个大小为 (max_len, d_model) 的位置编码矩阵其中每一行对应一个位置的编码。然后将这个矩阵注册为模型的缓冲区buffer这样它就不会参与梯度更新。词嵌入与位置编码的结合这个过程可以表示为H Embedding(X) PositionalEncoding需要注意的是在 PyTorch 的实现中词嵌入通常会乘以√d_model这是为了让词嵌入和位置编码具有相同的方差防止位置编码 “淹没” 原始的词向量信息。最后编码器层的完整计算流程可以总结为输入叠加位置编码经过多头自注意力层处理输出与输入相加残差连接进行层归一化经过前馈神经网络层处理输出与输入相加残差连接再次进行层归一化2.2 解码器结构与原理解码器的结构比编码器稍微复杂一些它需要同时处理两个信息源编码器的输出和已经生成的部分目标序列。下图梳理下编码解码器整体的过程编码器将原句的特征值编码器的输出作为输入传给所有解码器而非只给第一个解码器。因此一个解码器第一个除外将有两个输入一个是来自前一个解码器的输出另一个是编码器输出的特征值。编码器和解码器接下来我们学习解码器究竟是如何生成目标句的。当时表示时间步解码器的输入是 这表示句子的开始。解码器收到 作为输入生成目标句中的第一个词即 Je如图所示。解码器在时的预测结果同理当时解码器使用当前的输入和在上一步生成的单词预测句子中的下一个单词。在本例中解码器将 和 Je来自上一步作为输入并试图生成目标句中的下一个单词如图 所示。解码器在时的预测结果在每一步中解码器都将上一步新生成的单词与输入的词结合起来并预测下一个单词。因此在最后一步解码器将 、Je、vais和 bien 作为输入并试图生成句子中的下一个单词如下图解码器在时的预测结果从中可以看到一旦生成表示句子结束的 标记就意味着解码器已经完成了对目标句的生成工作。2.2.1 解码器层整体结构每个解码器层由三个子层及对应的残差连接、层归一化组成从输入到输出的流程为解码器输入位置编码 → 带掩码的多头自注意力层 → 残差连接层归一化 → 编码器-解码器注意力层 → 残差连接层归一化 → 前馈神经网络层 → 残差连接层归一化 → 解码器层输出其中三个子层各司其职带掩码的多头自注意力层保障生成顺序性编码器-解码器注意力层实现语义对齐前馈神经网络层增强特征表达能力。2.2.2 核心子层一带掩码的多头自注意力层该子层是解码器区别于编码器的核心组件之一核心功能是处理目标序列前缀的内部依赖关系同时通过掩码机制屏蔽未来位置信息确保生成过程的单向性训练与推理逻辑一致。1核心原理自注意力机制的本质是通过“查询Query, Q-键Key, K-值Value, V”的交互的计算得到每个位置的注意力权重再通过权重对V进行加权求和得到该位置的特征表示。而解码器的自注意力层需额外添加“下三角掩码”将未来位置的注意力分数置为负无穷经过Softmax后权重趋近于0从而实现“无法偷看未来信息”的效果。2输入与输出输入目标序列前缀的词嵌入向量位置编码记为T维度[batch_size, 目标序列前缀长度, d_model]。例如生成“我想吃蛋炒饭”时t1输入“”句子开始标记t2输入“ 我”t3输入“ 我 想”以此类推d_model为模型维度原论文设为512。输出经过内部注意力加权与掩码处理后的目标序列前缀特征记为M维度与输入一致。3具体计算步骤结合示例以示例中t3输入“ 我 想”目标序列前缀长度3、d_model512、多头注意力头数h8为例计算过程如下词嵌入与位置编码融合将“”“我”“想”分别转换为512维词嵌入向量再叠加位置编码采用原论文的正弦余弦公式。例如“我”的词嵌入向量为[0.1, 0.2, …, 0.4]512维叠加位置编码pos1后为[0.10.8415, 0.20.5403, …, 0.41.0000]位置编码计算PE(pos,2i)sin(pos/10000(2i/d_model))PE(pos,2i1)cos(pos/10000(2i/d_model))。最终得到输入矩阵T维度[1, 3, 512]batch_size1。生成Q、K、V矩阵通过三个可学习的权重矩阵W_Q、W_K、W_V均为[512, 512]对T进行线性变换得到Q、K、V矩阵维度均为[1, 3, 512]。由于头数h8将Q、K、V按头拆分每个头的Q、K、V维度为[1, 3, 64]512/8。生成掩码矩阵创建下三角掩码矩阵维度[3, 3]对角线以上元素设为-∞对角线及以下设为0矩阵形式为[[0, -∞, -∞], [0, 0, -∞], [0, 0, 0]]该掩码确保计算“”的注意力时仅关注自身计算“我”的注意力时关注“”和自身计算“想”的注意力时关注“”“我”和自身无法关注未来位置如未生成的“吃”“蛋炒饭”。多头注意力计算步骤1计算每个头的注意力分数Score Q·Kᵀ / √d_kd_k64√d_k8用于缩放避免数值过大导致Softmax梯度消失。此时Score维度为[1, 8, 3, 3]。步骤2应用掩码矩阵将Score中对应掩码为-∞的位置置为-1e9修正后的Score仅保留当前及历史位置的有效分数。步骤3Softmax归一化对修正后的Score沿最后一维做Softmax得到注意力权重每行和为1。例如“想”的权重分布为[0.1, 0.3, 0.6]表明主要关注自身其次是“我”和“”。步骤4加权求和与拼接将每个头的注意力权重与对应头的V矩阵相乘得到每个头的输出维度[1, 3, 64]再将8个头的输出拼接通过线性变换W_O[512, 512]得到最终输出M维度[1, 3, 512]2.2.3 核心子层二编码器-解码器注意力层该子层是实现“输入-输出语义对齐”的核心功能是让解码器生成每个目标词时精准关注输入序列中与之相关的词如生成“我”时关注输入的“I”生成“蛋炒饭”时关注输入的“fried rice”。1核心原理与自注意力机制不同编码器-解码器注意力层的Q、K、V来源不同Q来自前一子层带掩码的多头自注意力层的输出M目标序列前缀特征K和V均来自编码器的最终输出记忆向量Memory输入序列全局特征。这种设计让“目标序列查询输入序列的相关信息”实现跨序列的语义对齐Vaswani et al., 2017。2输入与输出输入①前一子层输出M[batch_size, 目标序列前缀长度, d_model]②编码器输出Memory[batch_size, 输入序列长度, d_model]示例中输入序列“I want to eat fried rice”长度为6故Memory维度为[1, 6, 512]。输出融合输入序列语义特征后的目标序列前缀特征记为C维度与M一致。3具体计算步骤结合示例生成Q、K、V矩阵Q由M通过W_Q’[512, 512]线性变换得到维度[1, 3, 512]K由Memory通过W_K’[512, 512]线性变换得到维度[1, 6, 512]V由Memory通过W_V’[512, 512]线性变换得到维度[1, 6, 512]。同样按头拆分h8每个头的Q、K、V维度分别为[1, 3, 64]、[1, 6, 64]、[1, 6, 64]。注意力分数计算与归一化Score Q·Kᵀ / √d_k维度[1, 8, 3, 6]每个元素表示目标序列前缀中某个词与输入序列中某个词的相似度。例如“想”与“I”的相似度分数为12.3与“want”的分数为15.7与其他词的分数均低于10。对Score做Softmax得到注意力权重每行和为1“想”的权重分布为[0.05, 0.8, 0.03, 0.07, 0.03, 0.02]表明主要关注输入的“want”。加权求和与拼接每个头的权重与对应头的V矩阵相乘得到头输出拼接后通过线性变换W_O’[512, 512]得到输出C维度[1, 3, 512]完成语义对齐。2.2.4 核心子层三前馈神经网络层该子层与编码器的前馈网络完全一致核心功能是对每个位置的特征进行独立的非线性变换增强模型的表达能力。2.2.5 残差连接与层归一化Add Norm解码器的每个子层均配备残差连接与层归一化核心功能是稳定深层网络训练缓解梯度消失问题确保特征传递的有效性He et al., 2016; Ba et al., 2016。具体实现逻辑为对每个子层的输入x先计算子层输出SubLayer(x)再通过残差连接将x与SubLayer(x)相加x SubLayer(x)最后进行层归一化。层归一化公式为 LN(y) γ·(y - μ)/√(σ² ε) β其中μ和σ²是y在特征维度的均值和方差γ和β是可学习的缩放与偏移参数ε1e-6避免除零。原论文中采用“子层输出残差连接→层归一化”的顺序Post-Norm后续研究表明“层归一化→子层输出残差连接”Pre-Norm可进一步提升训练稳定性Xiong et al., 2020。2.2.6 输出层线性层与Softmax经过N层解码器处理后顶层输出的特征F需通过输出层转换为目标词汇表空间的概率分布实现词的预测。线性层通过全连接层W_out[d_model, 目标词汇表大小]将F[batch_size, 目标序列前缀长度, d_model]映射为logits[batch_size, 目标序列前缀长度, 目标词汇表大小]。示例中目标词汇表包含“我”“想”“吃”等中文词大小设为10000故logits维度为[1, 3, 10000]。Softmax层对logits沿最后一维做Softmax得到每个位置的词汇概率分布每行和为1。选取概率最大的词作为当前时间步的输出例如t3时“吃”的概率为0.92故生成“吃”。2.3 位置编码机制由于Transformer无循环结构无法通过时序依赖捕捉序列顺序信息因此需通过对输入做位置编码为每个位置赋予唯一表示Vaswani et al., 2017。原论文采用正弦余弦位置编码核心优势是①相对位置可表示任意偏移量kPE(posk)可表示为PE(pos)的线性组合②外推能力强可处理训练时未见过的更长序列。位置编码矩阵预先计算并注册为模型缓冲区不参与梯度更新。总结下解码器的流程首先我们将解码器的输入转换为嵌入矩阵然后将位置编码加入其中并将其作为输入送入底层的解码器解码器 1。解码器收到输入并将其发送给带掩码的多头注意力层生成注意力矩阵。然后将注意力矩阵和编码器输出的特征值作为多头注意力层编码器−解码器注意力层的输入并再次输出新的注意力矩阵。把从多头注意力层得到的注意力矩阵作为输入送入前馈网络层。前馈网络层将注意力矩阵作为输入并将解码后的特征作为输出。最后我们把从解码器1得到的输出作为输入将其送入解码器 2。解码器 2 进行同样的处理并输出目标句的特征。我们可以将个解码器层层堆叠起来。从最后的解码器得到的输出解码后的特征将是目标句的特征。接下来我们将目标句的特征送入线性层和 softmax 层通过概率得到预测的词。现在我们已经详细了解了编码器和解码器的工作原理。让我们把编码器和解码器放在一起看看 Transformer 模型是如何整体运作的。我们可以看到整个流程输入句子原句编码器会学习其特征并将特征发送给解码器而解码器又会生成输出句目标句。三、 Transformer 训练过程训练核心目标是让模型生成的目标序列概率分布尽可能接近真实目标序列的分布通过“预测-计算误差-更新参数”的迭代过程优化所有可训练参数词嵌入矩阵、注意力权重、前馈网络参数等。训练数据及输入数据平行语料如翻译任务的“英文-法文”句子对或单语语料如生成任务的文本序列。输入处理编码器输入原始序列如“I am good”→词嵌入位置编码。解码器输入目标序列前缀如“ Je vais”→词嵌入位置编码确保生成时的时序逻辑。解码器标签真实目标序列如“Je vais bien ”与解码器输出下一个词逐位置对齐用于计算误差。训练过程初始化所有可训练参数词嵌入矩阵、注意力权重、前馈网络权重等为随机小值。批量输入训练数据通过编码器计算全局语义特征R解码器基于R生成目标序列的概率分布。用交叉熵损失计算预测分布与真实标签的差异。通过Adam优化器和反向传播更新所有参数最小化损失。重复步骤2-4迭代训练多轮如数万次直至损失收敛并在验证集上性能最优。四、Transformer逐步计算详解为了让抽象的Transformer原理变具体我们用中文句子“我想吃蛋炒饭”翻译为英文“I want to eat fried rice”的例子一步步拆解计算过程。核心逻辑先把文字转成模型能看懂的“数字向量”再通过“注意力机制”捕捉词与词的关联最后逐步生成目标语言句子。4.1 示例设定为了简化计算我们提前约定好3个关键设定就像做数学题前明确已知条件词汇表把所有要用到的词/符号编上号方便模型计算。{pad:0, 我:1, 想:2, 吃:3, 蛋炒饭:4, I:5, want:6, to:7, eat:8, fried:9, rice:10} pad是填充符号用于统一句子长度词向量维度d_model 4每个词最终会变成一个4个数字的“向量”比如“我”→[0.1,1.2,0.3,1.4]维度越小计算越简单注意力头数n_heads 2相当于让模型用“两个角度”同时关注词的关联最后综合结果每个头的维度d_k d_v 2每个角度的向量长度4.2 输入处理核心目的模型看不懂文字必须把“我想吃蛋炒饭”转成数字向量同时中文是有序的“我想”和“想我”意思不同还要给向量加“顺序信息”。4.2.1 词嵌入我们提前准备一个“词嵌入矩阵E”11×411是词汇表大小4是向量维度相当于“词→向量”的字典E [ [0, 0, 0, 0], # 0pad填充符号 [0.1, 0.2, 0.3, 0.4],# 1我这就是“我”的基础向量 [0.5, 0.6, 0.7, 0.8],# 2想 [0.9, 1.0, 1.1, 1.2],# 3吃 [1.3, 1.4, 1.5, 1.6],# 4蛋炒饭 ... # 英文词汇的向量此处省略原理和中文一样 ]通过这个矩阵我们能直接拿到每个词的“基础向量”的初始值比如“我”的基础向量e_我[0.1,0.2,0.3,0.4]“想”的e_想[0.5,0.6,0.7,0.8]。随着模型训练这个词嵌入矩阵也会随着数字更新以获得更精确的词嵌入表示。4.2.2 位置编码给向量加“顺序标签”基础向量里没有顺序信息所以要加“位置编码PE”。规则很简单用正弦/余弦函数计算不同位置的编码不一样比如第1个词位置0和第2个词位置1的编码不同。具体计算以位置0和位置1为例位置0对应“我”PE(0,0)sin(0/10000^(0/4))0PE(0,1)cos(0)1PE(0,2)sin(0)0PE(0,3)cos(0)1 → PE_0[0,1,0,1] 位置1对应“想”PE(1,0)sin(1/10000^0)sin(1)≈0.8415PE(1,1)cos(1)≈0.5403PE(1,2)sin(1/10000^1)≈0.0001PE(1,3)cos(1/10000^1)≈1.0 → PE_1≈[0.8415,0.5403,0.0001,1.0000]4.2.3 最终输入向量基础向量顺序标签把“词的基础向量”和“对应位置的编码”相加就得到模型真正能处理的输入向量h_我 e_我 PE_0 [0.10, 0.21, 0.30, 0.41] [0.1,1.2,0.3,1.4] h_想 e_想 PE_1 ≈ [0.50.8415, 0.60.5403, 0.70.0001, 0.81.0] ≈ [1.3415,1.1403,0.7001,1.8000]以此类推h_吃、h_蛋炒饭计算逻辑和上面一样分别加位置2、位置3的编码最终得到输入矩阵H₀形状4×4每一行就是一个词的“带顺序的数字向量”4行对应“我、想、吃、蛋炒饭”4个词。4.3 编码器计算提取输入句子的语义关联编码器的核心任务分析输入句子里词与词的关系比如“我”和“吃”有关“吃”和“蛋炒饭”有关把这些关系融入向量里。我们以第一个编码器层为例拆解3个关键步骤。4.3.1 多头自注意力让每个词“关注”相关的词核心逻辑让每个词生成3个向量——Q查询向量相当于“我要找什么”、K键向量相当于“我有什么”、V值向量相当于“我能提供什么”通过Q和K的匹配算出每个词该“关注”其他词的程度注意力权重再用权重整合所有词的V得到新的向量。因为我们设定了2个注意力头相当于用“两个角度”分别计算关注关系最后合并结果。第一步计算头1的Q、K、V第一个关注角度先给头1设定3个参数矩阵W_Q1、W_K1、W_V1都是4×2维度把4维输入向量转成2维适配每个头的维度W_Q1 [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6], [0.7, 0.8]] # 查询参数矩阵 W_K1 [[0.9, 1.0], [1.1, 1.2], [1.3, 1.4], [1.5, 1.6]] # 键参数矩阵 W_V1 [[0.1, -0.1], [-0.2, 0.2], [0.3, -0.3], [-0.4, 0.4]] # 值参数矩阵用输入矩阵H₀分别乘这3个参数矩阵得到头1的Q1、K1、V1都是4×2维度4行对应4个词# 计算逻辑矩阵相乘比如“我”的Q1向量h_我 × W_Q1 Q1 H0 W_Q1 ≈ [ [1.38, 1.6], # 我关注什么 [1.792, 2.056],# 想关注什么 [2.204, 2.512],# 吃关注什么 [2.616, 2.968] # 蛋炒饭关注什么 ] K1 H0 W_K1 ≈ [ [3.38, 3.6], # 我有什么 [4.092, 4.356],# 想有什么 [4.804, 5.108],# 吃有什么 [5.516, 5.860] # 蛋炒饭有什么 ] V1 H0 W_V1 ≈ [ [-0.38, 0.38], # 我能提供什么 [-0.492, 0.492],# 想能提供什么 [-0.604, 0.604],# 吃能提供什么 [-0.716, 0.716] # 蛋炒饭能提供什么 ]第二步计算注意力分数匹配程度和权重关注程度注意力分数用Q1乘K1的转置把K1倒过来再除以√d_k这里d_k2√2≈1.414目的是让分数不要太大得到每个词对其他词的“匹配程度”attn_scores1 Q1 K1.T / 1.414 ≈ [ [7.584, 9.170, 10.756, 12.341], # 我和“我、想、吃、蛋炒饭”的匹配度和蛋炒饭最高 [9.847, 11.904, 13.963, 16.023],# 想和其他词的匹配度和蛋炒饭最高 [12.110, 14.641, 17.172, 19.703],# 吃和其他词的匹配度和蛋炒饭最高 [14.373, 17.389, 20.399, 23.409] # 蛋炒饭和其他词的匹配度和自己最高 ]注意力权重用Softmax函数把分数转成“概率”所有概率加起来1得到每个词该“关注”其他词的程度attn_weights1 softmax(attn_scores1) ≈ [ [0.006, 0.016, 0.043, 0.935], # 我93.5%关注蛋炒饭其他词关注很少 [0.002, 0.008, 0.028, 0.962], # 想96.2%关注蛋炒饭 [0.001, 0.004, 0.016, 0.979], # 吃97.9%关注蛋炒饭 [0.000, 0.002, 0.009, 0.989] # 蛋炒饭98.9%关注自己 ]这个结果很合理“我想吃蛋炒饭”的核心是“蛋炒饭”所以“我、想、吃”都主要关注“蛋炒饭”。第三步计算头1输出整合关注到的信息用注意力权重乘以V1相当于“按关注程度整合所有词的信息”得到头1的输出4×2维度head1 attn_weights1 V1 ≈ [ [-0.686, 0.686], # 我整合了93.5%蛋炒饭的信息 [-0.697, 0.697], # 想整合了96.2%蛋炒饭的信息 [-0.708, 0.708], # 吃整合了97.9%蛋炒饭的信息 [-0.717, 0.717] # 蛋炒饭整合了98.9%自己的信息 ]第四步头2计算与多头合并头2的计算逻辑和头1完全一样只是参数矩阵不同假设得到头2的输出head2 ≈ [ [0.150, -0.150], # 我第二个角度的关注结果 [0.145, -0.145], # 想第二个角度的关注结果 [0.140, -0.140], # 吃第二个角度的关注结果 [0.135, -0.135] # 蛋炒饭第二个角度的关注结果 ]把两个头的输出“拼起来”concat再通过一个线性变换W_O参数矩阵转成4维向量就得到多头自注意力的最终输出attn_output4×4维度multi_head concat([head1, head2]) ≈ [ [-0.686, 0.686, 0.150, -0.150], # 我两个角度的信息合并 [-0.700, 0.700, 0.145, -0.145], # 想两个角度的信息合并 [-0.712, 0.712, 0.140, -0.140], # 吃两个角度的信息合并 [-0.722, 0.722, 0.135, -0.135] # 蛋炒饭两个角度的信息合并 ] attn_output multi_head W_O ≈ [ [0.022, 0.026, 0.030, 0.034], # 我最终的注意力输出向量 [0.006, 0.010, 0.014, 0.018], # 想最终的注意力输出向量 [-0.010, -0.006, -0.002, 0.002], # 吃最终的注意力输出向量 [-0.026, -0.022, -0.018, -0.014] # 蛋炒饭最终的注意力输出向量 ]4.3.2 残差连接和层归一化让模型训练更稳定残差连接把“多头自注意力的输出”和“最初的输入向量H0”加起来residual H0 attn_output。目的是“保留原始信息”避免模型越学越偏。residual ≈ [ [0.122, 1.226, 0.330, 1.434], # 我原始向量注意力信息 [1.3475, 1.1503, 0.7141, 1.8180], # 想原始向量注意力信息 [2.214, 2.522, 1.124, 2.524], # 吃原始向量注意力信息 [2.642, 2.986, 1.538, 3.154] # 蛋炒饭原始向量注意力信息 ]层归一化把上面的结果“标准化”让每个向量的数值范围差不多比如都在-1到1之间方便后续计算。计算逻辑是先算每个向量的平均值mean和标准差std再用向量-平均值/标准差得到归一化结果norm_output ≈ [ [-1.176, 0.803, -0.803, 1.176], # 我归一化后的向量 [0.198, -0.234, -1.190, 1.230], # 想归一化后的向量 [0.201, -0.041, -1.656, 0.729], # 吃归一化后的向量 [0.019, 0.248, -2.083, 0.515] # 蛋炒饭归一化后的向量 ]4.3.3 前馈网络进一步加工向量信息核心逻辑用两个全连接层中间加ReLU激活函数对归一化后的向量做“非线性变换”让向量能表达更复杂的语义。计算过程很简单ffn_output ReLU(norm_output W1 b1) W2 b2假设得到结果4×4维度ffn_output ≈ [ [0.22, 0.33, 0.44, 0.55], # 我加工后的最终向量 [0.20, 0.30, 0.40, 0.50], # 想加工后的最终向量 [0.18, 0.27, 0.36, 0.45], # 吃加工后的最终向量 [0.16, 0.24, 0.32, 0.40] # 蛋炒饭加工后的最终向量 ]之后再对ffn_output做一次“残差连接层归一化”就得到第一个编码器层的输出。这个输出会传给下一个编码器层如果有的话最终得到编码器的“语义特征”相当于把“我想吃蛋炒饭”的核心意思提炼成了一组向量。4.4 解码器计算解码器的核心任务拿着编码器提炼的“语义特征”就像我们要先学习好好听懂对方说的话然后再“一个词一个词”不紧不慢生成目标语言句子。如翻译为英文。4.4.1 先搞懂两个关键机制解码器多了“掩码机制”防止偷看未来的词和“编码器-解码器注意力”让生成的词和原来输入词对应上语义掩码机制生成句子是按顺序的比如先出“I”再出“want”模型不能“作弊”看还没生成的词。掩码就像“遮挡板”把未来位置的词遮住让模型只能关注已经生成的词。编码器-解码器注意力让生成的目标词比如英文“want”找到输入句子里对应的词比如中文“想”确保语义准确这就是“翻译对齐”的核心。4.4.2 目标序列生成过程自回归生成自回归生成从“句子开始标记”出发每一步只生成一个词再把生成的词加进去继续生成下一个直到生成“句子结束标记”。我们以“中文→英文”为例输入我想吃蛋炒饭输出I want to eat fried ricet1第一步输入“sos”→ 经过解码器的词嵌入位置编码再通过“掩码自注意力”只能关注自己、“编码器-解码器注意力”关注输入的“我”最后预测出概率最高的词是“I”概率0.85。 t2第二步输入“sos、I”→ 掩码自注意力能关注“sos”和“I”编码器-解码器注意力关注输入的“想”预测出“want”概率0.92。 t3第三步输入“sos、I、want”→ 预测出“to”概率0.95。 t4第四步输入“sos、I、want、to”→ 预测出“eat”概率0.94。 t5第五步输入“sos、I、want、to、eat”→ 编码器-解码器注意力关注输入的“蛋炒饭”预测出“fried”概率0.96。 t6第六步输入“sos、I、want、to、eat、fried”→ 预测出“rice”概率0.96。 t7第七步输入所有已生成的词→ 预测出“eos”句子结束生成停止。最终生成完整句子“I want to eat fried rice”和我们预期的翻译结果一致。4.4.3 掩码矩阵的具体样子通俗理解以生成到“、I、want、to、eat、fried、rice”长度7为例掩码矩阵是一个7×7的下三角矩阵1表示能关注0表示不能关注mask [ [1, 0, 0, 0, 0, 0, 0], # sos只能关注自己 [1, 1, 0, 0, 0, 0, 0], # I能关注sos和自己 [1, 1, 1, 0, 0, 0, 0], # want能关注前两个词和自己 [1, 1, 1, 1, 0, 0, 0], # to能关注前三个词和自己 [1, 1, 1, 1, 1, 0, 0], # eat能关注前四个词和自己 [1, 1, 1, 1, 1, 1, 0], # fried能关注前五个词和自己 [1, 1, 1, 1, 1, 1, 1] # rice能关注所有前面的词和自己 ]这个矩阵会加在注意力分数上把0的位置分数设为极小值-1e9经过Softmax后权重几乎为0相当于“完全不关注”。4.5 最终输出把向量转成具体的词解码器生成的最后一层向量维度[批量大小, 目标序列长度, d_model]还需要经过两步转换才能变成具体的词4.5.1 线性变换把向量转成词汇表大小的分数用一个全连接层参数矩阵W_out维度[4, 11]4是d_model11是词汇表大小把4维的词向量转成11个分数logits每个分数对应词汇表中一个词的“原始得分”。公式logits decoder_output W_out。4.5.2 Softmax归一化把分数转成概率对11个分数做Softmax运算把分数转成0-1之间的概率所有概率加起来1。我们选概率最大的那个词就是当前位置的预测结果。示例最终概率分布以生成的7个位置为例每个位置的概率分布只展示核心词其他词概率≈0probs[0] ≈ [0.01, 0.02, 0.03, 0.85, 0.04, 0.03, 0.00, 0.00, 0.00, 0.00, 0.00] # 位置1I0.85 probs[1] ≈ [0.00, 0.00, 0.00, 0.00, 0.92, 0.03, 0.02, 0.01, 0.02, 0.00, 0.00] # 位置2want0.92 probs[2] ≈ [0.00, 0.00, 0.00, 0.00, 0.01, 0.95, 0.02, 0.01, 0.01, 0.00, 0.00] # 位置3to0.95 probs[3] ≈ [0.00, 0.00, 0.00, 0.00, 0.00, 0.02, 0.94, 0.02, 0.02, 0.00, 0.00] # 位置4eat0.94 probs[4] ≈ [0.00, 0.00, 0.00, 0.00, 0.00, 0.01, 0.01, 0.96, 0.02, 0.00, 0.00] # 位置5fried0.96 probs[5] ≈ [0.00, 0.00, 0.00, 0.00, 0.00, 0.01, 0.01, 0.02, 0.96, 0.00, 0.00] # 位置6rice0.96 probs[6] ≈ [0.00, 0.00, 0.98, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00] # 位置7eos0.98 ]总结Transformer核心逻辑回顾输入处理文字→词向量位置编码→带顺序的数字向量编码器通过多头自注意力捕捉词间关联再用前馈网络加工→提炼语义特征解码器用掩码防止偷看未来词用编码器-解码器注意力对齐语义逐词生成目标句子最终输出向量→分数→概率→选概率最大的词完成翻译。整个过程就像“翻译官”先读懂中文编码器再按顺序说出英文解码器每说一个词都只看前面说过的内容同时确保和中文意思对应。五、PyTorch 完整实现模型import torch import torch.nn as nn import math # 3.1 基础模块实现 # 3.1.1 位置编码模块 # 作用为序列中的每个位置生成唯一的位置编码弥补Transformer无顺序感知的缺陷 class PositionalEncoding(nn.Module): def __init__(self, d_model: int, max_len: int 5000): 初始化位置编码层 Args: d_model: 模型的特征维度词嵌入维度 max_len: 最大序列长度默认5000 super(PositionalEncoding, self).__init__() # 1. 创建位置编码矩阵形状: [max_len, d_model] pe torch.zeros(max_len, d_model) # 2. 生成位置索引形状: [max_len, 1] position torch.arange(0, max_len, dtypetorch.float).unsqueeze(1) # 3. 计算频率缩放因子避免高频震荡 # 公式div_term exp(2i * (-ln(10000)/d_model))i为维度索引 div_term torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) # 4. 偶数维度用正弦编码奇数维度用余弦编码 pe[:, 0::2] torch.sin(position * div_term) # 步长2取偶数索引 pe[:, 1::2] torch.cos(position * div_term) # 步长2取奇数索引 # 5. 增加批次维度形状变为 [1, max_len, d_model]适配批量输入 pe pe.unsqueeze(0) # 6. 注册为缓冲区随模型保存/加载不参与梯度更新 self.register_buffer(pe, pe) def forward(self, x: torch.Tensor) - torch.Tensor: 前向传播将位置编码加到输入嵌入上 Args: x: 输入张量形状 [batch_size, seq_len, d_model]修正原代码维度说明错误 Returns: 叠加位置编码后的张量形状与输入一致 # 只取与输入序列长度匹配的位置编码避免冗余 x x self.pe[:, :x.size(1), :] return x # 3.1.2 缩放点积注意力模块 # 作用实现Transformer核心的缩放点积注意力机制计算query-key的相似度并加权value class ScaledDotProductAttention(nn.Module): def __init__(self, dropout: float 0.1): 初始化缩放点积注意力层 Args: dropout: dropout概率默认0.1 super(ScaledDotProductAttention, self).__init__() self.dropout nn.Dropout(dropout) def forward(self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor, mask: torch.Tensor None) - tuple[torch.Tensor, torch.Tensor]: 前向传播计算缩放点积注意力 Args: query: 查询张量形状 [batch_size, n_heads, seq_len_q, d_k] key: 键张量形状 [batch_size, n_heads, seq_len_k, d_k] value: 值张量形状 [batch_size, n_heads, seq_len_v, d_k]seq_len_kseq_len_v mask: 掩码张量形状 [batch_size, 1, seq_len_q, seq_len_k]0表示屏蔽位置 Returns: output: 注意力加权后的输出形状 [batch_size, n_heads, seq_len_q, d_k] attn_weights: 注意力权重形状 [batch_size, n_heads, seq_len_q, seq_len_k] # 1. 获取每个头的维度 d_k query.size(-1) # 2. 计算query-key点积相似度并缩放除以√d_k避免梯度爆炸 scores torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k) # 3. 掩码处理屏蔽位置设为负无穷softmax后权重趋近于0 if mask is not None: scores scores.masked_fill(mask 0, -1e9) # 4. 计算注意力权重softmax归一化 dropout attn_weights torch.softmax(scores, dim-1) attn_weights self.dropout(attn_weights) # 5. 加权求和得到最终输出 output torch.matmul(attn_weights, value) return output, attn_weights # 3.1.3 多头注意力模块 # 作用将输入拆分为多个头并行计算注意力捕捉不同维度的特征 class MultiHeadAttention(nn.Module): def __init__(self, d_model: int, n_heads: int, dropout: float 0.1): 初始化多头注意力层 Args: d_model: 模型总维度需能被n_heads整除 n_heads: 注意力头数 dropout: dropout概率默认0.1 super(MultiHeadAttention, self).__init__() # 校验d_model必须能被头数整除 assert d_model % n_heads 0, d_model必须能被n_heads整除 self.d_model d_model # 模型总维度 self.n_heads n_heads # 注意力头数 self.d_k d_model // n_heads # 每个头的维度 # 定义线性变换层将输入投影到d_model维度 self.w_q nn.Linear(d_model, d_model) 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) # 输出投影层 # 缩放点积注意力实例 self.attention ScaledDotProductAttention(dropout) self.dropout nn.Dropout(dropout) self.layer_norm nn.LayerNorm(d_model, eps1e-6) # 层归一化 def forward(self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor, mask: torch.Tensor None) - tuple[torch.Tensor, torch.Tensor]: 前向传播多头注意力计算 Args: query/key/value: 输入张量形状 [batch_size, seq_len, d_model] mask: 掩码张量形状 [batch_size, 1, seq_len, seq_len] Returns: output: 多头注意力输出形状 [batch_size, seq_len, d_model] attn_weights: 注意力权重形状 [batch_size, n_heads, seq_len, seq_len] batch_size query.size(0) # 1. 线性变换 拆分多头 # 形状变化[batch_size, seq_len, d_model] - [batch_size, n_heads, seq_len, d_k] Q self.w_q(query).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2) K self.w_k(key).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2) V self.w_v(value).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2) # 2. 计算缩放点积注意力 x, attn_weights self.attention(Q, K, V, mask) # 3. 合并多头输出 # 形状变化[batch_size, n_heads, seq_len, d_k] - [batch_size, seq_len, d_model] # contiguous()确保张量内存连续避免view操作报错 x x.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model) # 4. 输出投影 dropout x self.dropout(self.w_o(x)) return x, attn_weights # 3.2 编码器和解码器层 # 3.2.1 编码器层 # 作用单个编码器层包含多头自注意力 前馈网络 残差连接 层归一化 class EncoderLayer(nn.Module): def __init__(self, d_model: int, n_heads: int, d_ff: int, dropout: float 0.1): 初始化编码器层 Args: d_model: 模型维度 n_heads: 注意力头数 d_ff: 前馈网络隐藏层维度 dropout: dropout概率 super(EncoderLayer, self).__init__() # 多头自注意力层 self.self_attn MultiHeadAttention(d_model, n_heads, dropout) # 前馈网络两层线性变换 ReLU激活 self.feed_forward nn.Sequential( nn.Linear(d_model, d_ff), nn.ReLU(), nn.Linear(d_ff, d_model) ) # 层归一化Post-Norm与原始论文一致 self.norm1 nn.LayerNorm(d_model, eps1e-6) self.norm2 nn.LayerNorm(d_model, eps1e-6) self.dropout nn.Dropout(dropout) def forward(self, x: torch.Tensor, mask: torch.Tensor None) - torch.Tensor: 前向传播编码器层计算 Args: x: 输入张量形状 [batch_size, seq_len, d_model] mask: 源序列掩码填充掩码 Returns: 编码器层输出形状与输入一致 # 1. 自注意力 残差连接 层归一化 attn_output, _ self.self_attn(x, x, x, mask) x self.norm1(x attn_output) # 2. 前馈网络 残差连接 层归一化 ff_output self.dropout(self.feed_forward(x)) x self.norm2(x ff_output) return x # 完整编码器原代码缺失补充实现 class Encoder(nn.Module): def __init__(self, src_vocab_size: int, d_model: int, n_layers: int, n_heads: int, d_ff: int, max_len: int 5000, dropout: float 0.1): 初始化完整编码器 Args: src_vocab_size: 源语言词汇表大小 d_model: 模型维度 n_layers: 编码器层数 n_heads: 注意力头数 d_ff: 前馈网络维度 max_len: 最大序列长度 dropout: dropout概率 super(Encoder, self).__init__() self.d_model d_model # 词嵌入层 self.embedding nn.Embedding(src_vocab_size, d_model) # 位置编码层 self.pos_encoding PositionalEncoding(d_model, max_len) # 编码器层堆叠 self.layers nn.ModuleList([ EncoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(n_layers) ]) self.dropout nn.Dropout(dropout) def forward(self, src: torch.Tensor, mask: torch.Tensor None) - torch.Tensor: 前向传播编码器整体计算 Args: src: 源序列索引形状 [batch_size, seq_len] mask: 源序列掩码 Returns: 编码器输出memory形状 [batch_size, seq_len, d_model] # 1. 词嵌入 位置编码嵌入层乘以√d_model原始论文操作 x self.embedding(src) * math.sqrt(self.d_model) x self.pos_encoding(x) x self.dropout(x) # 2. 逐层计算编码器 for layer in self.layers: x layer(x, mask) return x # 3.2.2 解码器层 # 作用单个解码器层包含掩码自注意力 交叉注意力 前馈网络 残差/归一化 class DecoderLayer(nn.Module): def __init__(self, d_model: int, n_heads: int, d_ff: int, dropout: float 0.1): 初始化解码器层 Args: d_model: 模型维度 n_heads: 注意力头数 d_ff: 前馈网络维度 dropout: dropout概率 super(DecoderLayer, self).__init__() # 掩码自注意力防止偷看未来信息 self.self_attn MultiHeadAttention(d_model, n_heads, dropout) # 编码器-解码器交叉注意力 self.cross_attn MultiHeadAttention(d_model, n_heads, dropout) # 前馈网络 self.feed_forward nn.Sequential( nn.Linear(d_model, d_ff), nn.ReLU(), nn.Linear(d_ff, d_model) ) # 层归一化 self.norm1 nn.LayerNorm(d_model, eps1e-6) self.norm2 nn.LayerNorm(d_model, eps1e-6) self.norm3 nn.LayerNorm(d_model, eps1e-6) self.dropout nn.Dropout(dropout) def forward(self, x: torch.Tensor, memory: torch.Tensor, src_mask: torch.Tensor None, tgt_mask: torch.Tensor None) - torch.Tensor: 前向传播解码器层计算 Args: x: 目标序列输入形状 [batch_size, seq_len_tgt, d_model] memory: 编码器输出形状 [batch_size, seq_len_src, d_model] src_mask: 源序列掩码 tgt_mask: 目标序列掩码填充序列掩码 Returns: 解码器层输出形状与x一致 # 1. 掩码自注意力 残差 归一化 attn1_output, _ self.self_attn(x, x, x, tgt_mask) x self.norm1(x attn1_output) # 2. 交叉注意力Q来自解码器K/V来自编码器 残差 归一化 attn2_output, _ self.cross_attn(x, memory, memory, src_mask) x self.norm2(x self.dropout(attn2_output)) # 3. 前馈网络 残差 归一化 ff_output self.dropout(self.feed_forward(x)) x self.norm3(x ff_output) return x # 完整解码器原代码缺失补充实现 class Decoder(nn.Module): def __init__(self, tgt_vocab_size: int, d_model: int, n_layers: int, n_heads: int, d_ff: int, max_len: int 5000, dropout: float 0.1): 初始化完整解码器 Args: tgt_vocab_size: 目标语言词汇表大小 d_model: 模型维度 n_layers: 解码器层数 n_heads: 注意力头数 d_ff: 前馈网络维度 max_len: 最大序列长度 dropout: dropout概率 super(Decoder, self).__init__() self.d_model d_model # 词嵌入层 self.embedding nn.Embedding(tgt_vocab_size, d_model) # 位置编码层 self.pos_encoding PositionalEncoding(d_model, max_len) # 解码器层堆叠 self.layers nn.ModuleList([ DecoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(n_layers) ]) self.dropout nn.Dropout(dropout) def forward(self, tgt: torch.Tensor, memory: torch.Tensor, src_mask: torch.Tensor None, tgt_mask: torch.Tensor None) - torch.Tensor: 前向传播解码器整体计算 Args: tgt: 目标序列索引形状 [batch_size, seq_len_tgt] memory: 编码器输出形状 [batch_size, seq_len_src, d_model] src_mask: 源序列掩码 tgt_mask: 目标序列掩码 Returns: 解码器输出形状 [batch_size, seq_len_tgt, d_model] # 1. 词嵌入 位置编码 x self.embedding(tgt) * math.sqrt(self.d_model) x self.pos_encoding(x) x self.dropout(x) # 2. 逐层计算解码器 for layer in self.layers: x layer(x, memory, src_mask, tgt_mask) return x # 3.3 完整的Transformer模型 class Transformer(nn.Module): def __init__(self, src_vocab_size: int, tgt_vocab_size: int, d_model: int 512, n_layers: int 6, n_heads: int 8, d_ff: int 2048, max_len: int 5000, dropout: float 0.1): 初始化完整的Transformer模型 Args: src_vocab_size: 源语言词汇表大小 tgt_vocab_size: 目标语言词汇表大小 d_model: 模型核心维度默认512与论文一致 n_layers: 编码器/解码器层数默认6 n_heads: 注意力头数默认8 d_ff: 前馈网络隐藏层维度默认2048 max_len: 最大序列长度 dropout: dropout概率 super(Transformer, self).__init__() # 编码器 self.encoder Encoder(src_vocab_size, d_model, n_layers, n_heads, d_ff, max_len, dropout) # 解码器 self.decoder Decoder(tgt_vocab_size, d_model, n_layers, n_heads, d_ff, max_len, dropout) # 输出层将解码器输出映射到目标词汇表 self.fc_out nn.Linear(d_model, tgt_vocab_size) # 初始化模型参数 self._init_parameters() def _init_parameters(self): 参数初始化使用Xavier均匀初始化提升训练稳定性 for p in self.parameters(): if p.dim() 1: # 仅对矩阵参数初始化偏置项除外 nn.init.xavier_uniform_(p) def forward(self, src: torch.Tensor, tgt: torch.Tensor, src_mask: torch.Tensor None, tgt_mask: torch.Tensor None) - torch.Tensor: 前向传播完整Transformer计算 Args: src: 源序列索引形状 [batch_size, seq_len_src] tgt: 目标序列索引形状 [batch_size, seq_len_tgt] src_mask: 源序列掩码 tgt_mask: 目标序列掩码 Returns: 输出概率分布形状 [batch_size, seq_len_tgt, tgt_vocab_size] # 1. 编码器编码源序列得到memory memory self.encoder(src, src_mask) # 2. 解码器基于memory解码目标序列 output self.decoder(tgt, memory, src_mask, tgt_mask) # 3. 线性变换到目标词汇表大小 output self.fc_out(output) return output # 3.4 掩码生成函数 def create_masks(src: torch.Tensor, tgt: torch.Tensor, pad_idx: int) - tuple[torch.Tensor, torch.Tensor]: 生成Transformer所需的掩码 Args: src: 源序列形状 [batch_size, seq_len_src] tgt: 目标序列形状 [batch_size, seq_len_tgt] pad_idx: 填充符的索引如EOS/PAD Returns: src_mask: 源序列掩码形状 [batch_size, 1, 1, seq_len_src] tgt_mask: 目标序列掩码形状 [batch_size, 1, seq_len_tgt, seq_len_tgt] # 1. 源序列掩码填充掩码屏蔽pad_idx位置 # 形状变化[batch_size, seq_len_src] - [batch_size, 1, 1, seq_len_src] src_mask (src ! pad_idx).unsqueeze(1).unsqueeze(2) # 2. 目标序列掩码填充掩码 序列掩码防止偷看未来 # 2.1 填充掩码 tgt_pad_mask (tgt ! pad_idx).unsqueeze(1).unsqueeze(2) # 2.2 序列掩码下三角矩阵True表示可见False表示屏蔽未来 tgt_len tgt.size(1) tgt_sub_mask torch.tril(torch.ones((tgt_len, tgt_len), devicetgt.device)).bool() # 2.3 组合掩码同时屏蔽填充和未来位置 tgt_mask tgt_pad_mask tgt_sub_mask return src_mask, tgt_mask # ------------------------------ 测试代码可选 ------------------------------ if __name__ __main__: # 测试参数 src_vocab_size 1000 tgt_vocab_size 1000 batch_size 2 seq_len_src 10 seq_len_tgt 8 pad_idx 0 # 创建模型实例 model Transformer(src_vocab_size, tgt_vocab_size) # 生成测试数据 src torch.randint(1, src_vocab_size, (batch_size, seq_len_src)) # 避免pad_idx tgt torch.randint(1, tgt_vocab_size, (batch_size, seq_len_tgt)) # 生成掩码 src_mask, tgt_mask create_masks(src, tgt, pad_idx) # 前向传播 output model(src, tgt, src_mask, tgt_mask) print(f模型输出形状: {output.shape}) # 预期: [2, 8, 1000]六、PyTorch 实战训练一个简单的翻译模型我们将通过一个完整的 PyTorch 训练流程展示如何使用 Transformer 进行一个简单的翻译任务。#5.1 数据集准备 我们创建一个非常简单的中英翻译数据集 import torch from torch.utils.data import Dataset, DataLoader class SimpleTranslationDataset(Dataset): def __init__(self): # 源语言中文词汇表 self.src_vocab { pad: 0, sos: 1, eos: 2, 我: 3, 想: 4, 吃: 5, 蛋炒饭: 6 } self.src_idx_to_word {v: k for k, v in self.src_vocab.items()} # 目标语言英文词汇表 self.tgt_vocab { pad: 0, sos: 1, eos: 2, I: 3, want: 4, to: 5, eat: 6, fried: 7, rice: 8 } self.tgt_idx_to_word {v: k for k, v in self.tgt_vocab.items()} # 训练数据 self.data [ # 中文 - 英文 ( [1, 3, 4, 5, 6, 2], # sos 我 想 吃 蛋炒饭 eos [1, 3, 4, 5, 6, 7, 8, 2] # sos I want to eat fried rice eos ), # 添加更多示例... ] def __len__(self): return len(self.data) def __getitem__(self, idx): return self.data[idx] def src_vocab_size(self): return len(self.src_vocab) def tgt_vocab_size(self): return len(self.tgt_vocab) 5.2 模型初始化 # 超参数设置 src_vocab_size dataset.src_vocab_size() tgt_vocab_size dataset.tgt_vocab_size() d_model 32 # 简化为32维 n_layers 2 # 使用2层 n_heads 2 d_ff 128 max_len 10 dropout 0.1 # 初始化模型 device torch.device(cuda if torch.cuda.is_available() else cpu) model Transformer(src_vocab_size, tgt_vocab_size, d_model, n_layers, n_heads, d_ff, max_len, dropout).to(device) # 定义优化器和损失函数 optimizer torch.optim.Adam(model.parameters(), lr0.001) criterion nn.CrossEntropyLoss(ignore_index0) # 忽略填充符 5.3 训练循环 def train(model, dataloader, optimizer, criterion, pad_idx, device, n_epochs100): model.train() for epoch in range(n_epochs): total_loss 0 for batch_idx, (src, tgt) in enumerate(dataloader): src src.to(device) tgt tgt.to(device) # 创建掩码 src_mask, tgt_mask create_masks(src, tgt, pad_idx) src_mask src_mask.to(device) tgt_mask tgt_mask.to(device) # 前向传播 optimizer.zero_grad() output model(src, tgt[:, :-1], src_mask, tgt_mask[:, :, :-1, :-1]) # 计算损失 loss criterion(output.contiguous().view(-1, output.size(-1)), tgt[:, 1:].contiguous().view(-1)) # 反向传播 loss.backward() optimizer.step() total_loss loss.item() # 打印训练信息 if (epoch 1) % 10 0: avg_loss total_loss / len(dataloader) print(fEpoch {epoch1}/{n_epochs}, Average Loss: {avg_loss:.4f}) # 创建数据加载器 dataset SimpleTranslationDataset() dataloader DataLoader(dataset, batch_size2, shuffleTrue) # 开始训练 train(model, dataloader, optimizer, criterion, 0, device) 5.4 模型推理 训练完成后我们可以进行推理 def translate(model, src_sentence, src_vocab, tgt_vocab, device, max_len10): model.eval() # 将源句子转换为索引 src_idx [src_vocab[sos]] for word in src_sentence: if word in src_vocab: src_idx.append(src_vocab[word]) else: src_idx.append(src_vocab[pad]) src_idx.append(src_vocab[eos]) # 转换为张量 src_tensor torch.tensor(src_idx, dtypetorch.long).unsqueeze(0).to(device) # 创建源掩码 src_mask (src_tensor ! 0).unsqueeze(1).unsqueeze(2).to(device) # 初始化目标序列 tgt_idx [tgt_vocab[sos]] with torch.no_grad(): for i in range(max_len): # 转换为目标张量 tgt_tensor torch.tensor(tgt_idx, dtypetorch.long).unsqueeze(0).to(device) # 创建目标掩码 tgt_mask create_masks(tgt_tensor, tgt_tensor, 0)[1][:, :, :-1, :-1].to(device) # 推理 output model(src_tensor, tgt_tensor, src_mask, tgt_mask) # 获取最后一个位置的预测 last_token_logits output[0, -1, :] _, pred_token torch.max(last_token_logits, dim-1) # 如果预测为eos则结束 if pred_token.item() tgt_vocab[eos]: break # 添加到目标序列 tgt_idx.append(pred_token.item()) # 转换为单词 translated_words [tgt_vocab[idx] for idx in tgt_idx[1:]] # 去掉sos return translated_words # 测试翻译 src_sentence [我, 想, 吃, 蛋炒饭] translated_words translate(model, src_sentence, dataset.src_vocab, dataset.tgt_vocab, device) print(f源句子: { .join(src_sentence)}) print(f翻译结果: { .join(translated_words)})模型训练优化建议过拟合在小数据集上训练时容易过拟合。可以使用 dropout、增加训练数据、使用预训练模型等方法缓解。内存占用大Transformer 的内存需求与序列长度的平方成正比因为注意力矩阵的大小是 seq_len×seq_len。在处理长序列时需要特别注意。收敛速度慢相比 CNNTransformer 的收敛速度可能较慢。可以使用学习率调度器、增加训练轮数等方法。层归一化的位置原始论文使用的是 post-norm残差连接后进行层归一化但 recent 研究表明 pre-norm残差连接前进行层归一化可能有更好的稳定性。优化器选择可以尝试使用 AdamW 优化器它在 Adam 的基础上添加了权重衰减通常能取得更好的效果。学习率调度使用 warmup 策略在训练初期逐渐增加学习率有助于模型的稳定收敛。混合精度训练使用 PyTorch 的混合精度训练可以减少内存占用提高训练速度。结论本文系统地介绍了 Transformer 的技术原理与 PyTorch 实现。通过从整体架构到具体组件的详细讲解我们了解模型是如何通过自注意力机制实现并行计算和长距离依赖建模的。读者福利如果大家对大模型感兴趣这套大模型学习资料一定对你有用对于0基础小白入门如果你是零基础小白想快速入门大模型是可以考虑的。一方面是学习时间相对较短学习内容更全面更集中。二方面是可以根据这些资料规划好学习计划和方向。作为一名老互联网人看着AI越来越火也总想为大家做点啥。干脆把我这几年整理的AI大模型干货全拿出来了。包括入门指南、学习路径图、精选书籍、视频课还有我录的一些实战讲解。全部免费不搞虚的。学习从来都是自己的事我能做的就是帮你把路铺平一点。资料都放在下面了有需要的直接拿能用到多少就看你自己了。这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以点击文章最下方的VX名片免费领取【保真100%】
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

php网站服务器wordpress 家教主题

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 开发一个智能农业监控系统原型,功能要求:1) 土壤温湿度监测 2) 光照强度采集 3) 自动灌溉控制 4) 数据可视化看板 5) 异常预警功能。需要生成完整的硬件连接…

张小明 2025/12/26 13:06:11 网站建设

企业网站建设范文邯郸网站建设taigew

2025年,企业微信智能表格凭借AI驱动的功能升级,成为企业解决数据混乱、协作低效的关键工具。它通过智能字段识别、跨系统集成、多视图管理等能力,将传统表格的“手动整理”转向“自动驱动”,帮助企业实现数据实时同步、高效协作和…

张小明 2025/12/26 12:55:39 网站建设

利用wix建手机网站网站开发后端做那些

FreeIPA 集成与管理指南 1. 用户信息查询 在不登录主机的情况下,我们可以使用 getent 命令来查询用户信息。例如,查询用户 jose 的信息: [root@romeo ~]# getent passwd jose jvazquez:*:597800004:597800004:Jose Vazquez:/home/jose:/bin/sh不过,在未应用 ID 视图…

张小明 2025/12/26 13:08:37 网站建设

网站seo快速优化上海优化公司选哪个

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

张小明 2025/12/26 13:07:48 网站建设

网站建立企业wordpress olam

FaceFusion在游戏NPC个性化定制中的应用前景 在当今的游戏开发中,玩家对沉浸感的期待已远超以往。一个眼神呆滞、表情僵硬的NPC很容易将人拉出虚拟世界;而一张熟悉又生动的脸——哪怕只是隐约像极了自己或亲友——却能让整个体验变得截然不同。这种“真实…

张小明 2025/12/25 22:37:21 网站建设

哪个购物网站最便宜制作介绍的网站模板免费下载

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 请生成一个完整的性能对比测试方案,包含:1) 手动编写复杂cron任务的平均耗时统计;2) 使用快马平台AI生成相同功能的耗时统计;3) 两种…

张小明 2025/12/26 13:07:00 网站建设