循环神经网络RNN结构被广泛应用于自然语言处理、机器翻译、语音识别、文字识别等方向。本文主要介绍经典的RNN结构,以及RNN的变种(包括Seq2Seq结构和Attention机制)。希望这篇文章能够帮助初学者更好地入门。
1.经典的RNN结构
这就是最经典的RNN结构,它的输入是:
输出为:
也就是说,输入和输出序列必有相同的时间长度!
2.Sequence to Sequence模型
在Seq2Seq结构中,编码器Encoder把所有的输入序列都编码成一个统一的语义向量Context,然后再由解码器Decoder解码。在解码器Decoder解码的过程中,不断地将前一个时刻 t-1 的输出作为后一个时刻 **t **的输入,循环解码,直到输出停止符为止。
3.Embedding
还有一点细节,就是如何将前一时刻输出类别index(数值)送入下一时刻输入(向量)进行解码。假设每个标签对应的类别index如下:
4.Seq2Seq训练问题
5.Attention注意力机制
在Seq2Seq结构中,encoder把所有的输入序列都编码成一个统一的语义向量Context,然后再由Decoder解码。由于context包含原始序列中的所有信息,它的长度就成了限制模型性能的瓶颈。如机器翻译问题,当要翻译的句子较长时,一个Context可能存不下那么多信息,就会造成精度的下降。除此之外,如果按照上述方式实现,只用到了编码器的最后一个隐藏层状态,信息利用率低下。
所以如果要改进Seq2Seq结构,最好的切入角度就是:利用Encoder所有隐藏层状态 h(t)解决Context长度限制问题。
上下文context表示成如下的方式(h的加权平均):
那么权重alpha(attention weight)可表示成Q和K的乘积,小h即V(下图中很清楚的看出,Q是大H,K和V是小h):
6.乘法VS加法attention
加法注意力:
还是以传统的RNN的seq2seq问题为例子,加性注意力是最经典的注意力机制,它使用了有一个隐藏层的前馈网络(全连接)来计算注意力分配:
乘法注意力:
就是常见的用乘法来计算attention score:
乘法注意力不用使用一个全连接层,所以空间复杂度占优;另外由于乘法可以使用优化的矩阵乘法运算,所以计算上也一般占优。
论文中的乘法注意力除了一个scale factor:
论文中指出当dk比较小的时候,乘法注意力和加法注意力效果差不多;但当d_k比较大的时候,如果不使用scale factor,则加法注意力要好一些,因为乘法结果会比较大,容易进入softmax函数的“饱和区”,梯度较小。
7.Luong Attention(简单了解)
注:这里就是把Decoder中的每一次的输入:上一层的输出y换成了Attention。
8.Self-Attention
9.《Attention is all you need》
9.1 encoder
注: 词向量加上了positional embedding,即给位置1,2,3,4…n等编码(也用一个embedding表示)。然后在编码的时候可以使用正弦和余弦函数,使得位置编码具有周期性,并且有很好的表示相对位置的关系的特性(对于任意的偏移量k,PE[pos+k]可以由PE[pos]表示):
输入的序列长度是n,embedding维度是d,所以输入是n*d的矩阵,
N=6,6个重复一样的结构,由两个子层组成:
子层1:
- Multi-head self-attention
- 残余连接和LN: Output = LN (x+sublayer(x))
子层2:
- Position-wise fc层(跟卷积很像):对n*d的矩阵的每一行进行操作(相当于把矩阵每一行铺平,接一个FC),同一层的不同行FC层用一样的参数,不同层用不同的参数(对于全连接的节点数目,先从512变大为2048,再缩小为512),这里的max表示使用relu激活函数
- 残差连接(待)
输出:
- 整个encoder的输出也是n*d的矩阵
9.2 Decoder
输入:假设已经翻译出k个词,向量维度还是d,同样使用N=6个重复的层,依然使用残余连接和LN
3个子层,比encoder多一个attention层,是Decoder端去attend encoder端的信息的层(待)
Sub-L1:
- self-attention,同encoder,但要Mask掉未来的信息,得到k*d的矩阵 (这个暂且不懂代码实现)
Sub-L2:
- 和encoder做attention的层,输出k*d的矩阵,这里就不是self-Attention了。
Sub-L3:
- 全连接层,输出k*d的矩阵,用第k行去预测输出y
10.mutli-head attention
获取每个子任务的Q、K、V:
- 通过全连接进行线性变换映射成多个Q、K、V,线性映射得到的结果维度可以不变、也可以减少(类似降维)
- 或者通过Split对Q、K、V进行划分(分段)
如果采用线性映射的方式,使得维度降低;或者通过split的方式使得维度降低,那么多个head做attention合并起来的复杂度和原来一个head做attention的复杂度不会差多少,而且多个head之间做attention可以并行。
11.代码理解,待补充************
参考链接: