理解Transformer解码器

1.3 理解解码器

假设我们想把英语句子I am good(原句)翻译成法语句子Je vais bien(目标句)。首先,将原句I am good送入编码器,使编码器学习原句,并计算特征值。在前文中,我们学习了编码器是如何计算原句的特征值的。然后,我们把从编码器求得的特征值送入解码器。解码器将特征值作为输入,并生成目标句Je vais bien,如图1-35所示。

在这里插入图片描述

图1-35 Transformer的编码器和解码器

在编码器部分,我们了解到可以 N N N叠加个编码器。同理,解码器也可以有 N N N个叠加在一起。为简化说明,我们设定 N = 2 N=2 N=2。如图1-36所示,一个解码器的输出会被作为输入传入下一个解码器。我们还可以看到,编码器将原句的特征值(编码器的输出)作为输入传给所有解码器,而非只给第一个解码器。因此,一个解码器(第一个除外)将有两个输入:一个是来自前一个解码器的输出,另一个是编码器输出的特征值。

在这里插入图片描述

图1-36 编码器和解码器

接下来,我们学习解码器究竟是如何生成目标句的。当时 t=1(t 表示时间步),解码器的输入是<sos>,这表示句子的开始。解码器收到<sos>作为输入,生成目标句中的第一个词,即Je,如图1-37所示。

在这里插入图片描述

图1-37 解码器在时的预测结果

当时,解码器使用当前的输入和在上一步(t-1)生成的单词,预测句子中的下一个单词。在本例中,解码器将<sos>和Je(来自上一步)作为输入,并试图生成目标句中的下一个单词,如图1-38所示。

在这里插入图片描述

图1-38 解码器在时的预测结果

同理,你可以推断出解码器在 t=3 时的预测结果。此时,解码器将<sos>、Je和vais(来自上一步)作为输入,并试图生成句子中的下一个单词,如图1-39所示。

在这里插入图片描述

图1-39 解码器在时的预测结果

在每一步中,解码器都将上一步新生成的单词与输入的词结合起来,并预测下一个单词。因此,在最后一步(t=4),解码器将<sos>、Je、vais和bien作为输入,并试图生成句子中的下一个单词,如图1-40所示。

在这里插入图片描述

图1-40 解码器在时的预测结果

从图1-40中可以看到,一旦生成表示句子结束的<eos>标记,就意味着解码器已经完成了对目标句的生成工作。

在编码器部分,我们将输入转换为嵌入矩阵,并将位置编码添加到其中,然后将其作为输入送入编码器。同理,我们也不是将输入直接送入解码器,而是将其转换为嵌入矩阵,为其添加位置编码,然后再送入解码器。

如图1-41所示,假设在时间步t=2,我们将输入转换为嵌入(我们称之为嵌入值输出,因为这里计算的是解码器在以前的步骤中生成的词的嵌入),将位置编码加入其中,然后将其送入解码器。

在这里插入图片描述

图1-41 带有位置编码的编码器和解码器

接下来,让我们深入了解解码器的工作原理。一个解码器模块及其所有的组件如图1-42所示。

在这里插入图片描述

图1-42 解码器模块

在这里插入图片描述

从图1-42中可以看到,解码器内部有3个子层。

  • 带掩码的多头注意力层

  • 多头注意力层

  • 前馈网络层

与编码器模块相似,解码器模块也有多头注意力层和前馈网络层,但多了带掩码的多头注意力层。现在,我们对解码器有了基本的认识。接下来,让我们先详细了解解码器的每个组成部分,然后从整体上了解它的工作原理。

1.3.1 带掩码的多头注意力层

以英法翻译任务为例,假设训练数据集样本如图1-43所示。

在这里插入图片描述

图1-43 训练数据集样本

图1-43所示的数据集由两部分组成:原句和目标句。在前面,我们学习了解码器在测试期间是如何在每个步骤中逐字预测目标句的。

在训练期间,由于有正确的目标句,解码器可以直接将整个目标句稍作修改作为输入。解码器将输入的<sos>作为第一个标记,并在每一步将下一个预测词与输入结合起来,以预测目标句,直到遇到<eos>标记为止。因此,我们只需将<sos>标记添加到目标句的开头,再将整体作为输入发送给解码器。

比如要把英语句子I am good转换成法语句子Je vais bien。我们只需在目标句的开头加上<sos>标记,并将<sos>Je vais bien作为输入发送给解码器。解码器将预测输出为Je vais bien<eos>,如图1-44所示。

在这里插入图片描述

图1-44 Transformer的编码器和解码器

为什么我们需要输入整个目标句,让解码器预测位移后的目标句呢?下面来解答。

首先,我们不是将输入直接送入解码器,而是将其转换为嵌入矩阵(输出嵌入矩阵)并添加位置编码,然后再送入解码器。假设添加输出嵌入矩阵和位置编码后得到图1-45所示的矩阵。

在这里插入图片描述

图1-45 嵌入矩阵

然后,将矩阵送入解码器。解码器中的第一层是带掩码的多头注意力层。这与编码器中的多头注意力层的工作原理相似,但有一点不同。

为了运行自注意力机制,我们需要创建三个新矩阵,即查询矩阵Q、键矩阵K 和值矩阵V。由于使用多头注意力层,因此我们创建了 h 个查询矩阵、键矩阵和值矩阵。对于注意力头 i i i 的查询矩阵 Q i Q_i Qi、键矩阵 K i K_i Ki 和值矩阵 V i V_i Vi,可以通过将 X 分别乘以权重矩阵 W i Q 、 W i K 、 W i V W_{i}^{Q} 、W_{i}^{K}、W_{i}^{V} WiQWiKWiV而得。

下面,让我们看看带掩码的多头注意力层是如何工作的。假设传给解码器的输入句是<sos>Je vais bien。我们知道,自注意力机制将一个单词与句子中的所有单词联系起来,从而提取每个词的更多信息。但这里有一个小问题。在测试期间,解码器只将上一步生成的词作为输入。

比如,在测试期间,当 t=2 时,解码器的输入中只有[<sos>, Je],并没有任何其他词。因此,我们也需要以同样的方式来训练模型。模型的注意力机制应该只与该词之前的单词有关,而不是其后的单词。要做到这一点,我们可以掩盖后边所有还没有被模型预测的词。

比如,我们想预测与<sos>相邻的单词。在这种情况下,模型应该只看到<sos>,所以我们应该掩盖<sos>后边的所有词。再比如,我们想预测Je后边的词。在这种情况下,模型应该只看到Je之前的词,所以我们应该掩盖Je后边的所有词。其他行同理,如图1-46所示。

在这里插入图片描述

图1-46 掩码

像这样的掩码有助于自注意力机制只注意模型在测试期间可以使用的词。但我们究竟如何才能实现掩码呢?我们学习过对于一个注意力头 i 的注意力矩阵 Z_i

的计算方法,公式如下。

Z i = s o f t m a x ( Q i ⋅ K i T d k ) ⋅ V i Z_i = softmax(\frac{Q_i \cdot K_{i}^{T}} {\sqrt{d_k} } ) \cdot {V_i} Zi=softmax(dk QiKiT)Vi

计算注意力矩阵的第1步是计算查询矩阵与键矩阵的点积。图1-47显示了点积结果。需要注意的是,这里使用的数值是随机的,只是为了方便理解。

在这里插入图片描述

图1-47 查询矩阵与键矩阵的点积

第2步是将 Q i ⋅ K i T {Q_i \cdot K_{i}^{T}} QiKiT矩阵除以键向量维度的平方根 d k \sqrt{d_k} dk

