Sequence to sequence入门详解:从RNN, LSTM到Encoder-Decoder, Attention, transformer(四)

313 阅读10分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

5. Attention机制详解

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

图5.1 Attention机制示意图

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

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

aija_{ij}为encoder中第jj个隐藏状态hjh_j到decoder中第ii个隐藏状态hih_i'对应的cic_i的权重,可以通过训练确定的,具体计算方法见后文。attention机制的核心思想可以概括为"对输入信息加权求和得到编码信息c",也即如下公式:

ci=j=1nxaijhj(5-1)c_i=\sum_{j=1}^{n_x}a_{ij}h_j\tag{5-1}

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

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

Attention(Queryi,Source)=j=1LxSimilarity(Query,keyj)valuej(5-2)Attention(Query_i,Source)=\sum_{j=1}^{L_x}Similarity(Query,key_j)*value_j\tag{5-2}

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

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

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

aij=softmax(Sij)=exp(Simij)j=1Lxexp(Simij)(5-3)a_{ij}=softmax(S_{ij})=\frac{exp(Sim_{ij})}{\sum_{j=1}^{L_x}exp(Sim_{ij})}\tag{5-3}

5.2. 几种典型的attention机制

5.2.1. 第一种:Bahdanau Attention

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:

hi=tanh(W[hi1,xi]+b)(5-4)\begin{aligned} h_i&=tanh(W[h_{i-1},x_i]+b)\\ \tag{5-4} \end{aligned}

语义向量:

eij=a(si1,hj)αij=exp(eij)k=1Txexp(eik)ci=j=1Txαijhj(5-5)\begin{aligned} e_{ij}&=a(s_{i-1},h_j)\\ \alpha_{ij}&=\frac{exp(e_{ij})}{\sum_{k=1}^{T_x}exp(e_{ik})}\\ c_i&=\sum_{j=1}^{T_x}\alpha_{ij}h_j\\ \tag{5-5} \end{aligned}

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

si=tanh(W[si1,yi1,ci])oi=softmax(Vsi)(5-6)\begin{aligned} s_i&=tanh(W[s_{i-1},y_{i-1},c_i])\\ o_i&=softmax(Vs_i)\\ \tag{5-6} \end{aligned}

5.2.2. 第二种: Luong Attention

源自论文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。 语义向量与上一小节的不同之处在于,在计算权重系数αij\alpha_{ij}的时候利用的是decoder中第ii个隐藏状态(上一小节是第i1i-1个)和encoder中第jj个隐藏状态来计算:

eij=a(si,hj)αij=exp(eij)k=1Txexp(eik)ci=j=1Txαijhjsi=tanh(W[si1,yi1])\begin{aligned} e_{ij}&=a(s_{i},h_j)\\ \alpha_{ij}&=\frac{exp(e_{ij})}{\sum_{k=1}^{T_x}exp(e_{ik})}\\ c_i&=\sum_{j=1}^{T_x}\alpha_{ij}h_j\\ s_i&=tanh(W[s_{i-1},y_{i-1}])\\ \end{aligned}

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

a(si,hj)={siThjdotsiTWahjgeneralvaTtanh(Wa[si;hj])concat(5-7)a(s_{i},h_j)=\left\{ \begin{aligned} &s_i^Th_j &dot \\ &s_i^TW_a h_j &general\\ &v_a^Ttanh(W_a[s_i;h_j]) &concat \\ \end{aligned} \right. \tag{5-7}

Decoder

s~i=tanh(W[si,ci])oi=softmax(Vs~i)(5-8)\begin{aligned} \tilde s_i&=tanh(W[s_i,c_i])\\ o_i&=softmax(V\tilde s_i)\\ \tag{5-8} \end{aligned}

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, 首先计算出一个对齐位置pjp_j,然后一个以pip_i为中心的窗口[piD,pi+D][p_i-D,p_i+D]会被用来计算cic_i。窗口大小由经验给定。计算cic_i的方法与前面类似。 因此,重点在于如何计算pip_i。论文给出了两种方法:

  1. 单调对齐方式(Monotonic alignment):假设源句子和目标句子是单调对齐的。取pi=ip_i=i
  2. 预测对齐方式(Predictive alignment):采用如下公式计算pip_i:
pi=Ssigmoid(vpTtanh(Wpsi))(5-9)p_i=S\cdot sigmoid(v_p^Ttanh(W_ps_i))\tag{5-9}

其中S是源句子的长度。同时,为了增大pip_i附近的hidden state权重,权重系数将再乘上一个高斯分布:

aij=αijexp((jpi)22δ2)(5-10)a_{ij}=\alpha_{ij}\cdot exp\left(-\frac{(j-p_i)^2}{2\delta^2}\right)\tag{5-10}

其中δ=D/2\delta=D/2pip_i是个实数,而j是个整数

5.2.3. Self-Attention

Self-Attention最著名的应用是在论文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(Q,K,V),见下图:

在这里插入图片描述

图5.7 self-attention vectors

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

图5.8 self-attention score

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

图5.9 self-attention softmax

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

图5.10 self-attention output

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

z=softmax(QKdk)V(5-11)z=softmax(\frac{Q\cdot K}{\sqrt {d_k}})V\tag{5-11}

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

图5.11 self-attention 紧凑计算形式

5.2.4. Multi-Headed Attention

所谓multi-headed attention,其实就是对输入Embedding做多次self-attention计算,每次都是在不同的子空间,可以学习到多份不同的权重参数(transformer里面是8份,也即8个head)对应不同的模式(类似于卷积里面的多个通道),然后将结果concat在一起。具体来讲这样做有两点好处:

  1. 这是为了增加模型对于其他位置的词的注意力能力。因为如果只是一个self-attention,很容易使得某个词的注意力大部分集中在它自身。
  2. 为attention层提供多个“表征子空间”。因为multi-headed attention有多个Q/K/V的矩阵集合(在transformer中有8个head),每个Q/K/V集合可以将输入的Embedding向量(或者上一个encoder-decoder)映射到不同的表征空间里。

如下图所示,输入Embedding得到8个head(对应8个分数z): 在这里插入图片描述

图5.12 multi-headed attention得到8个z

在进行后续计算时,一般会把这8个zconcat成一个长的矩阵,然后乘以一个矩阵WOW^O,得到一个矩阵ZZ,这个ZZ捕获了前面8个z的信息。 在这里插入图片描述

图5.13 multi-headed attention整体流程

6. 对输入序列进行位置编码

在经典的序列处理模型中,对于序列中各元素的“位置信息”的利用似乎都不太够。例如CNN只关注filter覆盖的区域中的局部位置信息;RNN能稍微关注到较长的信息,但是容易丢失更早期的信息;LSTM会挑出重点位置的信息,但由此可能造成丢失其他的位置信息;self attention/transformer中只关注全局位置信息,容易丢失相对位置信息。对于序列问题,尤其是自然语言这种有强逻辑性的序列问题,显示地引入位置信息作为特征似乎显得格外重要。

例如,对于I believe I have fallen in love with you,如果采用self-attention,两个I的encoded输出将会是一样的。但是显然他们不一样。

那么如何引入位置信息呢?最朴素的想法是直接对位置进行整数编码:给定一个长度为TT的文本,位置编码PE=pos=0,1,2,,T1PE=pos=0,1,2,\cdots,T-1.这样的缺点在于,越往后面的元素,它的位置编码越大,可能会导致权重在它们身上的倾斜;它比其他的特征(例如input Embedding)大许多,会导致权重在位置编码特征倾斜太多。那对上述位置编码进行归一化呢?PE=pos/(T1)PE=pos/(T-1)?这样只解决了数值过大的问题,但是它只关注绝对位置信息而丢失了相对位置信息。例如I like orange, because it provides me with VCSince orange can provide me with VC, I like orange.其中两个like,按理说位置信息应该相差不大(它们在句中的绝对位置虽然不一样,但是相对位置是一样的),用上述编码是会出问题的。

简单总结一下位置编码的需求:

  1. 需要体现同一个单词在不同位置的区别;
  2. 需要体现一定的先后次序关系,并且在一定范围内的编码差异不应该依赖于文本长度,具有一定不变性。
  3. 需要值域落入一定数值区间内的编码,又需要保证编码大小与文本长度无关

一种选择就是周期函数,例如三角函数,这也是transformer中选用sin和cos函数来进行位置编码的原因。

参考

www.elecfans.com/d/1482740.h… www.zhihu.com/question/34… blog.csdn.net/qq_33431368… arxiv.org/abs/1508.04… arxiv.org/abs/1409.04… jalammar.github.io/illustrated… arxiv.org/abs/1706.03… jalammar.github.io/illustrated…