目录
前言
这篇文章是在边学习李沐老师的《动手学深度学习》,边写出一些小的见解。
提示:以下是本篇文章正文内容,下面案例可供参考
一、序列模型是什么?
序列模型(sequence to sequence model)主要用于解决像机器学习中的源语句和目标语句的词数目不匹配的问题,一般使用一种编码器到解码器的架构。
二、编码器
1.编码器原理
编码器是将可变长度的输入序列转换成固定长度的隐状态。公式如下:
h_t = f(x_t , h_t-1)
h_t是当前隐状态,f是指rnn层所做的变换,x_t和h_t-1为输入。
编码器做的是用一个函数q将所有隐状态转化为上下文变量c。当c = h_T时,T为时间步长度,代表仅仅输入序列在最后时间步的隐状态。
2.编码器的实现
代码如下(来自李沐《动手学深度学习》):
class Seq2SeqEncoder(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)
self.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的形状:(num_layers,batch_size,num_hiddens)
return output, state
首先需要说的是
self.rnn = nn.GRU(embed_size, num_hiddens, num_layers,dropout=dropout)
embed_size是每个词元的特征向量,num_hiddens是隐藏层大小,num_layers隐藏层层数。
然后是X = X.permute(1, 0, 2),这里巧妙地将X这个3维张量的第1维和第2维互换位置,目的是为了后面输入到RNN模型。
三、解码器
1.解码器原理
y_t-1为上一时间步的输出,c是上下文变量,s_t-1为上一个时间步的隐状态。s_t为当前隐状态。这是解码的隐状态更新公式。
当获取到解码器的隐状态后,我们可以直接用输出层和softmax来计算时间步t时的输出y_t的条件概率分布。
2.解码器的实现
在实现解码器时,我们需要用到编码器的最后一个隐状态来初始化解码器的隐状态。
代码如下(来自李沐《动手学深度学习》):
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] #uk
def forward(self, X, state):
# 输出'X'的形状:(batch_size,num_steps,embed_size)
X = self.embedding(X).permute(1, 0, 2)
# 广播context,使其具有与X相同的num_steps,拿state的最后一个时间步状态
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的形状:(num_layers,batch_size,num_hiddens)
return output, state
forward函数中的context = state[-1].repeat(X.shape[0],1,1),此句将最后一个时间步的state扩到于X相同的时间步长度,由此得到上下文变量c。补充一下pytorch的知识:
torch.reapeat(1,1,1)
当参数有三个时:(通道数的重复倍数,列的重复倍数,行的重复倍数)。
X_and_context就是将输入X与context拼接起来,
其中torch.cat((X,context),2),代表在张量的第3维(隐藏单元维度)将两个张量拼接起来。
将X_and_context和隐状态输入到RNNmodel中,最后将output的第1维和第2维换回来。
总结
以上就是今天要讲的内容,本文仅仅简单介绍了序列模型所解决的实际问题,编码器到解码器框架,以及编码器和解码器的实现。