### Transformer解码器的结构 Transformer解码器Transformer模型中的关键组成部分之一,主要负责将编码器输出的上下文信息转换为目标序列。解码器由多个相同的解码器层堆叠而成,通常包含6个或更多的层[^4]。每个解码器层主要包括三个子层: 1. **掩码多头自注意力机制(Masked Multi-Head Self-Attention)**:这一部分用于处理目标序列的嵌入表示,确保解码器在生成目标序列时只能关注到当前词及其之前的词,避免未来信息泄露。通过使用掩码机制,解码器能够保持序列生成的因果性。 2. **编码器-解码器注意力机制(Encoder-Decoder Attention)**:这一子层用于连接编码器和解码器解码器通过这一机制关注编码器输出的所有位置。这种注意力机制允许解码器在生成目标序列时,动态地选择性关注输入序列中的相关信息。 3. **前馈神经网络(Feed-Forward Neural Network)**:每个解码器层还包含一个全连接的前馈神经网络,用于对每个位置的特征进行非线性变换和组合。该网络由两个线性变换组成,中间有一个ReLU激活函数。 每个子层的输出会经过层归一化(Layer Normalization)和残差连接(Residual Connection),以提高模型的训练稳定性和性能。 ### Transformer解码器的工作原理 解码器的工作原理可以分为以下几个步骤: 1. **输入嵌入与位置编码**:目标序列首先被转换为嵌入向量,并与位置编码相结合。位置编码用于提供序列中每个词的位置信息,帮助模型理解序列的顺序[^3]。 2. **掩码多头自注意力计算**:解码器通过掩码多头自注意力机制,对目标序列的嵌入向量进行处理。掩码机制确保解码器在生成当前词时不会看到未来的信息。 3. **编码器-解码器注意力计算**:解码器利用编码器-解码器注意力机制,从编码器的输出中提取相关信息。这一步骤允许解码器根据当前生成的词动态选择性地关注输入序列中的相关部分。 4. **前馈神经网络处理**:每个位置的特征向量经过前馈神经网络进行非线性变换和组合,进一步提取和增强特征。 5. **残差连接与层归一化**:每个子层的输出会与输入进行残差连接,并通过层归一化操作,以缓解梯度消失问题并加速模型收敛。 6. **重复堆叠解码器层**:上述步骤会在每个解码器层中重复执行,逐步提炼和优化特征表示。 7. **最终输出生成**:经过所有解码器层处理后,解码器的输出将被传递给一个线性变换层和Softmax层,用于生成目标序列的词汇概率分布。最终,解码器会根据这些概率分布生成目标序列的下一个词。 ### 示例代码 以下是一个简单的Python代码示例,展示了如何实现一个基本的解码器类: ```python import torch import torch.nn as nn class DecoderLayer(nn.Module): def __init__(self, d_model, num_heads, d_ff, dropout=0.1): super(DecoderLayer, self).__init__() self.self_attn = nn.MultiheadAttention(d_model, num_heads, dropout=dropout) self.encoder_attn = nn.MultiheadAttention(d_model, num_heads, dropout=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) self.norm2 = nn.LayerNorm(d_model) self.norm3 = nn.LayerNorm(d_model) self.dropout = nn.Dropout(dropout) def forward(self, x, memory, src_mask=None, tgt_mask=None): # 掩码多头自注意力 x2 = self.norm1(x) x2, _ = self.self_attn(x2, x2, x2, attn_mask=tgt_mask) x = x + self.dropout(x2) # 编码器-解码器注意力 x2 = self.norm2(x) x2, _ = self.encoder_attn(x2, memory, memory, attn_mask=src_mask) x = x + self.dropout(x2) # 前馈神经网络 x2 = self.norm3(x) x2 = self.feed_forward(x2) x = x + self.dropout(x2) return x class Decoder(nn.Module): def __init__(self, layer, N): super(Decoder, self).__init__() self.layers = nn.ModuleList([layer for _ in range(N)]) self.norm = nn.LayerNorm(layer.feed_forward[2].out_features) def forward(self, x, memory, src_mask, tgt_mask): for layer in self.layers: x = layer(x, memory, src_mask, tgt_mask) return self.norm(x) ``` ### 相关问题 1. Transformer解码器中的掩码多头自注意力机制是如何工作的? 2. 如何实现Transformer解码器中的编码器-解码器注意力机制? 3. 解码器中的前馈神经网络在模型中起到什么作用? 4. 为什么在Transformer解码器中需要使用位置编码? 5. 解码器如何通过残差连接和层归一化提高模型性能?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值