之前总结过RNNLM,是一个SequenceModel,其结构类似如下:

这里面是一个一个的输出。我们如果以这种方式做机器翻译,每一个时刻输入一个词,相应的翻译一个词,显然这种一个一个单词的翻译方式不是很好,因为没有联系上下文进行翻译。我们希望先把一整句话喂给模型,然后模型在这一个整句的角度上来进翻译。这样翻译的效果更好。
所以本篇博文要总结的是Seq2Seq Model,给出一个完整的句子,能得出另外一个完整的句子。
下面我们以机器翻译来讲解下面几个要点。
Encoder-Decoder模型
网络结构

其中f1,f2,f3 是输入信息做完 embedding 后的矩阵,Encoder部分是一个两层的LSTM 神经网络,这个神经网络不做任何输出,只输出最后一步的h,c,我们可以理解这个h,c 是已经总结了的输入信息,Decoder部分也是一个两层的LSTM 神经网络,并且其隐藏层h,c 的初始值为Encoder 部分输出的h,c,然后在Decoder 部分进行翻译。
注意在Encoder 部分每一步并不预测任何东西,其初始的h,c 为全零向量,并且与Decoder 是完全不同的参数。
Encoder−Decoder 的参数集合
首先注意Encoder 和Decoder 部分都是两层的LSTM 神经网络。回顾下LSTM Cell 大致结构:

以及它的计算公式:

Encoder−Decoder 超参数
-
num_layers=n,hidden size=d(embedding的维度)
-
vocab for F=VF(输入单词的个数),vocab for E=VE(输出单词的个数)
Encoder 部分参数
-
Input:input embedding for f:VF∗d
-
LSTM: 第一层,第二层: 2∗(8d2+4d)(我们可以看上面LSTM 的计算公式,对于i,f,o,g 四个公式,每个公式都有两个参数矩阵,每个矩阵大小都是d∗d,再加上四个bias 参数矩阵,故每层共有(8d2+4d) 个参数)
Decoder 部分参数
-
Input:input embedding for e:VE∗d
-
LSTM: 第一层,第二层: 2∗(8d2+4d)
-
Output
output embedding for e:VE∗d
output bias for e:VE
Beam Search
Mismatch between Train and Test
首先需要注意到模型训练和模型预测是两个不同的过程,在训练时,我们知道每一步真正的reference,而在预测时是不知道每一步的reference 的。

在上图的网络结构中,都是以上一时刻真正的reference 作为下一时刻的input 来训练模型。
那么train 出这样一个模型,应该如何进行预测呢?因为在预测阶段我们是不知道reference 的,我们可以尝试这样做,把上一次的输出作为下一次的输入。

很显然,这样做的后果很严重:

一步错,步步错!
那么应如何解决上面这个问题呢?我们尝试这样做,现在假设语料库只有A,B 两个word,那么:

我们看上图的LSTM 结构,共有三个时间段,第一个时间段会输出两个单词P(A),P(B) 的概率,并不真正的输出最大概率对应的word 作为当时刻的输出,分别以[A,B]T 作为下一个时刻的输入,然后得到这一时刻输出P(A),P(B),P(A),P(B) 的概率矩阵,以此类推,直到最后我们可以得到输出是AAA,AAB,ABA,ABB,.....各个序列的概率,选择概率最大的作为真正的输出序列。
这里需要注意,在Decoder 部分,第三个时间步处两个输入的B 表示不同的含义,第一个B 的前驱为A,第二个B 的前驱为B。
这样我们可以计算出输出序列P(AAA)=0.6∗0.4∗0.5=0.12,P(AAB)=0.6∗0.4∗0.5=0.12,P(ABA)=0.6∗0.6∗0.4=0.144.... 如此类推计算,可以计算出最大概率对应的序列,作为预测结果。
在语料库中的words 很少的情况下,可以利用这样类似于穷举的方式来获得概率最大的那个序列作为预测结果,但是如果语料库中的words 很多时,这种穷举的方式肯定就变得不可行了,那么这个时候应该如何做呢?
可以尝试这样做,例如语料库有3个words,我们可以设置Beam size=2,也就是每次选择前一时刻输出概率最大的前2个words 作为当前的输入。


这里需要注意,当对应输入是a,b 时,输出最大概率的两个word 为b,c,并且其前驱都是a,那么此时以b 为前驱的就丢掉了。

当语料库中只有两个words 时,取Beam size=2时,其过程如下:

可以以下面这张图更好的理解Beam Search 过程:

注意在每个时间步时,可能有相同的word 作为输入,但是他们的意义是不同的,其前驱不一样。
Attention
上面讲的传统的Encoder−Decoder 神经网络结构在应对较短文本翻译时效果不错,但是随着文本长度的增加,其翻译效果会迅速的恶化。由此提出了Attention 这种结构,使得模型能够学习如何对input 和output 进行对齐。
简单来说,例如将“我 爱 你”翻译成 "i love you",这里模型如何学习到如何将翻译出的"i" 对齐到(attention) 到”我“。
例如下图:

那么问题来了,如何让模型学习对其(Attention) 呢?
在attention 的原始论文中是这样说的:

这里我久不从数学公式角度来说明,只说下它的大致思路。
上图中上半部分为Decoder ,其中s 为其hidden−states 输出信息,y 为其 output 。下半部分为Encoder ,X 为其 Input,h 为hidden−states 。
假设在 t 时刻,其Decoder 部分对应的hidden−states 为 St ,这个时候,我们把St 与Encoder 部分的所有hidden−state 信息做个相似度的计算,得出at,1,at,2,at,3,...,然后再把这些计算出来的相似度做个softmax,再进行如下计算:

将得出的cj 作为Decoder 部分的输入。
这样讲的估计有许多人没明白咋回事,为什么这样做就能Attention 呢?
我觉得这篇原始论文讲的虽然详细但是不够直观。我引用下台大李宏毅教授所讲attention 的ppt 来详细进行讲解。

假设我们再Encoder 部分输入“机器学习” 四个字,经过word−Embedding 以后作为输入喂给一个RNN ,然后经过隐藏层得出隐藏层信息h1,h2,...,这时候在Decoder 部分的第一个时刻的hidden−state 假设为z0,z0 的和h1,h2,,,进行相似度的计算,得出各个时刻的a10,a20,...,然后在ai 与对应的hi 相乘求和得到这c0 ,其大致过程如下:


我们可以看出encoder 和decoder 是联合的进行训练,在训练过程中,在某个时刻模型会学习到当前应该focus 到input 的哪一部分,这体现在a10,a20,... 不同的系数权重上,attention 就是在学习这些系数。
得出的C0 作为Decoder 的下一时刻的输入。后面的时刻同理。

其实对于attention 没有固定的套路,例如softmax 这一步不一定非要做,听说有人做实验发现不做softmax 效果还好些。其变种有很多。