Seq2seq,顾名思义,一个序列到另一个序列的转换。它是一种Encoder-Decoder协同训练的端到端网络。传统Seq2seq的Encoder以RNN为基础单元,把输入序列整合为一个content向量;Decoder同样以RNN为基础单元,把content向量拆解为输出序列。显然,content向量决定了Decoder可知的信息量,进而决定了输出序列的质量。由于RNN的时序串行结构,Encoder更关注位于输入序列末尾的元素,使得content向量“遗忘”了位于输入序列前端的元素。反之,如果把RNN的所有隐层输出一同送入Decoder,又显得过于臃肿(计算量大)。如今我们常把Seq2seq和Attention联系在一起,也正是因为Attention帮助Seq2seq解决了上述问题。本文将对此进行讲解。
一、从传统Seq2Seq说起
Seq2seq机器翻译开篇之作 Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation[1] 发表前,机器翻译研究以统计方法为主。这篇文章仿照人在翻译时的思维方式"通读原句子(encode),大脑加工整合(content), 复述新句子(decode)",提出了一种Encoder-Decoder结构,如下图所示。简单来讲,先把任意长度的文本序列编码(encode)为固定长度的向量表示(context),再把固定长度的向量表示解码(decode)回任意长度的文本序列。
Seq2seq结构灵活。我们看到上图中3个content向量(C1==C2==C3)被用作Decoder各个时刻的输入,这里可以稍作改动,如下图,content向量只作为Decoder第一时刻的输入,再随Decoder的RNN依次向后传递。另一个改动的是,content向量取自Encoder各个时刻隐藏单元的拼接(或加和),或者像下图中(参考文献[1]和[2]),只取用Encoder最后一个时刻的隐藏单元。
依照Seq2seq展开图(上图),Decoder各个时刻的输入是:
- 初始时刻:
<EOS>
标记的词向量,以及content向量; - 其他时刻:Decoder前一时刻的输出单词的词向量,以及Decoder前一时刻的隐层状态。
注:<EOS>
标记被用作通知解码器开始生成文本的信号。也是结束生成文本的信号。
注:反向传播时,Decoder的梯度通过content向量传递给Encoder。
注:反转输入数据的顺序[2]是一种Seq2Seq优化方法,效果提升因具体任务而异,优点是梯度的传播更平滑,学习效率更高。例如“我是猫”和“I am a cat”,单词“我”和“I”有转换关系,反向传播时,梯度需从“I”经过“猫、是”才到达“我”;而反转句子后,“我”和“I”相邻,梯度可以直接传递。
二、在Seq2Seq中引入Attention
Decoder可获得的全部信息存储在这一个小小的content向量中,那么如何增加content向量的信息量呢?
Attention可以在每个时间步关注到Encoder的不同部分,以收集产生Decoder输出词所需的语义细节。Attention权重模拟了人脑注意力的分配机制,为更重要的部分分配较多的注意力。
具体的计算过程是,Seq2seq每解码一个字,就用前一个时刻的Decoder隐层状态,和Encoder所有隐层状态做soft Attention,计算公式如下,得到当前输出和所有输入元素的关联程度,即权重。进而采用加权平均的方式,计算出对当前输出更友好的context向量表示。
这里涉及到三个重要的细节(结合下图进行理解):
-
加入Attention后,content向量在任意时刻是变化的。那么如何计算第 i 时刻content向量?
- 先计算Decoder的RNN隐层状态 H i − 1 H_{i-1} Hi−1 和Encoder中所有RNN隐层状态 h 1 , h 2 , . . . , h j {h_1, h_2, ..., h_j} h1,h2,...,h