尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Seq2Seq模型Decoder集成Attention机制:原理、PyTorch实现与优化

Seq2Seq模型Decoder集成Attention机制:原理、PyTorch实现与优化 1. 项目概述深入拆解Seq2Seq中的Decoder与Attention机制如果你正在研究自然语言处理尤其是机器翻译、文本摘要或者对话生成那么“Seq2Seq模型”和“Attention机制”这两个词你一定不陌生。今天我们不聊那些泛泛的概念而是聚焦在一个非常具体且核心的模块上Seq2Seq模型中Decoder部分如何集成Attention机制。这个模块我习惯称之为“Attention-04-decoder部分”因为它往往是你在理解了Encoder、基础Decoder和Attention原理后需要动手实现的第四个关键环节。它不是一个独立的算法而是将理论转化为有效模型的关键桥梁。简单来说它决定了Decoder在生成每一个目标词时应该“注意”源序列的哪些部分从而显著提升长序列处理的准确性和流畅度。无论你是刚入门想复现一个翻译模型的学生还是需要在项目中优化生成效果的一线工程师搞懂这个部分的实现细节和设计考量都能让你少走很多弯路。2. 核心架构与设计思路拆解2.1 Seq2Seq与Attention的协同工作流要理解Decoder中的Attention我们必须先回顾经典的Seq2Seq框架。最原始的模型像一个“压缩-解压”过程Encoder将整个源句子比如一句英文压缩成一个固定长度的上下文向量Context Vector然后Decoder基于这个单一的向量逐个生成目标句子比如中文。这个架构的瓶颈很明显对于长句子一个固定向量很难记住所有细节信息导致生成效果下降。Attention机制的引入彻底改变了这个信息传递的方式。它不再强迫Encoder把所有信息塞进一个“小盒子”里而是允许Decoder在生成每一个词时动态地、有选择性地去“回顾”Encoder输出的所有隐藏状态。你可以把它想象成翻译的过程当你在把英文“I love playing basketball”翻译成中文“我喜欢打篮球”时生成“篮球”这个词时你的注意力自然会更多地集中在源句子的“basketball”上而不是平均地看待整个句子。那么这个动态的“回顾”过程是如何在Decoder中实现的呢其核心工作流可以概括为以下几步Encoder输出Encoder通常是一个双向RNN、LSTM或Transformer Encoder会为源序列的每一个时间步即每一个词生成一个隐藏状态。这些状态共同构成了一个“记忆库”。Decoder当前状态在Decoder生成第t个目标词时它拥有自己上一个时间步的隐藏状态s_{t-1}以及可能的上一个输出y_{t-1}。计算注意力分数将Decoder的当前状态s_{t-1}与Encoder的所有隐藏状态进行比较计算出一个“相关性”分数。这个分数表示对于当前要生成的词源序列中每个词的重要性。生成上下文向量利用上一步计算出的分数通常经过Softmax归一化为权重对Encoder的所有隐藏状态进行加权求和。这个加权和的结果就是当前时间步专属的“上下文向量”c_t。它不再是固定的而是随着Decoder生成过程动态变化的。解码生成将动态的上下文向量c_t与Decoder当前的状态s_{t-1}以及上一个输出y_{t-1}进行拼接或融合输入到Decoder的RNN单元中计算出新的状态s_t并最终预测出当前时间步的输出词y_t。这个流程就是“Attention-04-decoder部分”要完成的核心任务。它不是一个黑盒而是一个清晰、可微分的计算模块。2.2 为何选择“加性Attention”或“点积Attention”在具体实现时计算注意力分数第3步主要有两种主流方式加性注意力Additive Attention和点积/缩放点积注意力Dot-Product/ Scaled Dot-Product Attention。在早期的RNN-based Seq2Seq模型中加性注意力更为常见而在Transformer成为主流的今天缩放点积注意力则是绝对的核心。加性注意力Additive / Bahdanau Attention 它的计算方式是将Decoder状态和Encoder状态分别通过一个线性层通常是一个单层神经网络然后相加再经过一个tanh激活函数和另一个线性层即一个向量v_a来得到一个标量分数。 公式大致为score(s_t-1, h_i) v_a^T * tanh(W_a * [s_t-1; h_i])。 这里的[;]表示拼接。这种方式的好处是它显式地将Decoder和Encoder的状态映射到一个新的空间再进行交互理论上表达能力更强尤其当两者维度不同时。但缺点也很明显它引入了额外的可学习参数W_a,v_a增加了计算量。点积/缩放点积注意力Dot-Product / Scaled Dot-Product Attention 这是Transformer使用的机制。计算方式简单直接score(s_t-1, h_i) s_t-1 · h_i点积。如果Decoder和Encoder的隐藏层维度d_k相同点积操作天然地衡量了二者的相似度。 但是当d_k较大时点积的值可能会非常大导致Softmax后的梯度非常小进入饱和区。因此Transformer论文中提出了“缩放”操作score(s_t-1, h_i) (s_t-1 · h_i) / sqrt(d_k)。这个除以根号d_k的操作使得点积后的数值分布更稳定有利于训练。选择考量 对于基于RNN/LSTM的Seq2Seq模型由于Encoder和Decoder的隐藏状态通常维度固定且相同点积注意力实现简单、计算高效是一个很好的选择。这也是为什么在PyTorch的许多教程和nn.Transformer的早期自定义实现中点积注意力被广泛使用。而加性注意力则更像是一个“保险”的选择当你不确定维度匹配或模型表现不佳时可以尝试用它。但在实际工程中除非有特殊需求否则我通常会优先使用缩放点积注意力因为它更简洁、更快且是当前主流架构Transformer的基石相关的优化如Flash Attention也更多。注意这里讨论的是经典的Seq2Seq with Attention。在“decoder-only架构”如GPT系列中Attention是模型的自注意力Self-Attention和交叉注意力Cross-Attention层本身其实现已经深度集成在Transformer Block里概念相通但结构位置不同。3. 核心模块实现细节解析3.1 Attention层的接口设计与输入输出在代码中我们将这个模块实现为一个独立的PyTorchnn.Module。清晰的接口设计是模块可复用和易调试的关键。这个Attention层应该完成我们上面提到的第3、4步计算注意力权重并生成上下文向量。输入decoder_hiddenDecoder上一个时间步的隐藏状态。形状通常为(batch_size, hidden_dim)。对于LSTM可能是最后一个层的隐藏状态或者是隐藏状态和细胞状态的组合通常取隐藏状态。encoder_outputsEncoder对所有源序列时间步的输出。这是我们的“记忆库”。形状为(src_len, batch_size, hidden_dim)或(batch_size, src_len, hidden_dim)。为了后续矩阵运算方便我更喜欢使用(batch_size, src_len, hidden_dim)的格式这与Transformer的惯例一致。输出context_vector计算出的动态上下文向量。形状为(batch_size, hidden_dim)。它包含了当前解码步最需要关注的源序列信息。attention_weights注意力权重。形状为(batch_size, src_len)。这个输出非常有用它可以被可视化让我们直观地看到模型在生成每个词时关注了源句子的哪些部分是调试和理解模型行为的重要工具。内部过程 模块内部会进行如下计算首先将decoder_hidden扩展维度以匹配encoder_outputs进行运算计算注意力分数然后通过Softmax得到权重最后加权求和得到context_vector。3.2 缩放点积注意力的PyTorch实现下面是一个典型的、用于RNN Seq2Seq模型的缩放点积注意力模块的实现。我添加了详细的注释并解释了几个关键的实现技巧。import torch import torch.nn as nn import torch.nn.functional as F import math class ScaledDotProductAttention(nn.Module): 缩放点积注意力模块。 用于经典的RNN/LSTM-based Seq2Seq模型中连接Decoder和Encoder。 def __init__(self, hidden_dim): super(ScaledDotProductAttention, self).__init__() self.hidden_dim hidden_dim # 缩放因子即 sqrt(d_k) self.scale math.sqrt(hidden_dim) def forward(self, decoder_hidden, encoder_outputs): Args: decoder_hidden: Tensor of shape (batch_size, hidden_dim) encoder_outputs: Tensor of shape (batch_size, src_len, hidden_dim) Returns: context_vector: Tensor of shape (batch_size, hidden_dim) attention_weights: Tensor of shape (batch_size, src_len) batch_size, src_len, hidden_dim encoder_outputs.size() # 1. 计算点积分数 # decoder_hidden: (batch_size, hidden_dim) - (batch_size, 1, hidden_dim) # 这样才可以与encoder_outputs进行矩阵乘法 decoder_hidden decoder_hidden.unsqueeze(1) # 现在形状是 (batch_size, 1, hidden_dim) # 点积操作: (batch_size, 1, hidden_dim) (batch_size, hidden_dim, src_len) (batch_size, 1, src_len) # 更直观的做法是使用torch.bmm (batch matrix multiply) # 或者直接使用einsum: b i h, b j h - b i j # 这里我们使用bmm需要将encoder_outputs转置最后两个维度 scores torch.bmm(decoder_hidden, encoder_outputs.transpose(1, 2)) # (batch_size, 1, src_len) scores scores.squeeze(1) # (batch_size, src_len) # 2. 缩放 scores scores / self.scale # 3. 应用Softmax得到注意力权重 attention_weights F.softmax(scores, dim-1) # (batch_size, src_len) # 确保权重和为1在最后一个维度上 # print(attention_weights.sum(dim1)) # 应该接近全1张量 # 4. 计算上下文向量加权和 # attention_weights: (batch_size, src_len) - (batch_size, 1, src_len) # encoder_outputs: (batch_size, src_len, hidden_dim) # context_vector: (batch_size, 1, hidden_dim) - (batch_size, hidden_dim) context_vector torch.bmm(attention_weights.unsqueeze(1), encoder_outputs) context_vector context_vector.squeeze(1) return context_vector, attention_weights实现要点与技巧维度变换的艺术使用unsqueeze和squeeze来增加或减少维度是注意力实现中的常规操作。关键在于时刻清楚每个张量的形状。bmm批矩阵乘法要求参与运算的两个张量是三维的且中间的两个维度需要匹配。这里我们通过unsqueeze(1)将decoder_hidden从(B, H)变为(B, 1, H)将encoder_outputs从(B, L, H)转置为(B, H, L)这样bmm的结果就是(B, 1, L)即每个批次、当前解码步对源序列所有位置的分数。缩放因子的重要性self.scale math.sqrt(hidden_dim)是缩放点积注意力的精髓。不要忘记它尤其是在隐藏维度较大如512、1024时不加缩放会导致训练初期非常不稳定。Softmax维度F.softmax(scores, dim-1)中的dim-1表示在最后一个维度即src_len维度上进行Softmax确保对于每个批次中的每个样本其所有源词位置的权重之和为1。可视化权重返回的attention_weights是一个极其有用的调试工具。你可以将它对齐源句子和目标句子画出来热力图直观检查模型的注意力是否合理例如对角线注意力在翻译中很常见。3.3 将Attention集成到Decoder中实现了Attention层之后下一步就是把它嵌入到Decoder的整体循环中。一个集成了Attention的Decoder步骤比普通Decoder多了一步在每一个时间步先调用Attention层计算上下文向量再将这个上下文向量与当前的输入和状态进行融合。以下是Decoder一个时间步的伪代码流程# 假设我们已经有了一个Attention模块 self.attention # decoder_input_t: 当前时间步的输入通常是上一个时间步的输出词的embedding形状 (batch_size, embed_dim) # decoder_hidden: 上一个时间步的RNN隐藏状态形状 (batch_size, hidden_dim) # encoder_outputs: Encoder的所有输出形状 (batch_size, src_len, hidden_dim) # 步骤1计算当前步的注意力上下文向量 context_vector, attn_weights self.attention(decoder_hidden, encoder_outputs) # 步骤2将上下文向量与当前输入拼接 # 这是最常见的融合方式也可以尝试相加或门控机制 combined_input torch.cat((decoder_input_t, context_vector), dim1) # 形状变为 (batch_size, embed_dim hidden_dim) # 步骤3将融合后的向量输入RNN单元得到新的隐藏状态 decoder_output, decoder_hidden self.rnn_cell(combined_input, decoder_hidden) # 步骤4可选将新的隐藏状态和上下文向量进一步融合用于预测输出词 # 例如可以再次拼接后通过一个线性层Softmax output_feature torch.cat((decoder_output, context_vector), dim1) final_output self.fc_out(output_feature) # self.fc_out是一个线性层输出维度是目标词表大小 final_output F.log_softmax(final_output, dim1) # 返回当前步的预测分布(final_output)、新的隐藏状态(decoder_hidden)、注意力权重(attn_weights) return final_output, decoder_hidden, attn_weights关键设计决策融合点在哪里融合context_vector常见的有两种1在输入RNN之前与输入词嵌入拼接如上例2在RNN输出之后与RNN的输出拼接再送入最终的分类层。第一种方式让Attention信息能直接影响RNN内部的状态演化第二种方式则将其作为最终预测的额外信息。经验上第一种方式更为常见和有效。RNN单元选择可以使用nn.GRUCell或nn.LSTMCell。对于LSTM需要小心处理细胞状态cell_state。通常Attention只基于隐藏状态hidden_state计算细胞状态在RNN内部更新。Teacher Forcing在训练时我们通常使用teacher forcing即decoder_input_t是真实目标序列中的上一个词而不是模型自己预测的词以加速收敛。这需要在训练循环中逻辑控制。4. 完整训练流程与代码整合4.1 构建完整的Attention Seq2Seq模型现在我们将Encoder、带Attention的Decoder以及最终的生成逻辑整合起来。这里我给出一个高度概括但结构清晰的模型类定义框架。class Seq2SeqAttn(nn.Module): def __init__(self, encoder, decoder, device): super().__init__() self.encoder encoder self.decoder decoder self.device device def forward(self, src, trg, teacher_forcing_ratio0.5): # src: (src_len, batch_size) # trg: (trg_len, batch_size) batch_size src.shape[1] trg_len trg.shape[0] trg_vocab_size self.decoder.output_dim # 初始化一个张量来存储每个时间步的输出 outputs torch.zeros(trg_len, batch_size, trg_vocab_size).to(self.device) # 初始化一个张量来存储注意力权重用于可视化 attentions torch.zeros(trg_len, batch_size, src.shape[0]).to(self.device) # 1. Encoder前向传播 encoder_outputs, hidden self.encoder(src) # encoder_outputs: (src_len, batch, hidden*num_directions) # 调整encoder_outputs形状以匹配Decoder Attention的输入要求 (batch, src_len, hidden) # 如果是双向RNN需要处理双向的隐藏状态通常取和或拼接最后层的前向后向状态 encoder_outputs encoder_outputs.permute(1, 0, 2) # - (batch, src_len, hidden*num_directions) # 调整初始Decoder隐藏状态例如取Encoder的最后层前向隐藏状态 if isinstance(hidden, tuple): # LSTM hidden hidden[0] # 取隐藏状态忽略细胞状态 # hidden: (num_layers * num_directions, batch, hidden_dim) # 我们通常用最后一层的所有方向的状态初始化Decoder。简单处理取最后一层如果是双向则拼接或求和。 # 这里假设是单向直接用最后一层 decoder_hidden hidden[-1, :, :].unsqueeze(0) # (1, batch, hidden) 符合Decoder RNN的初始状态输入要求 # 2. Decoder初始输入是SOS token input trg[0, :] # (batch_size) # 3. Decoder循环 for t in range(1, trg_len): # 从1开始因为0是SOS # 集成Attention的Decoder一步 output, decoder_hidden, attention self.decoder(input, decoder_hidden, encoder_outputs) outputs[t] output # 存储预测分布 attentions[t] attention # 存储注意力权重 # 决定下一个输入是真实标签还是模型预测 teacher_force random.random() teacher_forcing_ratio top1 output.argmax(1) # 获取预测概率最大的词索引 input trg[t] if teacher_force else top1 return outputs, attentions这个框架省略了Encoder和Decoder内部的具体构造如嵌入层、RNN类型等但清晰地展示了数据流Encoder处理源序列输出记忆库和初始状态Decoder在每个时间步利用Attention从记忆库中提取信息并逐步生成目标序列。4.2 训练循环的关键配置与超参数选择训练一个带Attention的Seq2Seq模型除了常规的超参数有几个点需要特别关注优化器与学习率Adam优化器是默认的起点。学习率可以从3e-4或1e-3开始。使用学习率调度器如ReduceLROnPlateau在验证集损失停滞时降低学习率对收敛很有帮助。损失函数由于我们输出的是每个词在词表上的对数概率分布使用nn.CrossEntropyLoss并设置ignore_index为填充符PAD token的索引以避免计算填充位置的损失。梯度裁剪Gradient Clipping对于RNN模型梯度爆炸是个常见问题。在loss.backward()之后调用torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)将梯度范数裁剪到一个阈值内能显著提升训练稳定性。Teacher Forcing比率这是一个重要的技巧。在训练初期使用高比率如0.9的Teacher Forcing可以帮助模型快速学习到合理的语言模式。随着训练进行可以逐渐降低这个比率课程学习让模型更多依赖自己的预测从而提高推理时的鲁棒性。批次Batch与序列长度使用BucketIterator或padded_batch将长度相似的句子放在同一个批次中可以减少填充提高计算效率。动态填充每个批次单独填充到该批次最大长度是标准做法。一个简化的训练步骤循环如下model.train() for epoch in range(num_epochs): for batch in train_iterator: src, src_len batch.src trg batch.trg optimizer.zero_grad() output, _ model(src, trg, teacher_forcing_ratio0.5) # 训练时使用Teacher Forcing # output形状: (trg_len, batch_size, vocab_size) # trg形状: (trg_len, batch_size) # 需要reshape以计算损失 output_dim output.shape[-1] output output[1:].view(-1, output_dim) # 忽略SOS并展平时间步和批次维度 trg trg[1:].view(-1) # 忽略SOS loss criterion(output, trg) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step()5. 高级话题与性能优化5.1 应对长序列与效率优化从Flash Attention到Paged Optimizer当序列长度很长比如处理长文档或高分辨率图像的序列化表示时标准的Attention计算会遇到两个主要问题巨大的内存占用O(L²)和缓慢的计算速度。这在训练大型模型时是致命的。Flash Attention正是为了解决这个问题而生的革命性优化。它不是一个新奇的Attention算法而是对标准缩放点积Attention计算过程Softmax的重新组织。核心思想是通过**平铺Tiling和重计算Recomputation**技术将注意力矩阵的运算分解成小块在GPU的高速SRAM共享内存中进行并避免将庞大的中间矩阵QK^T写回慢速的HBM显存。简单来说Flash Attention通过精妙的算法设计在数学等价的前提下极大地减少了GPU内存的读写次数即IO开销从而实现了数倍到数十倍的加速并且内存占用与序列长度呈线性关系。对于我们的“Attention-04-decoder部分”如果你在实现一个基于Transformer的Decoder或者处理超长序列的RNN Attention直接使用集成Flash Attention的库如xformers库的memory_efficient_attention或PyTorch 2.0的torch.nn.functional.scaled_dot_product_attention其后端在支持的情况下会自动调用Flash Attention是提升性能的首选。对于自定义的RNN Attention由于其计算模式相对固定且序列长度通常不会极端长Flash Attention的收益可能不如Transformer场景明显但了解其原理对优化任何注意力计算都有启发。Paged Optimizer分页优化器是另一个层面的优化主要解决的是GPU显存碎片化和大模型参数优化器状态内存过大的问题。例如在训练非常大的模型时优化器状态如Adam的动量和方差可能比模型参数本身还要大几倍。NVIDIA的Apex库或更现代的bitsandbytes库提供了类似“分页Adam”的优化器它们像操作系统管理内存一样管理优化器状态将暂时不用的部分转移到CPU内存需要时再换入从而允许在有限的GPU显存中训练更大的模型。这对于我们训练大型Seq2Seq模型如十亿参数级别至关重要。5.2 注意力机制的变体与应用扩展基础的注意力机制已经很强大了但在不同场景下衍生出了许多变体全局注意力 vs 局部注意力我们上面实现的是全局注意力Decoder可以关注源序列的任何位置。对于非常长的序列这计算开销大且可能不必要。局部注意力Local Attention限制Decoder只关注源序列的一个窗口如前后10个词在保证性能的同时大幅降低计算量。这在某些语音识别或长文档任务中很有效。多头注意力Multi-Head Attention这是Transformer的核心。与其只计算一次注意力不如将查询Q、键K、值V投影到多个不同的子空间头并行计算多个注意力函数然后将结果拼接并再次投影。这允许模型同时关注来自不同表示子空间的信息极大地增强了模型的表达能力。在Decoder中通常包含两种多头注意力自注意力关注已生成的目标序列部分和编码器-解码器注意力即我们实现的交叉注意力。注意力在CV中的应用Attention U-Net注意力机制早已超越NLP。在图像分割领域Attention U-Net在经典的U-Net架构上增加了注意力门Attention Gate。在解码器上采样路径的每一层它利用编码器对应层的特征图作为“键”和“值”利用解码器当前层的特征图作为“查询”生成一个空间注意力图。这个图会加权编码器的特征从而让网络在重建细节时更关注与当前解码区域相关的语义部分抑制不相关的背景噪声显著提升了分割精度特别是在医学图像中器官边界模糊的情况下。理解这些变体能帮助你在面对不同任务时知道如何调整或选择更适合的注意力形式。例如如果你的任务中源序列和目标序列的对齐关系非常单调如语音识别局部注意力可能更高效如果你的模型需要捕捉非常复杂的依赖关系那么多头注意力是必须的。6. 实战调试与常见问题排查6.1 注意力权重可视化与模型诊断模型训练起来了但怎么知道它学得好不好注意力权重的可视化是最强大的诊断工具之一。在验证或测试时保存模型返回的attention_weights形状为(trg_len, batch_size, src_len)。# 假设我们有一个训练好的模型和一对样例 model.eval() with torch.no_grad(): output, attentions model(src, trg, teacher_forcing_ratio0) # 推理时不用Teacher Forcing # attentions: (trg_len, batch_size, src_len) # 取第一个样本的注意力 sample_attn attentions[:, 0, :].cpu().numpy() # (trg_len, src_len) import matplotlib.pyplot as plt import matplotlib.ticker as ticker def plot_attention(attention, source_sentence, target_sentence): fig plt.figure(figsize(10, 10)) ax fig.add_subplot(111) cax ax.matshow(attention, cmapbone) fig.colorbar(cax) # 设置坐标轴标签 ax.set_xticklabels([] source_sentence, rotation90) ax.set_yticklabels([] target_sentence) ax.xaxis.set_major_locator(ticker.MultipleLocator(1)) ax.yaxis.set_major_locator(ticker.MultipleLocator(1)) plt.show() # 将索引转换为单词 source_words [src_field.vocab.itos[i] for i in src[:, 0].cpu().numpy() if i ! src_field.vocab.stoi[pad]] target_words [trg_field.vocab.itos[i] for i in trg[:, 0].cpu().numpy() if i ! trg_field.vocab.stoi[pad]] plot_attention(sample_attn.T, source_words, target_words) # 转置一下让源句子在x轴一个训练良好的翻译模型其注意力图通常近似对角线但也会有合理的发散如形容词修饰名词时。如果注意力图非常分散或呈现奇怪的模式可能意味着模型没有收敛或者超参数如学习率设置不当。6.2 常见问题速查与解决方案在实际操作中你几乎一定会遇到下面这些问题。这里是我的排查清单问题现象可能原因排查步骤与解决方案损失不下降或为NaN1. 学习率过高。2. 梯度爆炸。3. 数据预处理问题如token未归一化。4. Attention分数未缩放维度大时。1.降低学习率尝试1e-4, 3e-5。2.实施梯度裁剪(clip_grad_norm_)范数设为1.0或5.0。3. 检查数据确保词嵌入已初始化输入值范围正常。4.确认Attention计算中是否除以了sqrt(d_k)。模型输出重复或无关词1. Teacher Forcing比率过高模型未学会自主推理。2. 解码策略贪婪搜索陷入局部最优。3. 注意力机制失效权重均匀。1.逐步降低Teacher Forcing比率或在训练后期采用计划采样。2. 推理时尝试束搜索Beam Searchbeam size设为4-10。3.可视化注意力图检查注意力是否聚焦。检查Attention层输入维度是否正确。训练速度极慢1. 序列填充过多计算浪费。2. 未使用GPU。3. 模型结构有瓶颈如未批处理。1. 使用BucketIterator按长度分组批次减少填充。2. 确保张量和模型都在.to(device)。3. 确保Decoder循环是向量化操作避免Python原生循环可使用torch.nn.utils.rnn.pack_padded_sequence处理变长序列。验证集损失先降后升过拟合1. 模型容量过大数据量不足。2. 缺乏正则化。1. 增加Dropout在RNN层之间、全连接层之前。2. 尝试权重衰减L2正则化。3. 获取更多训练数据或使用数据增强。注意力权重全为零或均匀1. Softmax前分数值过大或过小导致梯度消失。2. Encoder输出或Decoder隐藏状态初始化不当。1.确保进行了缩放(/ sqrt(d_k))。2. 检查Encoder和Decoder的激活函数避免梯度饱和。尝试使用**层归一化LayerNorm**稳定训练。3. 检查初始化方法。6.3 从RNN Attention到Transformer Decoder的思维迁移最后理解这个“Attention-04-decoder部分”是通往现代Transformer架构的绝佳跳板。在Transformer的Decoder中Attention机制被用到了极致Masked Self-AttentionDecoder在预测第t个位置时只能看到1到t-1的位置这是通过一个注意力掩码上三角矩阵为负无穷实现的防止信息泄露。Encoder-Decoder Attention这就是我们实现的交叉注意力层Query来自Decoder上一层的输出Key和Value来自Encoder的输出。多头并行上述两种注意力都是多头的并行计算后再融合。位置前馈网络在注意力层之后还有一个全连接前馈网络为每个位置进行独立变换。因此当你熟练掌握了在RNN Decoder中集成和调试Attention后再去学习Transformer的Decoder你会发现其核心思想一脉相承只是组织方式更模块化、并行化程度更高。这时你可以利用PyTorch内置的nn.TransformerDecoderLayer和nn.TransformerDecoder来快速构建更强大的模型而底层原理早已了然于胸。
返回列表