处理 Seq2seq 最常用的就是 RNN。RNN 的问题在于无法 Parallel (并行处理),可以用 CNN 解决这个问题,但是 CNN 能够考虑的特征向量非常少,而解决这个问题又需要通过再次叠加 CNN 来解决。
为了解决这个问题,引入了 Self-Attention Layer,其输入是一个 sequence 输出也是一个 sequence,能够达到跟 RNN 一样的效果,输出 b
可以并行计算出来。
其技术最早出现的那篇文章就是 Attention Is All You Need 。
输入首先经过一个 embedding 输出 a 1 a^1 a1− a 4 a^4 a4。
a i = W x i a^i=Wx^i ai=Wxi
a i a^i ai 再乘上三个 Matrix:q
、k
、v
。q
用于 match 其它输出,k
用于被 match,v
是抽取出来的信息。
q i = W q a i q^i=W^qa^i qi=Wqai
k i = W k a i k^i=W^ka^i ki=Wkai
v i = W v a i v^i=W^va^i vi=Wvai
之后拿每一个 query q
去对每个 key k
做 attention。Attention 是输入两个向量,输出这两个向量有多匹配 (输出一个分数),做 Attention 的方法有很多种。之后除以 d \sqrt d d 是相当于归一化的处理,消除维度对于 Attention 计算的影响(维度越大向量乘积后求和项越多,分数越高)。
α 1 , i = q 1 ⋅ k i d \alpha_{1, i}=\frac{q^1·k^i}{\sqrt d} α1,i=dq1⋅ki
之后经过 softmax 得到 α ^ \hat \alpha α^
α ^ 1 , i = e α 1 , i ∑ j e α 1 , i \hat \alpha_{1,i}=\frac{e^{\alpha_{1,i}}}{\sum_je^{\alpha_{1,i}}} α^1,i=∑jeα1,ieα1,i
最后将 α ^ \hat \alpha α^ 与 v
相乘得到 b
b 1 = ∑ i α 1 , i v i b^1=\sum_i \alpha_{1,i}v^i b1=i∑α1,ivi
Self-Attention 的优势在于,计算 b1
的同时可以并行地计算 b2
通过下图向量化计算可以看到并行运算的方式
输入看做 I
,通过分别乘矩阵 W
得到 Q
、K
、V
K T Q K^TQ KTQ 得到 Attention 矩阵
再对 Attention 矩阵做 softmax
最后再乘 Value 矩阵得到 Output 矩阵
整个计算流程可以使用下图表示
Multi-head Self-attention 所期望的就是不同的 head 能够关注不同的东西,比如有的 head 关注局部信息,而另外一些关注全局信息。另外,可以类比于 CNN 里面的 filter,可以认为 Multi-head 是捕捉不同特征的 filter。
对 self-attention 来说,input 的次序是不重要的,就像之前看的一篇文章说 attention 是一个“精致”的词袋模型。
这样的话就会导致一个问题,比如说语句 “我一进来就看见常威在打来福” 跟 “我一进来就看见来福在打常威” 是一样的。但是我们希望将 Input 的次序考虑进去,在论文中引入了 Positional Encoding。在原始的论文中,作者加入设定的 e i e^i ei(不是学习出来的) 来解决这个问题。这里在第 1 步之前 e i + a i e^i+a^i ei+ai ,使得向量能够表征位置信息。
论文给出的编码公式如下
P E ( p o s , 2 i ) = s i n ( p o s 1000 0 2 i d m o d e l ) PE_{(pos,2i)}=sin(\frac {pos}{10000^{\frac{2i}{d_{model}}}}) PE(pos,2i)=sin(10000dmodel2ipos)
P E ( p o s , 2 i + 1 ) = c o s ( p o s 1000 0 2 i d m o d e l ) PE_{(pos,2i+1)}=cos(\frac {pos}{10000^{\frac{2i}{d_{model}}}}) PE(pos,2i+1)=cos(10000dmodel2ipos)
这里位置信息的加入可以理解为在第 0 步运算的时候, x i x^i xi 拼接了一个 one-hot 向量 p i p^i pi 表示第 i 个位置的词,然后乘权重矩阵 W,拆解来看最终等效于 e i + a i e^i+a^i ei+ai
上面讲的是 self-attention 取代 RNN。接下来我们阐述 self-attention 怎么在 seq2seq 中使用,简要来说就是 Encoder 中的 RNN 与 Decoder 中的 RNN 统统都用 Self-Attention 来代替。
Transform 的网络结构如下所示:
数据先加上位置关系进入编码器,在编码器中先经过 Self-Attention Layer 再通过 add 和 layer norm 层。再 Decode 中输入是前一个 time step 的 output,经过编码和位置编码之后输入到 block 中,Masked Multi-Head 中的 Mask 用于处理之前发生过的状态做 attention。再与之前的 Input Embedding 得到的输出结合,得到最终的输出。
Add 层是将第 0 步计算得到的 a 和第 4 步计算得到的 b 求和
Layer Norm 是对样本的各个特征进行归一化(Batch-normalization 与 Layer-normalization 区别)
Mask 的作用是只作用于已经产生的序列,没有产生的不能使用(机器学习翻译成 machine learning,翻译 machine 的时候是看不到 learning 的,翻译 learning 的时候可以看到 machine)
这里对于 Transformer 框架结构讲的并不是很深入,【NLP】Transformer 详解。两者都没有讲残差连接这个点。
Encoder 由 N=6 个相同的 layer 组成,layer 指的就是上图左侧的单元,最左边有个 “Nx”,这里是 x6 个。每个 Layer 由两个 sub-layer 组成,分别是 multi-head self-attention mechanism 和 fully connected feed-forward network。其中每个 sub-layer 都加了 residual connection 和 normalisation,因此可以将 sub_layer 的输出表示为:
s u b _ l a y e r _ o u t p u t = L a y e r N o r m ( x + ( S u b L a y e r ( x ) ) ) sub\_layer\_output=LayerNorm(x+(SubLayer(x))) sub_layer_output=LayerNorm(x+(SubLayer(x)))
接下来按顺序解释一下这两个 sub-layer:
原始 attention 的表达式 a t t e n t i o n _ o u t p u t = A t t e n t i o n ( Q , K , V ) attention\_output=Attention(Q,K,V) attention_output=Attention(Q,K,V)
multi-head attention 则是通过 h 个不同的线性变换对 Q,K,V 进行投影,最后将不同的 attention 结果拼接起来:
h e a d i = A t t e n t i n ( Q W i Q , K W i K , V W i V ) head_i=Attentin(QW_i^Q,KW_i^K,VW_i^V) headi=Attentin(QWiQ,KWiK,VWiV)
M u l t i H e a d ( Q , K , V ) = C o n c a t ( h e a d 1 , . . . , h e a d h ) W O MultiHead(Q,K,V)=Concat(head_1,...,head_h)W^O MultiHead(Q,K,V)=Concat(head1,...,headh)WO
self-attention 则是取 Q,K,V 相同。
文章中 attention 的计算采用了 scaled dot-product
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt d_k})V Attention(Q,K,V)=softmax(dkQKT)V
这层主要是提供非线性变换。Attention 输出的维度是 [bszseq_len, num_headshead_size],第二个 sub-layer 是个全连接层,之所以是 position-wise 是因为过线性层时每个位置 i 的变换参数是一样的。
Decoder 和 Encoder 的结构差不多,但是多了一个 attention 的 sub-layer,这里先明确一下 decoder 的输入输出和解码过程:
输入
Encoder 的输出
对应 i-1 位置 Decoder 的输出
所以中间的 attention 不是 self-attention,是上图里的 Encoder-Decoder Attention。它的 K,V 来自 Encoder,Q 来自上一位置 Decoder 的输出
输出
对应 i 位置的输出词的概率分布
解码
这里要特别注意一下,编码可以并行计算,一次性全部 encoding 出来,但解码不是一次把所有序列解出来的,而是像 rnn 一样一个一个解出来的,因为要用上一个位置的输入当作 attention 的 query。
Decoder 最下面的 Self-Attention 对应框架图的 Masked Multi-head Attention 层,因为解码器无法得知该位置后面的信息,所以有了 mask 机制,让 self-attention 层仅允许得知输出序列中已知的输出。
Decoder 的流程可以总结为:
Encoder 的输出作为 K 和 V
Docoder 前一个位置的输入经过 Masked Multi-head Attention 得到 Q
通过 Encoder-Decoder Attention 层和 Feed Forward 层得到该层输出
经过多个 Decoder 层和 Linear + Softmax 得到预测结果
The animal didn’t cross the street because it was too tired.
The animal didn’t cross the street because it was too wide.
下面的图里面颜色越深表示注意力权重越大,第一句话里面 it 权重最大的是 animal,第二句里面 it 权重最大的是 street,和原来句子的指代完全相同。
上面部分关注的是全局信息,下面部分关注的是局部信息,Multi-head 可以将两个特征都提取出来
横向 Transformer,纵向 RNN
Tensorflow 实现
Pytorch 实现
优点
缺点
参考链接
李宏毅 Transformer 笔记附代码
详解 Transformer (Attention Is All You Need)
【NLP】Transformer 详解