Fork me on GitHub

seq2seq入门详解:从RNN到Attention


@[toc]

1. 前馈神经网络的缺点

对于输入向量中个分量的位置信息不感知,也即无法利用序列型输入特征向量中的位置信息(将个分量调换顺序最后训练出的模型是等价的),但是在实际的任务中,各分量是有先后关系的。例如,我们在理解一段文本时,孤立地理解每个字或者词是不够的,还要将它们作为一个整体的序列来理解。

2. 循环神经网络RNN

2.1. RNN的基本结构与数学定义

RNN的输入数据,一般有三个维度:batch大小,时间长度,特征维数。TensorFlow中的RNN层API的输入数据shape为[batch, timesteps, feature]。因为本节的图片来自Andrew NG的Coursera公开课中的例子,因此这里的RNN输入数据形状将以Andrew NG的习惯为例,这不影响原理的讲解。输入层的维数是$(n_x,m,T_x)$,其中$n_x$是每个训练样本的维数;$m$是一个batch的大小;$T_x$是输入序列的长度。

输出层的维数是$(n_y,m,T_y)$,其中$n_y$是输出预测向量的维数;$m$是一个batch的大小;$T_y$是输出序列的长度。

我们先研究输入向量和输出向量相等,即$n_x=n_y$的情况,结构图如下所示(图片来源https://www.coursera.org/learn/nlp-sequence-models/notebook/X20PE/building-a-recurrent-neural-network-step-by-step)。
在这里插入图片描述

图2.1 RNN基本结构

上下标说明举例:$a_5^{(2)[3]<4>}$表示第2个训练样本,第3层,第4个时刻,激活函数输出向量的第5维。
每个RNN-Cell的内部结构见下图
在这里插入图片描述

图2.2 RNN的一个基本单元

注意,输出$\hat y$是状态向量$a$经过线性变换再经过softmax变换得到的。

需要注意的是,在不同的RNN-Cell中,上述公式里面的参数$W,b$都是共享的。

2.2. 输入输出长度的讨论

2.2.1. $n_x=n_y=n$

第一种情况是输入输出长度相等的情况,如下图所示(图片来源https://www.jianshu.com/p/c5723c3bb921
在这里插入图片描述

图2.3 RNN-输入输出长度相等

常用于序列标注模型,例如命名实体识别模型中。

2.2.2. $n_x=n,n_y=1$

第二种情况是输入长度为N,输出长度为1(图片来源https://www.jianshu.com/p/c5723c3bb921
在这里插入图片描述

图2.4 RNN-输出长度为1

模型只在最后一个时刻输出,常用于文本分类模型
利用RNN网络预测sin函数的代码例子:https://github.com/lankuohsing/TensorFlow-Examples/blob/master/RNN/RNN_sin.py

2.2.3. $n_x=1,n_y=n$

第三种情况是输入长度为1,输出长度为N。uti实现时,可以将输入作为最开始时刻的输入,也可以作为所有时刻的输入(图片来源https://www.jianshu.com/p/c5723c3bb921
在这里插入图片描述

图2.5 RNN-输入长度为1

在这里插入图片描述

图2.6 RNN-输入长度为1(输入特征在所有时刻重复使用)

常用于文字生成模型中。

2.2.4. $n_x=n,n_y=m$,Encoder-Decoder模型

第四种情况是输入长度为N,输出长度为M的情况,也即Encoder-Decoder模型(图片来源https://www.jianshu.com/p/c5723c3bb921
在这里插入图片描述

图2.7 Encoder-Decoder模型,输入输出长度为一般情况的RNN

常用于语音识别、机器翻译等场景。在后面的章节中我们会详细介绍Encoder-Decoder模型

3. RNN的复杂变种

3.1. GRU(Gated Recurrent Unit)

GRU的提出是为了解决RNN难以学习到输入序列中的长距离信息的问题。
GRU引入一个新的变量——记忆单元,简称$C$。$C^{\langle t\rangle}$其实就是$a^{\langle t\rangle}$
$C$的表达式不是一步到位的,首先定义$C$的候选值$\tilde C$:

更新门:

在实际训练好的网络中$\Gamma$要么很接近1要么很接近0,对应着输入序列里面有些元素起作用有些元素不起作用。

也即输入序列的有些元素,记忆单元不需要更新,有些元素需要更新。

The cat, which already ate …, was full

cat后面的词直到was之前,都不需要更新$C$,直接等于cat对应的$C$
可以解决梯度消失的问题.输出层的梯度可以传播到cat处

注:$C$和$\Gamma$都可以是想聊,它们在相乘时采用的是element-wise的乘法。当为向量时,与cat的单复数无关的词对应的$\Gamma$可能有些维度为零,有些维度不为零。为零的维度,是用来保留cat的单复数信息的;不为零的维度可能是保留其他语义信息的,比如是不是food呀之类的
目前讨论的是简化版的GRU,结构图如下
在这里插入图片描述

图3.1GRU的一个基本单元

完整的GRU:

$\Gamma_r$表示了$\tilde C^{\langle t\rangle}$和$C^{\langle t-1\rangle}$之间的相关程度

3.2. LSTM(Long Short-Term Memory)

没有了$\Gamma_r$,将$1-\Gamma_u$用$\Gamma_f$代替

(注意公式里面的$\Gamma_u$等价于图片中的$\Gamma_i$)

在这里插入图片描述

图3.2 LSTM的一个基本单元

在这里插入图片描述

图3.3 标准LSTM模型-输入维数等于输出维数

3.2.1. peephole连接

在这里插入图片描述

图3.4 LSTM带有peephole

3.2.2 projection

对隐藏层状态a进行一次线性变换,降低其维数

4. Encoder-Decoder模型

由前面的章节我们知道,Encoder-Decoder模型就是输入输出长度为一般情况的RNN模型,示意图如下:
在这里插入图片描述

图4.1 Encoder-Decoder

其中Encoder负责将输入进行编码,得到语义编码向量C;Decoder负责将语义编码向量C进行解码,得到输出。以机器翻译为例,英文作为输入,输出为中文。可以用如下的数学模型来表示:

从Encoder得到C的方式有多种,可以将Encoder最后一个时刻的隐藏状态作为C,也可以将所有的隐藏状态进行某种变换得到C。
语义编码C在Decoder中的作用当时有多种,常见的有如下两种
(1) C作为Decoder的初始状态$h_0$。
在这里插入图片描述

图4.2 C作为Decoder的初始状态h0

(2) C作为Decoder的每一步输入。
在这里插入图片描述

图4.2 C作为Decoder的每一步输入

4.1. 几种典型的encoder-decoder

4.1.1. 第一种:语义编码C作为Decoder的初始输入

本小节的encode-decoder模型可以看成是seq2seq的开山之作,来源于google的论文https://arxiv.org/abs/1409.3215 Sequence to Sequence Learning with Neural Networks。该论文的模型是为了翻译问题而提出的,其中的encoder和decoder都采用LSTM,decoder中采用了beam search来提升效果。此外,该论文还采用了一个tric——将输入源句子倒序输入。这是因为,无论RNN还是LSTM,其实都是有偏的,即顺序越靠后的单词最终占据的信息量越大。如果源句子是正序的话,则采用的是最后一个词对应的state来作为decoder的输入来预测第一个词。这样是是不符合直觉的,因为没有对齐。将源句子倒序后,某种意义上实现了一定的对齐。
在这里插入图片描述

图4.3 Encoder-Decoder框架1-C作为Decoder的初始输入

Encoder:

Decoder:

4.1.2. 第一种:语义编码C作为Decoder的每一步输入

https://arxiv.org/pdf/1406.1078 Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation
在这里插入图片描述

图4.4 Encoder-Decoder框架2-C作为Decoder的每一步输入

Encoder:

Decoder:

4.2. Encoder-Decoder的缺点

  1. 对于输入序列的每个分量的重要程度没有区分,这和人的思考过程是不相符的,例如人在翻译的时候,对于某个一词多义的词,可能会结合上下文中某些关键词进行辅助判断。
  2. 如果在Decoder阶段,仅仅将C作为初始状态,随着时间往后推进,C的作用会越来越微弱。

事实上,Attention机制的提出,主要就是为了解决上述问题。

5. Attention机制详解

前面讲到,在一般形式的encoder-decoder中,输入信息先经过encoder编码保存在C中,C再被decoder使用。这种“直接粗暴”的方式,可能会导致输入信息没有被合理的利用,尤其是当输入信息过长的时候。为了解决这个问题,Attention机制被提出,解决的思路是:在decoder阶段,每个时间点输入的C是不同的(示意图如下图所示),需要根据当前时刻要输出的y去合理地选择输入x中的上下文信息。
在这里插入图片描述

图5.1 Attention机制示意图

具体来讲,就是对encoder的隐藏状态进行加权求和,以便得到不同的C,以中文翻译英文为例,示意图如下:
在这里插入图片描述

图5.2 Attention-对encoder隐藏状态进行加权求和得到不同的C

记$a_{ij}$为encoder中第$j$个隐藏状态$h_j$到decoder中第$i$个隐藏状态$h_i’$对应的$c_i$的权重,可以通过训练确定的,具体计算方法见后文。attention机制的核心思想可以概括为”对输入信息加权求和得到编码信息c”,也即如下公式:

5.1. attention机制中权重系数的计算过程

attention机制中权重系数有多种计算过程,对应于不同种类的attention机制。但是大部分的attention机制,都能表示为下文提到的三个抽象阶段。这里先引入几个概念。
我们将模型输入内容记为source,输出内容记为target。
source可以表示为一个一个的,target则表示为一个一个的query。在机器翻译中,key和value合并为一个,就是输入句子中每个单词对应的隐藏层状态。
通过计算Query和各个Key的相似性或者相关性(需要进行softmax归一化),得到每个Key对应Value的权重系数,然后对Value进行加权求和,即得到了最终的Attention数值。

具体来说可以分为三个阶段,如下图所示:
在这里插入图片描述

图5.3 attention机制中计算权重系数的三个阶段

其中第一阶段计算相似性时有多种方法,例如向量点积、余弦相似度,甚至可以用一个小的神经网络来通过学习的方式计算。
第二阶段softmax归一化的公式如下:

5.2. 几种典型的attention机制

5.2.1. 第一种:Bahdanau Attention

https://arxiv.org/abs/1409.0473 Neural Machine Translation by Jointly Learning to Align and Translate,这篇论文可以看做是attention机制的开山论文。该论文也是为了解决翻译问题而提出的模型,encoder采用了双向的RNN结构,正向RNN和反向RNN的每个时刻的隐藏state拼接成一个两倍长的新的state。如果要用一个简洁的公式来概括attention,那就是mlp + softmax+加权求和。之前的模型都是用一个固定长度的语义向量C来将encoder和decode连接起来,而这篇论文里是采用一个attention模型来连接。
在这里插入图片描述

图5.4 attention机制框架1

Encoder:

语义向量:

其中$e_{ij}$是Encoder中$j$时刻隐藏层状态$h_j$对Decoder中$i$时刻的处处状态$s_i$的影响程度;$a(s_{i-1},h_j)$称为一个对齐模型,本论文中采用的是一个单隐藏层的多层感知机$a(s_{i-1},h_j)=v_a^T tanh(W_a s_{i-1}+U_ah_i)$,注意在实际推理的时候,$U_ah_i$是可以在推理之前预先算好的以减少推理计算量;$\alpha_{ti}$是对$e_{ti}$进行softmax归一化成的概率,也即前文提到的$a_{ij}$或者叫attention权重;$c_t$是$t$时刻的语义向量。可见上述计算得到权重系数$\alpha_{ij}$的的过程主要分为两步:MLP+SOFTMAX
Decoder:

5.2.2. 第二种: Luong Attention

源自此论文https://arxiv.org/abs/1508.04025 Effective Approaches to Attention-based Neural Machine Translation。该论文提出了两种不同的attention:global attention和local attention。前者attention将作用在源句子的每个词,计算量较大;后者是出于减小计算量的考虑,只关注一个区间内的词(并且是在一个句子内部)。

5.2.2.1. Global attention

在这里插入图片描述

图5.5 attention框架2.1-global attention

Encoder与上一小节中的不同之处在于,采用最顶层的LSTM的隐藏状态用于计算后续的语义向量C。
语义向量与上一小节的不同之处在于,在计算权重系数$\alpha_{ij}$的时候利用的是decoder中第$i$个隐藏状态(上一小节是第$i-1$个)和encoder中第$j$个隐藏状态来计算:

其中$a(s_{i},h_j)$的选择有多种:

Decoder

5.2.2.2. Local Attention

Global attention的缺点在于,预测某个target word时,需要计算该target对应的hidden state与encoder汇总所有hidden state之间的关系。当输入句子比较长时,这将非常耗时。Local attention就散为了解决这个问题,它只关注源句子中一个窗口内的词对应的hidden state。它的思想来源于soft-attention和hard-attention。
在这里插入图片描述

图5.6 attention框架2.2-local attention

具体来说,对于要预测的某个target word, 首先计算出一个对齐位置$p_j$,然后一个以$p_i$为中心的窗口$[p_i-D,p_i+D]$会被用来计算$c_i$。窗口大小由经验给定。计算$c_i$的方法与前面类似。
因此,重点在于如何计算$p_i$。论文给出了两种方法:

  1. 单调对齐方式(Monotonic alignment):假设源句子和目标句子是单调对齐的。取$p_i=i$。
  2. 预测对齐方式(Predictive alignment):采用如下公式计算$p_i$:其中S是源句子的长度。同时,为了增大$p_i$附近的hidden state权重,权重系数将再乘上一个高斯分布:其中$\delta=D/2$。$p_i$是个实数,而j是个整数

    5.2.3. Self-Attention

    参考https://jalammar.github.io/illustrated-transformer/
    Self-Attention最著名的应用是在论文https://arxiv.org/abs/1706.03762 Attention Is All You Need中,作为transformer结构的一个重要组成部分。
    前面提到的几种attention,都是应用于基于RNN或者LSTM的encoder-decoder架构,通过计算decoder中隐藏状态和encoder中各个隐藏状态之间的关系来得到对应的权重。在《Attention Is All You Need》中,完全抛弃RNN/LSTM/CNN等结构,仅采用attention机制来进行机器翻译任务。并且在本论文中的attention机制采用的是self-attention,在encode源句子中一个词时,会计算这个词与源句子中其他词的相关性,减少了外部信息的依赖(这里我的理解是,与前面的几种attention机制不同,self-attention不需要decoder中的信息,也即encode一个句子时只用到了自身的信息,这就是self的含义)。
    下面个详细讲解self-attention原理。第一阶段是计算三个vector,见下图:图片来源https://jalammar.github.io/illustrated-transformer/

在这里插入图片描述

图5.7 self-attention vectors

为了简单起见,假设输入句子只有两个词,是”Thinking Machines”。如上图所示:首先得到这两个词的Embedding向量$x_1,x_2$(跟一般的nlp任务中的Embedding向量是一个概念),然后对于每个词,都计算出三个向量:Query Vector,Key Vector和Value Vector。计算过程为分别乘以三个矩阵:$W^Q,W^K,W^V$。
第二阶段是计算attention分数,假设现在要计算的是”Thinking”的attention值,那么需要计算”Thinking”与该句子中所有词的分数。需要计算$score(q_1,k_1),score(q_1,k_2)$。常见是计算公式是内积。过程如下图所示:
在这里插入图片描述

图5.8 self-attention score

第三阶段是对分数进行归一化。首先除以$\sqrt d_k$(Key Vector的维度),这是为了避免梯度爆炸;然后进行softmax归一化,这样是为了使得分数都在$[0,1]$之间。
在这里插入图片描述

图5.9 self-attention softmax

这个分数代表了encode “Thinking”需要放置多少注意力在各个单词上,不妨记为$s_{1i}$。
第四阶段就是利用上述得到分数对所有的Value Vector加权求和得到”Thinking”的representation: $z_1=\sum_{i=1}^{L_x}s_{1i}*v_i$
在这里插入图片描述

图5.10 self-attention output

值得注意的是,上述过程是可以并行化计算的,这是self-attention相对于RNN,LSTM等序列模型的优势(在处理长序列问题上)。
最后,我们给出self-attention的紧凑表达式:

这样就一步到位得到了输入句子中所有词的represent vector。
\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HzRS859m-1610871220672)(index_files/self-attention-matrix-calculation-2.png "self-attention 紧凑计算形式")\]

图5.11 self-attention 紧凑计算形式

5.2.4. Multi-Head Attention

所谓multi-head attention,其实就是将输入的Embedding切分成多份,对每一份独立地进行self-attention,然后将结果concat在一起。这是为了增加模型对于其他位置的词的注意力能力。因为如果只是一个self-attention,很容易使得某个词的注意力大部分集中在它自身。