NLP预训练模型【5】 – T r a n s f o r m e r Transformer T r an s f or m er
《 A t t e n t i o n Attention A tt e n t i o n Is All You Need》 是一篇Google提出的将 A t t e n t i o n Attention A tt e n t i o n 思想发挥到极致的论文。这篇论文中提出一个全新的模型,叫 T r a n s f o r m e r Transformer T r an s f or m er ,抛弃了以往深度学习任务里面使用到的CNN和RNN。目前大热的BERT就是基于 T r a n s f o r m e r Transformer T r an s f or m er 构建的,这个模型广泛应用于NLP领域,例如机器翻译,问答系统,文本摘要和语音识别等等方向。
2.1 总体结构
T r a n s f o r m e r Transformer T r an s f or m er 的结构和 A t t e n t i o n Attention A tt e n t i o n 模型一样, T r a n s f o r m e r Transformer T r an s f or m er 模型中也采用了 E n c o e r − D e c o d e r Encoer- Decoder E n coer − Deco d er 架构。但其结构相比于 A t t e n t i o n Attention A tt e n t i o n 更加复杂,论文中 E n c o d e r Encoder E n co d er 层由6个 E n c o d e r Encoder E n co d er 堆叠在一起, D e c o d e r Decoder Deco d er 层也一样。
不了解 A t t e n t i o n Attention A tt e n t i o n 模型的,可以回顾之前的文章。
每一个 E n c o d e r Encoder E n co d er 和 D e c o d e r Decoder Deco d er 的内部结构如下图:
E n c o d e r Encoder E n co d er ,包含两层,一个 s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 层和一个前馈神经网络, s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 能帮助当前节点不仅只关注当前词,还能获取到上下文的语义。
D e c o d e r Decoder Deco d er 也包含 E n c o d e r Encoder E n co d er 提到的两层网络,但是在这两层中间还有一层 A t t e n t i o n Attention A tt e n t i o n 层,帮助当前节点获取到当前需要关注的重点内容。
2.2 E n c o d e r Encoder E n co d er 层结构
首先,模型需要对输入的数据进行一个 E m b e d d i n g Embedding E mb e dd in g 操作,也可以理解为类似 w o r d 2 v e c word2vec w or d 2 v ec 的操作, E m b e d d i n g Embedding E mb e dd in g 结束之后,输入到 E n c o d e r Encoder E n co d er 层, s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 处理完数据后把数据送给前馈神经网络,前馈神经网络的计算可以并行,得到的输出会输入到下一个 E n c o d e r Encoder E n co d er 。
2.2.1 Positional Encoding
T r a n s f o r m e r Transformer T r an s f or m er 模型中缺少一种解释输入序列中单词顺序的方法,它跟序列模型还不不一样。为了处理这个问题, T r a n s f o r m e r Transformer T r an s f or m er 给 E n c o d e r Encoder E n co d er 层和 D e c o d e r Decoder Deco d er 层的输入添加了一个额外的向量 P o s i t i o n a l E n c o d i n g Positional \quad Encoding P os i t i o na l E n co d in g ,维度和 E m b e d d i n g Embedding E mb e dd in g 的维度一样,这个向量采用了一种很独特的方法来让模型学习到这个值,这个向量能决定当前词的位置,或者说在一个句子中不同的词之间的距离。这个位置向量的具体计算方法有很多种,论文中的计算方法如下:
P E ( p o s , 2 i ) = sin ( p o s 1000 0 1 d m o d e l ) P E ( p o s , 2 i + 1 ) = cos ( pos 1000 0 1 i d m o d e l ) \begin{equation}
\begin{aligned}
P E(p o s, 2 i)&=\sin \left(\frac{p o s}{10000^{\frac{1}{d_{m o d e l}}}}\right) \\\\
P E(p o s, 2 i+1)&=\cos \left(\frac{\text { pos }}{10000^{\frac{1 i}{d_{m o d e l}}}}\right)
\end{aligned}
\end{equation}
PE ( p os , 2 i ) PE ( p os , 2 i + 1 ) = sin ( 1000 0 d m o d e l 1 p os ) = cos ( 1000 0 d m o d e l 1 i pos )
其中 p o s pos p os 是指当前词在句子中的位置,i i i 是指向量中每个值的 i n d e x index in d e x ,可以看出,在偶数位置,使用正弦编码,在奇数位置,使用余弦编码 。
最后把这个 P o s i t i o n a l E n c o d i n g Positional Encoding P os i t i o na lE n co d in g 与 E m b e d d i n g Embedding E mb e dd in g 的值相加,作为输入送到下一层。
2.2.2 S e l f − A t t e n t i o n Self- Attention S e l f − A tt e n t i o n
接下来我们详细看一下 s e l f − A t t e n t i o n self- Attention se l f − A tt e n t i o n ,其思想和 A t t e n t i o n Attention A tt e n t i o n 类似,但是 s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 是 T r a n s f o r m e r Transformer T r an s f or m er 用来将其他相关单词的“理解”转换成我们正在处理的单词的一种思路,我们看个例子:
The animal didn’t cross the street because it was too tired
这里的 it 到底代表的是 animal 还是 street 呢,对于我们来说能很简单的判断出来,但是对于机器来说,是很难判断的,s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 就能够让机器把 it 和 animal 联系起来,接下来我们看下详细的处理过程。
首先,s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 会计算出三个新的向量,在论文中,向量的维度是512维,我们把这三个向量分别称为 Q u e r y Query Q u ery 、 K e y Key Key 、 V a l u e Value Va l u e ,这三个向量是用 E m b e d d i n g Embedding E mb e dd in g 向量与一个矩阵相乘得到的结果,这个矩阵是随机初始化的,维度为(64,512)注意第二个维度需要和 E m b e d d i n g Embedding E mb e dd in g 的维度一样,其值在 BP的过程中会一直进行更新,得到的这三个向量的维度是64。
然后,计算 s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 的分数值,该分数值决定了当我们在某个位置 E n c o d e Encode E n co d e 一个词时,对输入句子的其他部分的关注程度。这个分数值的计算方法是 Q u e r y Query Q u ery 与 K e y Key Key 做点乘,以下图为例,首先我们需要针对 Thinking 这个词,计算出其他词对于该词的一个分数值,首先是针对于自己本身即 q 1 ⋅ k 1 q_1·k_1 q 1 ⋅ k 1 ,然后是针对于第二个词即 q 1 ⋅ k 2 q_1·k_2 q 1 ⋅ k 2 。
接下来,把点乘的结果除以一个常数,这里我们除以 8 ,这个值一般是采用上文提到的矩阵的第一个维度的开方即 64 的开方 8 ,当然也可以选择其他的值,然后把得到的结果做一个 s o f t m a x softmax so f t ma x 的计算。得到的结果即是每个词对于当前位置的词的相关性大小,当然,当前位置的词相关性肯定会会很大。
下一步就是把 V a l u e Value Va l u e 和 s o f t m a x softmax so f t ma x 得到的值进行相乘,并相加,得到的结果即是 s e l f − a t t e n t i o n self-attention se l f − a tt e n t i o n 在当前节点的值。
在实际的应用场景,为了提高计算速度,我们采用的是矩阵的方式,直接计算出 Q u e r y Query Q u ery , K e y Key Key , V a l u e Value Va l u e 的矩阵,然后把 E m b e d d i n g Embedding E mb e dd in g 的值与三个矩阵直接相乘,把得到的新矩阵Q与K相乘,乘以一个常数,做 s o f t m a x softmax so f t ma x 操作,最后乘上V矩阵。
这种通过 Q u e r y Query Q u ery 和 K e y Key Key 的相似性程度来确定 V a l u e Value Va l u e 的权重分布的方法被称为 scaled dot-product Attention 。
2.2.3 Multi-Headed A t t e n t i o n Attention A tt e n t i o n
这篇论文更牛逼的地方是给 s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 加入了另外一个机制,被称为 M u l t i − H e a d e r A t t e n t i o n Multi-Header \quad Attention M u lt i − He a d er A tt e n t i o n ,该机制理解起来很简单,就是说不仅仅只初始化一组 Q 、 K 、 V Q、K、V Q 、 K 、 V 的矩阵,而是初始化多组, T r a n f o r m e r Tranformer T r an f or m er 是使用了 8 组 ,所以最后得到的结果是 8 个矩阵。
2.2.4 Layer normalization
在 T r a n s f o r m e r Transformer T r an s f or m er 中,每一个子层(s e l f − a t t e n t i o n self-attention se l f − a tt e n t i o n ,F e e d F o r w a r d N e u r a l N e t w o r k Feed \quad Forward \quad Neural \quad Network F ee d F or w a r d N e u r a l N e tw or k )之后都会接一个残差模块,并且有一个 L a y e r N o r m a l i z a t i o n Layer \quad Normalization L a yer N or ma l i z a t i o n 。
N o r m a l i z a t i o n Normalization N or ma l i z a t i o n 有很多种,但是它们都有一个共同的目的,那就是把输入转化成均值为 0 方差为 1 的数据。我们在把数据送入激活函数之前进行 n o r m a l i z a t i o n normalization n or ma l i z a t i o n (归一化),因为我们不希望输入数据落在激活函数的饱和区。
Batch Normalization
BN 的主要思想就是:在每一层的每一批数据上进行归一化。我们可能会对输入数据进行归一化,但是经过该网络层的作用后,我们的数据已经不再是归一化的了。随着这种情况的发展,数据的偏差越来越大,我的反向传播需要考虑到这些大的偏差,这就迫使我们只能使用较小的学习率来防止梯度消失或者梯度爆炸。BN 的具体做法就是对每一小批数据,在批这个方向上做归一化。
Layer normalization
它也是归一化数据的一种方式,不过** LN 是在每一个样本上计算均值和方差**,而不是 BN 那种在批方向计算均值和方差!公式如下:
L N ( x i ) = α ∗ x i − μ L σ L 2 + ε + β L N\left(x_{i}\right)=\alpha * \frac{x_{i}-\mu_{L}}{\sqrt{\sigma_{L}^{2}+\varepsilon}}+\beta
L N ( x i ) = α ∗ σ L 2 + ε x i − μ L + β
2.2.5 Feed Forward Neural Network
这给我们留下了一个小的挑战,前馈神经网络没法输入 8 个矩阵呀,这该怎么办呢?所以我们需要一种方式,把 8 个矩阵降为 1 个,首先,我们把 8 个矩阵连在一起,这样会得到一个大的矩阵,再随机初始化一个矩阵和这个组合好的矩阵相乘,最后得到一个最终的矩阵。
2.3 D e c o d e r Decoder Deco d er 层结构
根据上面的总体结构图可以看出, D e c o d e r Decoder Deco d er 部分其实和 E n c o d e r Encoder E n co d er 部分大同小异,刚开始也是先添加一个位置向量 P o s i t i o n a l E n c o d i n g Positional \quad Encoding P os i t i o na l E n co d in g ,方法和2.2.1节一样,接下来接的是 M a s k e d M u t i l − h e a d A t t e n t i o n Masked \quad Mutil-head \quad Attention M a s k e d M u t i l − h e a d A tt e n t i o n ,这里的 m a s k mask ma s k 也是 T r a n s f o r m e r Transformer T r an s f or m er 一个很关键的技术,下面我们会进行一一介绍。
其余的层结构与 E n c o d e r Encoder E n co d er 一样,请参考 E n c o d e r Encoder E n co d er 层结构。
2.3.1 Masked Mutil-head Attention
mask表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果 。 T r a n s f o r m e r Transformer T r an s f or m er 模型里面涉及两种 m a s k mask ma s k ,分别是 p a d d i n g m a s k padding \quad mask p a dd in g ma s k 和 s e q u e n c e m a s k sequence \quad mask se q u e n ce ma s k 。其中, p a d d i n g m a s k padding \quad mask p a dd in g ma s k 在所有的 s c a l e d d o t − p r o d u c t A t t e n t i o n scaled dot-product Attention sc a l e dd o t − p ro d u c t A tt e n t i o n 里面都需要用到,而 s e q u e n c e m a s k sequence \quad mask se q u e n ce ma s k 只有在 D e c o d e r Decoder Deco d er 的 s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 里面用到。
(1) p a d d i n g m a s k padding \quad mask p a dd in g ma s k
什么是 p a d d i n g m a s k padding \quad mask p a dd in g ma s k 呢?因为每个批次输入序列长度是不一样的也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充0。但是如果输入的序列太长,则是截取左边的内容,把多余的直接舍弃。因为这些填充的位置,其实是没什么意义的,所以我们的 A t t e n t i o n Attention A tt e n t i o n 机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。
具体的做法是,把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 s o f t m a x softmax so f t ma x ,这些位置的概率就会接近0!
而我们的 p a d d i n g m a s k padding \quad mask p a dd in g ma s k 实际上是一个张量,每个值都是一个Boolean,值为false的地方就是我们要进行处理的地方。
(2) s e q u e n c e m a s k sequence \quad mask se q u e n ce ma s k
文章前面也提到, s o f t m a x softmax so f t ma x 是为了使得 D e c o d e r Decoder Deco d er 不能看见未来的信息。也就是对于一个序列,在time_step为t的时刻,我们的解码输出应该只能依赖于t时刻之前的输出,而不能依赖t之后的输出。因此我们需要想一个办法,把t之后的信息给隐藏起来。
那么具体怎么做呢?也很简单:产生一个上三角矩阵,上三角的值全为0。把这个矩阵作用在每一个序列上,就可以达到我们的目的 。
对于 D e c o d e r Decoder Deco d er 的 s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n ,里面使用到的 $ scaled dot-product Attention$ ,同时需要 p a d d i n g m a s k padding \quad mask p a dd in g ma s k 和 s e q u e n c e m a s k sequence \quad mask se q u e n ce ma s k 作为 a t t n − m a s k attn-mask a tt n − ma s k ,具体实现就是两个 m a s k mask ma s k 相加作为 a t t n − m a s k attn-mask a tt n − ma s k 。
其他情况,a t t n − m a s k attn-mask a tt n − ma s k 一律等于 p a d d i n g m a s k padding \quad mask p a dd in g ma s k 。
2.3.2 Output层
当 D e c o d e r Decoder Deco d er 层全部执行完毕后,怎么把得到的向量映射为我们需要的词呢,很简单,只需要在结尾再添加一个全连接层和 s o f t m a x softmax so f t ma x 层,假如我们的词典是1w个词,那最终 s o f t m a x softmax so f t ma x 会输入1w个词的概率,概率值最大的对应的词就是我们最终的结果。
2.4 动态流程图
编码器通过处理输入序列开启工作。顶端编码器的输出之后会变转化为一个包含向量 K K K (键向量)和V V V (值向量)的注意力向量集 ,这是并行化操作 。这些向量将被每个解码器用于自身的“编码-解码注意力层”,而这些层可以帮助解码器关注输入序列哪些位置合适:
在完成编码阶段后,则开始解码阶段。解码阶段的每个步骤都会输出一个输出序列(在这个例子里,是英语翻译的句子)的元素。
接下来的步骤重复了这个过程,直到到达一个特殊的终止符号,它表示 T r a n s f o r m e r Transformer T r an s f or m er 的解码器已经完成了它的输出。每个步骤的输出在下一个时间步被提供给底端解码器,并且就像编码器之前做的那样,这些解码器会输出它们的解码结果 。
原论文中说到进行 M u l t i − h e a d A t t e n t i o n Multi-head \quad Attention M u lt i − h e a d A tt e n t i o n 的原因是将模型分为多个头,形成多个子空间,可以让模型去关注不同方面的信息,最后再将各个方面的信息综合起来。其实直观上也可以想到,如果自己设计这样的一个模型,必然也不会只做一次 A t t e n t i o n Attention A tt e n t i o n ,多次 A t t e n t i o n Attention A tt e n t i o n 综合的结果至少能够起到增强模型的作用,也可以类比CNN中同时使用多个卷积核 的作用,直观上讲,多头的注意力有助于网络捕捉到更丰富的特征/信息 。
(1)R N N RNN RNN 系列的模型,并行计算能力很差。 R N N RNN RNN 并行计算的问题就出在这里,因为 T T T 时刻的计算依赖 T − 1 T-1 T − 1 时刻的隐层计算结果,而 T − 1 T-1 T − 1 时刻的计算依赖 T − 2 T-2 T − 2 时刻的隐层计算结果,如此下去就形成了所谓的序列依赖关系。
(2) T r a n s f o r m e r Transformer T r an s f or m er 的特征抽取能力比 R N N RNN RNN 系列的模型要好。
具体实验对比可以参考:放弃幻想,全面拥抱Transformer :自然语言处理三大特征抽取器(CNN/RNN/TF)比较
但是值得注意的是,并不是说 T r a n s f o r m e r Transformer T r an s f or m er 就能够完全替代 R N N RNN RNN 系列的模型了,任何模型都有其适用范围,同样的,R N N RNN RNN 系列模型在很多任务上还是首选,熟悉各种模型的内部原理,知其然且知其所以然,才能遇到新任务时,快速分析这时候该用什么样的模型,该怎么做好。
s e q 2 s e q seq2seq se q 2 se q 缺点:这里用代替这个词略显不妥当, s e q 2 s e q seq2seq se q 2 se q 虽已老,但始终还是有其用武之地, s e q 2 s e q seq2seq se q 2 se q 最大的问题在于将 E n c o d e r Encoder E n co d er 端的所有信息压缩到一个固定长度的向量中 ,并将其作为 D e c o d e r Decoder Deco d er 端首个隐藏状态的输入,来预测 D e c o d e r Decoder Deco d er 端第一个单词(token)的隐藏状态。在输入序列比较长的时候,这样做显然会损失 E n c o d e r Encoder E n co d er 端的很多信息,而且这样一股脑的把该固定向量送入 D e c o d e r Decoder Deco d er 端, D e c o d e r Decoder Deco d er 端不能够关注到其想要关注的信息。
T r a n s f o r m e r Transformer T r an s f or m er 优点: T r a n s f o r m e r Transformer T r an s f or m er 不但对 s e q 2 s e q seq2seq se q 2 se q 模型这两点缺点有了实质性的改进(多头交互式 A t t e n t i o n Attention A tt e n t i o n 模块),而且还引入了 s e l f − A t t e n t i o n self-Attention se l f − A tt e n t i o n 模块,让源序列和目标序列首先“自关联”起来,这样的话,源序列和目标序列自身的 E m b e d d i n g Embedding E mb e dd in g 表示所蕴含的信息更加丰富,而且后续的 F F N FFN FFN 层也增强了模型的表达能力,并且 T r a n s f o r m e r Transformer T r an s f or m er 并行计算的能力是远远超过 s e q 2 s e q seq2seq se q 2 se q 系列的模型,因此我认为这是 T r a n s f o r m e r Transformer T r an s f or m er 优于 s e q 2 s e q seq2seq se q 2 se q 模型的地方。
6. 代码实现
地址:https://github.com/Kyubyong/Transformer
代码解读: Transformer 解析与tensorflow代码解读
7. 参考文献
作者:@mantchs
GitHub:https://github.com/NLP-LOVE/ML-NLP