推荐文章Neural Machine Translation by Jointly Learning to Align and Translate,里面还大概阐述了encoder-decoder(编码)模型的概念,以及传统的RNN实现。然后还阐述了自己的attention模型。
文章地址
Neural Machine Translation by Jointly Learning to Align and Translate
这是 2015 年发表在 ICLR 上的论文,也是 NLP 中 Attention 机制的开山之作,Attention 机制是为了解决一般的 RNN Encoder-Decoder 对长句子表现不佳的问题而设计的。从论文题目中我们可以看到,作者希望通过 Attention 机制将输入句子 input 和输出句子 output 进行 “对齐”(SMT 中也有所谓的词对齐模型)。但是,由于不同语言的句法语法结构千差万别,想将源句子与翻译句子严格的对齐是很困难的,所以这里的对齐实际上是软对齐(soft-alignment),也就是不必将源句子显式分割,因而又被形象地称为注意力机制(Attention Mechanism)
近年来,基于神经网络的机器翻译模型经常被用来处理机器翻译任务。与传统基于统计的翻译方法相比,审计网络的机器翻译模型意在构建单个神经网络模型来提升整体翻译的准确率,主要的模型架构基本都是 Seq2Seq 家族的。在本论文中,作者认为该模型的瓶颈主要在于中间转换的向量是固定维度的。因此,作者提出了一种新的解码方式,其解码的源头不仅仅包括该向量,它们希望构建一种为当前预测词从输入序列中自动搜寻相关部分的机制(soft-search,也就是注意力机制)。作者运用这种新的机制来搭建升级版的神经翻译模型,取得了卓越的效果,并且也通过定量分析来证明了这种注意力机制的合理性
注意力机制解决的问题
传统的 Encoder-Decoder 结构对于长句子和生词过多的句子效果很不理想,因为随着序列的增长,句子越前面的词信息就会丢失的越厉害,虽然也有很多论文提出了一些 trick,比如将句子倒序输入,或者重复输入两遍,或者使用 LSTM 模型。但这些都治标不治本,对模型性能的提升并不明显,因为在解码时,当前预测词对应的输入词的上下文信息、位置信息等基本都已丢失
并且,性能的瓶颈在于 Encoder 强制将句子中所有信息都压缩在一个定长的向量中, 而当句子长度过大时,定长的向量就难以将所有的信息都编码进向量中,因此造成了性能下降。由此,作者提出,在翻译目标词的每一步,应该让 Decoder 自动抽取源句子中那些与目标词信息相关的部分,而忽略不相关的部分,这些部分的信息构成一个上下文向量 (context word) ,取代传统 Encoder 中的语义表示向量 。也就是说,Encoder 将句子编码成一个向量序列,而不是一个向量,然后再预测翻译单词的每一步选择这些向量的子集作为注意力向量输入到 Decoder 中。这是符合我们直觉的,因为人类在翻译句子时,不会每时每刻都考虑整个句子的含义,而是在翻译特定片段时,重点注意这个片段附近的上下文,而不会注意离我们正在翻译的片段较远的那些片段。
Seq2seq
简单的说,就是根据一个输入序列x,来生成另一个输出序列y。seq2seq有很多的应用,例如翻译,文档摘取,问答系统等等。在翻译中,输入序列是待翻译的文本,输出序列是翻译后的文本;在问答系统中,输入序列是提出的问题,而输出序列是答案。
为了解决seq2seq问题,有人提出了encoder-decoder模型,也就是编码-解码模型。所谓编码,就是将输入序列转化成一个固定长度的向量;解码,就是将之前生成的固定向量再转化成输出序列。
也就是编码-解码模型。所谓编码,就是将输入序列转化成一个固定长度的向量;解码,就是将之前生成的固定向量再转化成输出序列。
具体实现的时候,编码器和解码器都不是固定的,可选的有CNN/RNN/BiRNN/GRU/LSTM等等,你可以自由组合。比如说,你在编码时使用BiRNN,解码时使用RNN,或者在编码时使用RNN,解码时使用LSTM等等。
对于输入序列x=(x1,…,xT),其会将输入序列如图所示编码成一个context vector c ,encoder一般使用RNN,在RNN中,当前时间的隐藏状态是由上一时间的状态和当前时间输入决定的,也就是
获得了各个时间段的隐藏层以后,再将隐藏层的信息汇总,生成最后的语义向量 c,相当于把整个句子的信息都包含了,可以看成整个句子的一个语义表示。
其中的f和q是非线性的函数
例如,在论文中有使用
来简化计算
一般其作用为在给定context vector c和所有已预测的词 {y1,…,yt−1}去预测 yt,故t时刻翻译的结果y为以下的联合概率分布.
在RNN中(如上图所示),t时刻隐藏状态st为:
而联合条件分布为:
其中s是输出RNN中的隐藏层,C代表之前提过的语义向量,yt−1表示上个时间段的输出,反过来作为这个时间段的输入。而q则可以是一个非线性的多层的神经网络,产生词典中各个词语属于yt的概率。
encoder-decoder模型虽然非常经典,但是局限性也非常大。最大的局限性就在于编码和解码之间的唯一联系就是一个固定长度的语义向量C。也就是说,编码器要将整个序列的信息压缩进一个固定长度的向量中去。但是这样做有两个弊端,一是语义向量无法完全表示整个序列的信息,还有就是先输入的内容携带的信息会被后输入的信息稀释掉,或者说,被覆盖了。输入序列越长,这个现象就越严重。这就使得在解码的时候一开始就没有获得输入序列足够的信息, 那么解码的准确度自然也就要打个折扣了
为了解决这个问题,作者提出了Attention模型,或者说注意力模型。简单的说,这种模型在产生输出的时候,还会产生一个“注意力范围”表示接下来输出的时候要重点关注输入序列中的哪些部分,然后根据关注的区域来产生下一个输出,如此往复。
相比于之前的encoder-decoder模型,attention模型最大的区别就在于它不在要求编码器将所有输入信息都编码进一个固定长度的向量之中。相反,此时编码器需要将输入编码成一个向量的序列,而在解码的时候,每一步都会选择性的从向量序列中挑选一个子集进行进一步处理。这样,在产生每一个输出的时候,都能够做到充分利用输入序列携带的信息。而且这种方法在翻译任务中取得了非常不错的成果。
attention是一种能让模型对重要信息重点关注并充分学习吸收的技术,它不算是一个完整的模型,应当是一种技术,能够作用于任何序列模型中。简单回顾一下Seq2Seq模型,传统的机器翻译基本都是基于Seq2Seq模型来做的,该模型分为encoder层与decoder层,并均为RNN或RNN的变体构成,如下图所示:
在encode阶段,第一个节点输入一个词,之后的节点输入的是下一个词与前一个节点的hidden state,最终encoder会输出一个context,这个context又作为decoder的输入,每经过一个decoder的节点就输出一个翻译后的词,并把decoder的hidden state作为下一层的输入。该模型对于短文本的翻译来说效果很好,但是其也存在一定的缺点,如果文本稍长一些,就很容易丢失文本的一些信息,为了解决这个问题,Attention应运而生。
Attention,正如其名,注意力,该模型在decode阶段,会选择最适合当前节点的context作为输入。Attention与传统的Seq2Seq模型主要有以下两点不同。
1)encoder提供了更多的数据给到decoder,encoder会把所有的节点的hidden state提供给decoder,而不仅仅只是encoder最后一个节点的hidden state。
2)decoder并不是直接把所有encoder提供的hidden state作为输入,而是采取一种选择机制,把最符合当前位置的hidden state选出来,具体的步骤如下
这里我们以一个具体的例子来看下其中的详细计算步骤:
把每一个encoder节点的hidden states的值与decoder当前节点的上一个节点的hidden state相乘,如上图,h1、h2、h3分别与当前节点的上一节点的hidden state进行相乘(如果是第一个decoder节点,需要随机初始化一个hidden state),最后会获得三个值,这三个值就是上文提到的hidden state的分数,注意,这个数值对于每一个encoder的节点来说是不一样的,把该分数值进行softmax计算,计算之后的值就是每一个encoder节点的hidden states对于当前节点的权重,把权重与原hidden states相乘并相加,得到的结果即是当前节点的hidden state。可以发现,其实Atttention的关键就是计算这个分值。
明白每一个节点是怎么获取hidden state之后,接下来就是decoder层的工作原理了,其具体过程如下:
第一个decoder的节点初始化一个向量,并计算当前节点的hidden state,把该hidden state作为第一个节点的输入,经过RNN节点后得到一个新的hidden state与输出值。注意,这里和Seq2Seq有一个很大的区别,Seq2Seq是直接把输出值作为当前节点的输出,但是Attention会把该值与hidden state做一个连接,并把连接好的值作为context,并送入一个前馈神经网络,最终当前节点的输出内容由该网络决定,重复以上步骤,直到所有decoder的节点都输出相应内容。
Attention模型并不只是盲目地将输出的第一个单词与输入的第一个词对齐。实际上,它在训练阶段学习了如何在该语言对中对齐单词(示例中是法语和英语)。Attention函数的本质可以被描述为一个查询(query)到一系列(键key-值value)对的映射。
在计算attention时主要分为三步,第一步是将query和每个key进行相似度计算得到权重,常用的相似度函数有点积,拼接,感知机等;然后第二步一般是使用一个softmax函数对这些权重进行归一化;最后将权重和相应的键值value进行加权求和得到最后的attention。目前在NLP研究中,key和value常常都是同一个,即key=value。
我们先来看看解码。解码部分使用了attention模型。类似的,我们可以将之前定义的条件概率写作。
上式si表示解码器i时刻的隐藏状态。计算公式是:
注意这里的条件概率与每个目标输出yi相对应的内容向量ci有关。而在传统的方式中,只有一个内容向量C。那么这里的内容向量ci又该怎么算呢?其实ci是由编码时的隐藏向量序列(h1,…,hTx)按权重相加得到的。
由于编码使用了双向RNN,因此可以认为hi中包含了输入序列中第i个词以及前后一些词的信息。将隐藏向量序列按权重相加,表示在生成第j个输出的时候的注意力分配是不同的。αij的值越高,表示第i个输出在第j个输入上分配的注意力越多,在生成第i个输出的时候受第j个输入的影响也就越大。那么现在我们又有新问题了,αij又是怎么得到的呢?这个其实是由第i-1个输出隐藏状态si−1和输入中各个隐藏状态共同决定的。也即是:
注:
相比于上面解码的创新,这边的编码就比较普通了,只是传统的单向的RNN中,数据是按顺序输入的,因此第j个隐藏状态h→j只能携带第j个单词本身以及之前的一些信息;而如果逆序输入,则h←j包含第j个单词及之后的一些信息。如果把这两个结合起来,hj=[h→j,h←j]就包含了第j个输入和前后的信息。
参考: