Transformer定义 Transformer是一种更现代的深度学习模型专为处理序列数据而设计最初用于自然语言处理任务。它不依赖于RNN或CNN等传统结构而是引入了注意力机制。结构 Transformer模型主要由编码器和解码器组成它们由自注意力层和全连接前馈网络组成。它使用注意力机制来捕捉输入序列中不同位置之间的依赖关系同时通过多头注意力来提高模型的表达能力。优势 Transformer的设计使其能够更好地处理长距离依赖关系同时具有更好的并行性。google研究院提出的tramsformer模型总体示意图如下所示。详细讲解可以参考李宏毅教授的机器学习网课【2025版】Transformer (上)_哔哩哔哩_bilibili关键算法多头注意力理论多头注意力机制将查询、键和值矩阵分成多个头即多个子空间每个头具有不同的线性变换参数。每个头独立地计算注意力得分并生成一个注意力加权后的输出。这些输出随后被合并形成一个最终的、更复杂的表示。由于每个头的计算是独立的这些计算可以并行进行从而提高模型的计算效率。这种并行性使得多头注意力机制在处理长序列数据时更加高效。输入句子与词向量句子 “Thinking Machines” 先变成每个 token 的向量表示组成矩阵X图里还画了一个R表示在后续 encoder 层里输入不一定是原始embedding而是上一层encoder的输出表示线性映射得到 Q/K/V图中第 3 步W Q , W K , W V W^Q, W^K, W^VWQ,WK,WV对每个 head都有自己的一套投影矩阵第 i 个头Q i X W i Q , K i X W i K , V i X W i V Q_i X W_i^Q,\;\; K_i X W_i^K,\;\; V_i X W_i^VQiXWiQ,KiXWiK,ViXWiV直观含义QQuery我“想找什么信息”KKey我“有哪些可被匹配的索引”VValue真正“要取走并加权汇总的信息内容”多头的核心拆成 8 个头并行做注意力。同一个X XX通过 8 套不同参数的投影得到 8 组Q i , K i , V i Q_i, K_i, V_iQi,Ki,Vi。每个头独立计算一次缩放点积注意力Scaled Dot-Product Attention注意力权重Attn i softmax ( Q i K i ⊤ d k ) \text{Attn}_i \text{softmax}\left(\frac{Q_i K_i^\top}{\sqrt{d_k}}\right)Attnisoftmax(dkQiKi⊤)该头输出Z i Attn i V i Z_i \text{Attn}_i V_iZiAttniVi4拼接各头输出把 8 个头的输出按特征维拼接Z Concat ( Z 0 , Z 1 , … , Z 7 ) Z \text{Concat}(Z_0, Z_1, \dots, Z_7)ZConcat(Z0,Z1,…,Z7)此时Z ZZ汇聚了多个“视角”下的注意力结果最后一层线性变换拼接后的Z ZZ再乘输出矩阵W O W^OWO得到该层多头注意力的最终输出MHA ( X ) Z W O \text{MHA}(X) Z W^OMHA(X)ZWO编程实现首先实例化多头注意力模型所需的全连接层其中默认包含可学习的W权重矩阵和b偏置矩阵self.W_qnn.Linear(d_model,d_model)self.W_knn.Linear(d_model,d_model)self.W_vnn.Linear(d_model,d_model)self.W_onn.Linear(d_model,d_model)分别计算 Q、K、Vqueryself.W_q(q)# (batch, seq_len, d_model)keyself.W_k(k)valueself.W_v(v)多头注意力可以与GPU批处理结合并行的计算一批中所有头我们书写的时候也要注意这一点将一批数据切分后之后只需要处理其中一个头其他的头也会并行的自动被计算。取每个头的维度self.d_k d_model // h之后根据d_k的值对Q、K、V进行切分query query.view(query.shape[0], query.shape[1], self.h, self.d_k).transpose(1, 2) key key.view( key.shape[0], key.shape[1], self.h, self.d_k).transpose(1, 2) value value.view(value.shape[0], value.shape[1], self.h, self.d_k).transpose(1, 2)对每一个分头进行attention计算得到的Attn i \text{Attn}_iAttni完毕后进行合并x x.transpose(1, 2).contiguous().view(x.shape[0], -1, self.h * self.d_k)最后计算Z Attn V Z \text{Attn} VZAttnV得到注意力矩阵Z ZZ前馈神经网络FFN的经典结构线性变换升维→ 激活函数 → 线性变换降维通过激活函数引入非线性对信息进行深度加工让每个词语“独立深度思考”消化和提炼信息FFN是多头注意力的完美搭档注意力层每个词查看全句决定“我需要关注哪些词的信息”FFN层每个词独立对自己获得的信息进行深度思考结果经过多个这样的“收集-思考”循环模型逐步理解从语法到语义的各个层面激活函数演进ReLU最基础max(0,x)计算简单GELU更平滑考虑输入分布效果更好SwiGLU当前主流如LLaMA、GPT性能更优代码表示returnself.linear2(self.dropout(torch.relu(self.linear1(x))))位置编码 PositionalEncodings理论Transformer 是一个完全并行计算的模型。不像 RNN/CNN 那样通过结构隐含顺序它处理输入序列是无顺序感知的。为了解决这个问题Transformer 在每个词的表示上添加了一个与它在句中位置有关的向量也就是 Positional Encoding。论文中的原始计算公式如下这里pos是位置i是当前维度的位置d表示词向量维度对于输入的每个 token我们遍历维度对每一个维度使用公式计算出其数值为其生成一个长度为d m o d e l d_modeldmodel的向量交替使用sin和cos来填充奇偶维度。最终二者相加给词向量注入位置感知能力这种方法简介但是也存在一些限制后续可能的优化途径代码petorch.zeros(seq_len,d_model)# 初始化一个全零张量pe形状为 (seq_len, d_model)positiontorch.arange(0,seq_len,dtypetorch.float).unsqueeze(1)# position是一个形状为 (seq_len, 1) 的张量包含从0到seq_len-1的整数表示每个位置的索引div_termtorch.exp(torch.arange(0,d_model,2).float()*(-math.log(10000.0)/d_model))# 生成从0到d_model-1的偶数索引的张量形状为 (d_model/2,)并将其乘以一个缩放因子得到div_termpe[:,0::2]torch.sin(position*div_term)# 选取所有行位置和偶数列赋值为sin(position * div_term)pe[:,1::2]torch.cos(position*div_term)# 对于偶数 d_model 索引使用正弦对于奇数索引使用余弦这里利用了 PyTorch的广播功能自动将position的第二个维度拓展成d_model/2得到seq_lend_model/2的pe矩阵再在第0维增加batch维度得到形状(1, seq_len, d_model)以便与batch中的多个句子进行广播相加。最后编写向前传播函数将位置编码和token序列相加并进行dropoutdef forward(self, x): # x的形状为 (batch, seq_len, d_model) x x self.pe[:, :x.shape[1], :].detach() # 将输入x与位置编码相加位置编码通过切片操作选择与输入序列长度匹配的部分并使用detach()方法确保位置编码在反向传播过程中不更新 return self.dropout(x)交叉注意力 Cross_Attention交叉注意力机制顾名思义是一种“交叉”的注意力形式。它允许一个序列或信息流中的元素关注另一个不同序列中的元素从而实现两个序列之间的信息对齐和融合。仅微调交叉注意力层就能高效地将预训练模型适配到新的翻译任务上同时保留了跨语言对齐的特性。这里我们直接复用多头注意力的类通过改变输入参数K、V即可。xself.residual_conn[1](x,lambda x:self.cross_attention(x,encoder_output,encoder_output,src_mask))词嵌入层InputEmbeddings类实现了一个标准的词嵌入层将离散的token索引转换为连续的向量表示并按照Transformer论文的做法乘以sqrt(d_model)以调整尺度。这个模块是Transformer模型处理输入文本的第一步。def __init__(self, d_model :int, vocab_size :int): super().__init__() self.d_model d_model self.vocab_size vocab_size self.embedding nn.Embedding(vocab_size, d_model) def forward(self, x): return self.embedding(x) * math.sqrt(self.d_model)创建 PyTorch 的 nn.Embedding 层它本质上是一个可训练的查找表输入token索引整数输出对应的d_model维向量。x输入的 token 索引张量形状通常为(batch_size, seq_len)每个元素是token在词汇表中的索引。self.embedding(x)将索引映射为词嵌入向量输出形状为 (batch_size, seq_len, d_model)为什么乘以 sqrt(d_model)乘以sqrt(d_model) 后向量的方差会变为d_model使得嵌入向量的尺度与后续的位置编码相加时处于相近的量级两者尺度匹配相加时不会有一方主导适当的缩放有助于在训练初期稳定梯度避免因嵌入值过大或过小导致的梯度消失或爆炸编码器Encoder编码器含有多个编码模块EncoderBlock每个Encoder Block含有多头注意力、前馈神经网络、残差网络模块。Dncoder就是堆叠多个EncoderBlock最后加 一层 LayerNorm。我们首先编写一个EncoderBlock其包含__init__方法和forward方法。__init__方法初始化编码模块的信息并且提前保留向前传播所需的残差连接模块。def __init__(self, features: int, # 特征维度d_model self_attention : MultiHeadAttention, # 多头自注意力模块 FFN : FeedForwardNetwork, # 前馈神经网络模块 dropout :float):# dropout率 super().__init__() # 初始化父类 self.self_attention self_attention self.FFN FFN self.residual_conn nn.ModuleList([ResidualConnection(features, dropout) for _ in range (2)]) # 使用ModuleList存储两个残差连接模块分别用于自注意力和前馈神经网络的残差连接需要定义了一个可复用的残差连接模块ResidualConnection用于包装任意的子层class ResidualConnection(nn.Module): def __init__(self, features: int, dropout :float): super().__init__() self.dropout nn.Dropout(dropout) self.norm LayerNorm(features) # LayerNorm来进行pre-norm以便进行残差连接 def forward(self, x, sublayer): # apply layer norm first then add sublayer return x self.dropout(sublayer(self.norm(x)))features: int特征维度即 d_model用于层归一化确保输入和输出维度一致。dropout: floatdropout 概率用于对子层输出进行随机失活。sublayer一个可调用对象代表要执行的子层如self_attention 或 FFNself.norm采用自己编写LayerNorm来进行归一化forward函数让输入张量先归一化再经过子层最后加回原始输入形成残差连接。这有助于缓解梯度消失问题让信息更直接地流过深层网络。这里我们给出层归一化LayerNorm的定义class LayerNorm(nn.Module): def __init__(self, features: int, eps :float 10**-6): super().__init__() self.eps eps #保存eps供向前传播使用 self.alpha nn.Parameter(torch.ones(features)) # 保存可学习的缩放参数alpha初始化为全1维度为features self.beta nn.Parameter(torch.ones(features)) # 保存可学习的偏移参数beta初始化为全1维度为features def forward(self, x): mean x.mean(dim -1, keepdim True) std x.std(dim -1, keepdim True) # 计算输入x的均值和标准差沿着最后一个维度计算并保持维度不变keepdimTrue以便后续的广播操作 return self.alpha * ((x - mean)/(std self.eps)) self.beta # 对输入x进行层归一化处理首先将x减去均值并除以标准差加上一个小的eps以避免除零然后乘以可学习的缩放参数alpha并加上可学习的偏移参数beta最后返回归一化后的结果接着我们继续编写EncoderBlock的向前传播函数defforward(self,x,src_mask):xself.residual_conn[0](x,lambdax:self.self_attention(x,x,x,src_mask))# 调用自注意力模块并与之前保存的x进行残差连接得到新的xxself.residual_conn[1](x,self.FFN)# 调用前馈神经网络模块并与之前保存的x进行残差连接得到新的xreturnx# 返回最终的x作为编码器块的输出调用之前构造的residual_conn实例封装地进行多头注意力/前馈神经网络的计算再加残差连接的操作。多头注意力和前馈神经网络在前文中以及已经了编写。在论文原文中作者采用了六个EncoderBlock组成Encoder这里可以利用循环来进行六个模块的叠加每一层的输出x作为下一层的输入x最后经过一次层归一化得到最终的Encoder的结果classEncoder(nn.Module):def__init__(self,features:int,layers:nn.ModuleList):super().__init__()self.layerslayers self.normLayerNorm(features)# 进行层归一化确保输出的特征维度为featuresdefforward(self,x,mask):forlayerinself.layers:xlayer(x,mask)# 依次通过每个编码器块更新x的值returnself.norm(x)解码器DecoderDecoder堆叠多个DecoderBlock最后加LayerNorm,论文中的Decoder采用了6个DecoderBlock堆叠。DecoderBlock包含三个子层掩码自注意力、交叉注意力、前馈网络同时每个子层外都有残差连接。最后将解码器输出映射回词汇表大小并取 log-softmax得到自然语言结果首先编写DecoderBlock形式类似编码器但是要加入一个cross_attention层def __init__(self, features: int, self_attention : MultiHeadAttention, cross_attention : MultiHeadAttention, FFN : FeedForwardNetwork, dropout :float): super().__init__() self.self_attention self_attention self.cross_attention cross_attention self.FFN FFN self.residual_conn nn.ModuleList([ResidualConnection(features, dropout) for _ in range(3)]) def forward(self, x, encoder_output, src_mask, trgt_mask): x self.residual_conn[0](x, lambda x : self.self_attention(x, x, x, trgt_mask)) x self.residual_conn[1](x, lambda x : self.cross_attention(x, encoder_output, encoder_output, src_mask)) x self.residual_conn[2](x, self.FFN)ProjectionLayer还需要一个新类用于将解码器的输出映射到词汇表大小的 logits然后应用 log_softmax以便后续计算损失或生成概率。super().__init__()self.projnn.Linear(d_model,vocab_size)创建一个全连接层输入维度为d_model输出维度为vocab_size。它将解码器的每个位置的隐藏表示映射到词汇表上的logitsdef forward(self, x): return torch.log_softmax(self.proj(x), dim -1)self.proj(x)通过线性层将每个位置的d_model维向量映射为vocab_size维的logits输出形状为 (batch_size, seq_len, vocab_size)。torch.log_softmax(…, dim-1)在最后一个维度词汇表维度上计算log-softmax得到对数概率Transformer类我们已经完成了所有必要组建的编写最后来书写Transformer类**构造函数__init__()**接收所有预构建好的子模块编码器、解码器、嵌入层、位置编码、投影层并将它们保存为成员变量。encode 方法对源语言序列进行处理先通过词嵌入src_embed然后添加位置编码src_pos最后送入编码器encoder得到编码表示。ecode 方法对目标语言序列进行处理先通过目标词嵌入trgt_embed添加位置编码trgt_pos然后送入解码器decoder同时传入编码器的输出和相应的掩码。project 方法将解码器输出投影到目标词汇表大小通过 proj_layer得到最终的 logits。build_model函数这是模型的构建函数根据超参数创建所有组件并组装成完整的 Transformerdefbuild_model(src_vocab_size:int,trgt_vocab_size:int,src_seq_len:int,trgt_seq_len:int,d_model:int512,d_ff:int2048,N:int6,h:int8,dropout:float0.25):参数包括源/目标词汇表大小、源/目标序列最大长度、模型维度、前馈网络维度、编码器/解码器层数N、注意力头数h、dropout概率后续构造Model实例def build_model(src_vocab_size :int, trgt_vocab_size :int, src_seq_len :int, trgt_seq_len :int, d_model :int 512, d_ff :int 2048,N :int 6, h :int 8, dropout :float 0.25): src_embed InputEmbeddings(d_model, src_vocab_size) trgt_embed InputEmbeddings(d_model, trgt_vocab_size) # 输入嵌入层的输入参数包括特征维度d_model和词汇表大小vocab_size分别用于源语言和目标语言的输入嵌入层 src_pos PositionalEncodings(d_model, src_seq_len, dropout) trgt_pos PositionalEncodings(d_model, trgt_seq_len, dropout) # 位置编码器的输入参数包括特征维度d_model、序列长度seq_len和dropout率分别用于源语言和目标语言的位置编码器 encoder_blocks [] for _ in range(N): self_attention MultiHeadAttention(d_model, h, dropout) ffn FeedForwardNetwork(d_model , d_ff, dropout) encoder_block EncoderBlock(d_model, self_attention, ffn, dropout) encoder_blocks.append(encoder_block) decoder_blocks [] for _ in range(N): self_attention MultiHeadAttention(d_model, h, dropout) cross_attention MultiHeadAttention(d_model, h, dropout) ffn FeedForwardNetwork(d_model , d_ff, dropout) decoder_block DecoderBlock(d_model, self_attention, cross_attention, ffn, dropout) decoder_blocks.append(decoder_block) # 构建编码器块和解码器块的列表使用循环来创建N个编码器块和N个解码器块每个块都包含多头自注意力模块、前馈神经网络模块和残差连接模块 encoder Encoder(d_model, nn.ModuleList(encoder_blocks)) decoder Decoder(d_model, nn.ModuleList(decoder_blocks)) # 创建编码器和解码器实例传入特征维度d_model和编码器块列表、解码器块列表 projection_layer ProjectionLayer(d_model, trgt_vocab_size) # 创建投影层实例传入特征维度d_model和目标语言的词汇表大小trgt_vocab_size transformer Transformer(encoder, decoder, src_embed, trgt_embed, src_pos, trgt_pos, projection_layer) # 创建Transformer模型实例传入编码器、解码器、输入嵌入层、位置编码器和投影层 for p in transformer.parameters(): if p.dim() 1: nn.init.xavier_uniform_(p) # 对模型的参数进行初始化使用Xavier均匀分布初始化权重参数条件是参数的维度大于1即权重矩阵而非偏置向量 return transformer