正如我们在9.5节中看到的,机器翻译中的输入序列和输出序列都是长度可变的,为了解决这类问题,我们在9.6节中设计了一个通用的编码器-解码器架构,在本节中,我们将使用两个循环神经完了的编码器和解码器,并将其应用于序列到序列类的学习任务。
遵循编码器-解码器架构的设计原则,循环神经网络编码器使用长度可变的序列作为输入,将其转换为固定形状的隐状态。换言之,输入序列的信息被编码到循环神经网络编码器的隐状态中,为了连续生成输出序列的词元,独立的循环神经网络解码器是基于输入序列的编码信息和输出序列可见的或者生成的词元来预测下一个词元,图9-12展示了如何在机器翻译中使用两个循环神经网络进行序列到序列学习。
编码器
They are watching eos
解码器
bos lts regardent
图9-12 使用循环神经网络编码器和循环神经网络解码器的序列到序列学习
特定的eos 表示序列结束词元,一旦输出序列生成此词元,模型就会停止预测,在循环神经网络解码器的初始化时间步,有两个特定的设计:首先,特定的表示序列开始词元,是解码器的输入序列的第一个词元,其次,使用循环神经网络编码器最终的隐状态来初始化解码器隐状态。例如,在参考文献的设计中,基于这种设计将输入序列的编码信息送入输入解码器中来生成输出序列的。在其他一些设计中,如图9-12所示,编码器最终的隐状态在每个时间步都作为解码器的输入序列的一部分。类似于8.3节中语言模型的训练,可以允许标签作为原始的输出序列,从源序列词元,到新序列词元来移动预测的位置。
我们动手构建图9-12的设计,并将基于9.5节中介绍的英语-法语数据集来训练这个机器翻译模型。
import collections
import math
import torch
from torch import nn
from d2l import torch as d2l
9.7.1 编码器
编码器将长度可变的输入序列转换成形状固定的上下文变量,并且将输入序列的信息在该上下文变量中进行编码。如图9-12所示,可以使用循环神经网络来设计编码器。
考虑由一个序列组成的样本,假设输入序列是x1,,,,xt,其中Xt时输入文本序列中的第i个词元,在时间步t,循环神经网络将词元xt的输入特征向量Xt和Ht-1 转换为ht。使用一个函数f来描述循环神经网络的循环层所做的转换。
Ht = f(Xt,Ht-1)
编码器通过选定的函数q, 将所有时间步的隐状态转换为上下文变量
c = q(h1,,ht)
当选择q(h1,,,ht)时,上下文变量仅仅是输入序列在最后时间步的隐状态Ht
到目前为止,我们使用的是一个单向循环神经网络设计编码器,其中隐状态只依赖输入子序列,这个子序列为输入序列的开始位置到隐状态所在的时间步设置,我们也可以使用双向循环神经网络构建编码器,其中隐状态依赖两个输入子徐磊,这两个字序列分别是隐状态所在的时间步的位置之前的序列和之后的序列,因此隐状态对整个序列的信息都进行了编码。
我们实现循环神经网络编码器,注意,我们使用了嵌入层来获得输入序列中每个词元的特征向量,嵌入层的权重是一个矩阵,其行数等于输入词表大小,其列数等于特征向量的维度,对于任意输入词元的索引 嵌入层获取权重矩阵的第i行1️⃣返回其特征向量,这里选择了一个多层们控循环单元来实现编码器。
class Seq2seqEncdoer(d2l.Encoder):
用于序列到序列学习的循环神经网络编码器
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers, dropout=0, **kwargs):
super(Seq2seqEncoder, self).__init__(**kwargs)
嵌入层
self.embedding = nn.Embedding(vocab_size, embed_size)
slef.rnn = nn.GRU(embed_size, num_hiddens, num_layers, dropout=dropout)
def forward(self, X, *args):
输入X的形状为(batch_size, num_steps, embed_size)
X = self.embedding(X)
在循环神经网络模型中,第一个轴对应于时间步
X =X.permute(1, 0, 2)
如果未提及状态,则默认为0
output, state = self.rnn(X)
output的形状为(num_steps, batch_size, num_hiddens)
#state[0]的形状为(num_layers, batch_szie, num_hiddens)
return output, state
循环层返回变量的说明参见8.6节
我们实例化上述编码器的实现,使用一个两层们控循环单元编码器,隐藏单元数为16,给定一个小批量的输入序列X,在完成所有时间步后,最后一层的隐状态的输出是一个张量,其形状为 时间步数,批量大小,隐藏单元数。
encoder = Seq2SeqEncoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers = 2)
encoder.eval()
X=torch.zeros(4, 7), dtype = torch.long
output, state = encoder(X)
output.shape
torch.Size([7, 4, 16])
这里使用的是门控循环单元,最后一个时间步的多层隐状态的形状是,如果使用长短期记忆网络,state中还将包含记忆元信息。
state.shape
torch.Size([2, 4, 16])
9.7.2 解码器
编码器输出的上下文变量c对整个输入序列x1,,,Xt进行编码,来自训练数据的输出序列为Y1,Y2,,,Yt,对于每个时间步t 解码器输出Yt的概率取决于之前的输出子序列Y1,,,,Yt和上下文变量c,即P(Y1|Y2,,,Yt-1, x)
在序列上模型化这种条件概率,我们可以使用另一个循环神经网络作为解码器,在输出序列上的任意时间步t,循环神经网络将来自上一个时间步的输出Yt-1和上下文变量c作为其输入,然后在当前时间步将它们和上一个隐状态St-1转换为隐状态St,可以使用函数g来表示解码器的隐藏层的变换。
St = g(Yt-1,c,St-1)
在获得将其的隐状态之后,可以使用输出层和softmax操作来计算在时间步t时输出yt的条件概率分布P(Yt|Y1,,,Yt-1, c)
根据图9-12 ,当实现解码器时,直接使用编码器最后一个时间步的隐状态来初始化解码器的隐状态,这就要求使用循环神经网络实现的编码器和解码器具有相同数量的层和隐藏单元,为了进一步包含经过编码的输入序列的信息,上下文变量在所有的时间步于解码器的输入进行连接。为了预测输出词元的概率分布,在循环神经网络解码器的最后一层使用全连接层来变换隐状态。
class Seq2SeqDecoder(d2l.Decoder):
用于序列到序列学习的循环神经网络解码器
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers, dropout=0, **kwargs):
super(Seq2SeqDecoder, self).__init__(**kwargs)
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = nn.GRU(embed_size + num_hiddens, num_hiddens, num_layers, dropout=dropout)
self.dense = nn.Linear(num_hiddens, vocab_size)
def init_state(self, enc_outputs, *args):
return enc_outputs[1]
def forward(self, X, state):
输出X的形状为(batch_size, num_steps, embed_size)
X = self.embedding(X).permute(1, 0, 2)
广播context, 使其具有于X相同的num_steps
context = state[-1].repeat(X.shape[0], 1, 1)
X_and_context = torch.cat((X, context), 2)
output, state = self.rnn(X_and_context, state)
output = self.dense(output).permute(1,0,2)
output的形状为(batch_size, num_steps, vocab_size)
state[0]的形状为num_layers, batch_size, num_hiddens
return output, state
下面我们用与前面提到的编码器中相同的超参数来实例化解码器,解码器的输出形状变为批量大小,时间步数,词表大小,其中张量的最后一个维度存储预测的词元分布。
decoder = Seq2SeqDecoder(vocab_size=10, embed_size=8, num_hiddens=16,num_layers=2)
decoder.eval()
state = decoder.init_state(encoder(X))
output, state = decoder(X, state)
output.shape, state.shape
torch.Size([4,7,10]), torch.Size([2,4,16])
上述循环神经网络编码器,解码器模型中的各层如图9-13所示
编码器
循环层
嵌入层
源
解码器
全连接层
循环层
嵌入层
目标
图9-13 循环神经网络编码器-解码器模型中的各层
序列到序列学习详解
1128

被折叠的 条评论
为什么被折叠?



