Seq2Seq模型学习(pytorch)

在看pytorch的官方英文例子,做些笔记,如有纰漏请指正,原文:https://pytorch.org/tutorials/beginner/chatbot_tutorial.html

数据准备

  • 首先是单词编码。seq2seq的单词编码的方式可以参见seq2seq翻译教程,这篇文章采用了对单词建立索引,每个单词用一个index表示的方法。
  • 正常情况的句子矩阵是(句子长度,句子数量)的矩阵。为了批量训练,需要对其进行转置,每次读取各个句子一个时间步长的单词,如下图batches
  • 输入的内容包括以下几个部分:
  1. input_variable:转化为tensor的句子编码矩阵,形式为:
    tensor([[  34,   16,  147, 4556,   50],
            [   4, 5553,  379, 2528,    6],
            [  76, 3810,   45,    6,    2],
            [ 331,    4,  380,    2,    0],
            [ 117,    4, 1967,    0,    0],
            [  47,    4,    6,    0,    0],
            [   4,    2,    2,    0,    0],
            [   2,    0,    0,    0,    0]])
  2. lengths,每个句子的长度,可以理解为上面矩阵的每个纵向的没用0填充之前的长度,形式为
    tensor([8, 7, 7, 4, 3])
  3. target_variable:目标结果,即希望得到的输出
    tensor([[  50,   16,   25,   27, 1582],
            [ 331,  997,  197,  153,    4],
            [ 117,    4,  117,  112,    2],
            [  47,   50,   24, 3484,    0],
            [   6,  368,    4,  587,    0],
            [   2,    6,    2,    4,    0],
            [   0,    2,    0,    2,    0]])
  4. mask:即pad_token
    tensor([[1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1],
            [1, 1, 1, 1, 0],
            [1, 1, 1, 1, 0],
            [1, 1, 1, 1, 0],
            [0, 1, 0, 1, 0]], dtype=torch.uint8)

Seq2Seq模型原理

Seq2Seq意如其名,是使用固定大小的模型实现一个变长序列到变长序列的模型。输入一个变长的句子,返回一个变长的句子,故可以用来执行翻译任务和起标题任务,人机对话等。是rnn的衍生模型。

具体模型使用两个rnn模型来实现此功能。一个RNN充当编码器,他将输入的变长序列编码成固定长度的上下文向量,理论上,这个上下文向量,也就是RNN最终的隐藏层包含了输入的语义信息,(类似于CNN卷积后的中间层包含了图片的边缘、色彩等信息一样)。第二个RNN充当解码器,他接受输入和上下文向量,并返回对下一个单词的猜测和下一次迭代中使用的隐藏状态。model

编码器

编码器每次迭代接受输入语句中的一个标记(一个单词),并产生一个输出向量和一个隐藏状态向量。这个中间隐藏状态向量接着被送向下一个迭代中,而输出向量被记录。编码器将他在句子中每个点处看到的上下文转化成高维空间中的一组点,以便后面解码器用它来产生有意义的输出。

这里用的编码器的核心是有Cho等人在2014年发表的多层门控循环单元。这里使用GRU的双向变体,以为着这里有两个独立的RNN,一个以正常顺序输入序列,另一个以相反顺序输入序列。每一个迭代中输出将会求和,使用双向GRU将提供之前和之后的上下文的优势。

双向RNN原理如图:rnn_bidir

  • embedding层将把单词编码到任意大小的特征空间,对于我们的模型,该层将映射每个单词到大小为hidden_size的特征空间,当训练时,意义相同的单词在特征空间里的值也应当相近。
  • 填充序列时用了nn.utils.rnn.pack_padded_sequence,解包时需要用nn.utils.rnn.pad_packed_sequence

 模型流程:

  1. 将单词索引转化为embeddings
  2. 为RNN模块打包填充序列
  3. 正向通过GRU
  4. 解填充
  5. 对双向GRU输出进行求和
  6. 返回输出和最终的隐藏层状态

输入

  1. input_seq:一批输入的句子,矩阵大小为(最大句子长度,句子数量)
  2. input_lengths:每个句子对应的长度矩阵
  3. hidden:隐藏层的状态,大小为(n层,维数,数量,hidden_size)

输出

  1. GRU最后一个隐藏层的输入特征(双向输出之和),大小为(max_length,batch_size,hidden_size)
  2. GRU的隐藏层更新状态,大小为(n_layers x num_directions,batch_size,hidden_size)

 

解码器

解码器RNN以token-by-token的形式生成回应。他使用编码器生成的上下文向量,和内部的隐藏层状态来生成序列中的下一个单词。他持续生成单词知道他输出了 EOS_token,即一个句子的末尾。一个常见的Seq2Seq问题是如我我们仅仅依赖于上下文向量来编码整个输入序列的含义,很可能会丢失信息,特别是在处理长输入序列的时候,这将极大限制解码器的能力。

为了克服这种现象,Bahdanau等人提出了注意机制来允许解码器来关注输入句子的一部分,而不是使用整个固定的上下文。

在更高的级别里,计算注意力是靠着解码器当前隐藏层的状态和编码器的输入。输出注意力的权重与输入句子有着相同形式的矩阵大小,以便允许将其和编码器的输入相乘,给出一个加权和表示要注意的编码器输出部分,下图很好的描述了这一点:

attn2

之后有一种改进的方法称为global attention,关键的变化是通过global attention,我们可以关注编码器的全部隐藏层状态,而不是之前提出的当前迭代中的局部隐藏层状态。另一个变化是计算注意力权重只使用当前迭代的隐藏层状态,而上一种还需要知道上一个迭代中解码器的状态,而且给出了各种方法来计算attention energies,被称为score functions

scores

ht代表当前目标解码器状态,hs代表所有解码器的状态

global attention机制可以通过下图总结,在pytorch中注意层实现为一个名为Attn的独立nn.Module,输出的shape为(batch_size,1,max_length).

global_attn

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值