神经机器翻译 之 谷歌 transformer 模型

声明:转载请在标题标明转载,并添加原文链接。

简介

这篇博客的主要内容是对谷歌提出的transformer 进行论文解读,包含算法复杂度的分析。对应的论文是 “Attention is all you need", 链接如下 https://arxiv.org/pdf/1706.03762.pdf

选择这篇论文的原因有三点。

1. 这篇论文达到了 new the state-of-the-art result,  应该是现在做神经翻译里最好的BLUE结果。

2. 这篇文章提出的算法另辟蹊径,没有采取大热的RNN/LSTM/GRU的结构,而是使用attention layer 和全连接层,达到了较好的效果,并且解决了 RNN/LSTM/GRU 里的long dependency problem 。

3. 这篇文章的算法解决了传统RNN 训练并行度的问题,并降低了计算复杂度。

接下来会按照 "Attention is all you need" 论文中的逻辑, 逐个模块介绍, 希望能对大家有所帮助。原文写在我的笔记上。

https://shimo.im/docs/gmRW4WV2mjoXzKA1/


模型结构

Fig.1 The transformer architecture


上面这个Fig.1 就是谷歌提出的transformer 的架构。这其中左半部分是 encoder 右半部分是 decoder.

Encoder: 这里面有 N=6 个 一样的layers, 每一层包含了两个sub-layers. 第一个sub-layer 就是多头注意力层(multi-head attention layer) 然后是一个简单的全连接层。 这里还有一个残差连接 (residual connection), 在这个基础上, 还有一个layer norm.  这里的注意力层会在下文详细解释。

Decoder: 这里同样是有六个一样的Layer是,但是这里的layer 和encoder 不一样, 这里的layer 包含了三个sub-layers,  其中有 一个self-attention layer, encoder-decoder attention layer 最后是一个全连接层。 前两个sub-layer 都是基于multi-head attention layer.  这里有个特别点就是masking,  masking 的作用就是防止在训练的时候 使用未来的输出的单词。 比如训练时, 第一个单词是不能参考第二个单词的生成结果的。 Masking就会把这个信息变成0, 用来保证预测位置 i 的信息只能基于比 i 小的输出。

Attention

Scaled dot-product attention

这里就详细讨论scaled dot-product attention. 在原文里, 这个算法是通过queriies, keys and values 的形式描述的, 非常抽象。这里我用了一张CMU NLP 课里的图来解释, Q(queries), K (keys) and V(Values), 其中 Key and values 一般对应同样的 vector, K=V 而Query vecotor  是对应目标句子的 word vector.


Fig. 2 Attention process (source:ttp://phontron.com/class/nn4nlp2017/assets/slides/nn4nlp-09-attention.pdf)

Fig. 2 里的quary vector  来自 decoder state, key/value vector来自所有的encoder state.  具体的操作有三个步骤。

1. 每个query-key 会做出一个点乘的运算过程

2. 最后会使用soft max 把他们归一。

3. 再到最后会乘以V (values) 用来当做attention vector.

这里的数学表达式如下。

如果用Numpy 来写, scaled dot-product attention, 内容如下

这个在实际呢, 是一个tensor dot product. 对于新手来说tensor dot product 可能还有些陌生。 对于一维向量的点乘(dot product), 结果是一个 标量scalar, 对于一个高纬度的tensor  dot product, 结果就不那么好理解了。 在numpy 里的 numpy.dot  解释 如下,

If a is an N-D array and b is an M-D array (where M>=2), it is a sum product over the last axis of a and the second-to-last axis of b:

dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m]), source: https://docs.scipy.org/doc/numpy 1.14.0/reference/generated/numpy.dot.html

举个例子, 比如 张量(tensor)a  是一个四维矩阵,维度是[3,4,5,6], 张量 b 也是一个四维矩阵, 维度是[5,4,6,3], 那么 dot(a,b) 的维度就是 [3,4,5,5,4,3].

这里面如果考虑计算复杂度呢, c 里的每一个元素 都是通过 sum(a[i,j,:] * b[k,:,m])计算得来,对于上图的例子,就需要6次乘法 5 次加法, 总共11 FLOPs, 那这样的张量dot product 就是11*3*4*5*5*4*3 FLOPs的运算量了。

Multi-head attention

上面的scaled dot-product attention, 看起来还有点简单, 网络的表达能力还有一些简单,所以提出了多头注意力机制(multi-head attention)。参考文献是[3] 用图说明。

Fig. 3 Convolution and Self-attention difference (source: https://nlp.stanford.edu/seminar/details/lkaiser.pdf)

这里的self-attention, 就是上文中query (Q), keys (K) and Values (V) 都是相同的, 表达同一段句子 (Q=K=V). Fig. 3 右图说明了, 对于self-attention, target node (生成的那个点) 实际上和 输入中的任意一点的距离是相同的。 这点是可以用来获得 long path dependency的, 在翻译句子里尤为重要。 而convolution, 因为有卷积窗口的 问题, 任意两点的局距离是 Log_k(n), 其中k 是卷积窗口的大小,而n句子长度。 所以说这个self-attention 可以解决 翻译句子 source -target 对齐的问题。    一般有三种注意力方式, 如下图所示。

Fig. 4 三种注意力方式 (source: https://nlp.stanford.edu/seminar/details/lkaiser.pdf)

这里的masked decoder self attention, 就是为了防止 当前的单词生成 对未来的单词产生依赖性。

Fig. 5 多头注意力机制(multi-head attention)和卷积

多头注意力机制很棒啊。 首先each head, 是可以并行计算的, 然后每个head 都有自己对应的weight, 实现不同的线性转换, 这样每个head 也就有了自己特别的表达信息。 所以Fig. 5 里的每个连接 是用彩色表示的。

那multi-head attention 是怎么实现的呢。 在执行self-attention 之前 Q, K, V 都会先乘以一个矩阵 做linear project, 把他投影到维度num_head*(d_q, d_k, d_v)。 这个地方举个例子就比较容易说明了, 比如以前的head 维度是 64, 我需要8 个head, 那我就用线性转换, 把我的输入变成的维度变成 512, 再reshape 一下。

具体就是, 我单个self head attention 的维度是 [1, 16, 512]  对应的是[batch, seq_length, head_dim]。 那我现在做multi-head attention, 那我就先把512 通过linear transform 变成64,(multiplied by 512*64 mixtrx) 那得到了 [1,16,64], 同样的操作作进行八次, reshape, 就是 [1, 8, 16,64].  这样multi-head attention 就实现了。 接下来继续按照Fig.2 中介绍的三步骤来实现 attention。

我们回到multi-head attention 论文中的讨论,

Fig. 6 Multi-head attention

有了前文的解释,Fig. 6 中的原文讨论就容易理解的多了。 第一段就是我上文的讨论, 说出了 a single attention head 的问题。  d_model 就是 512, d_k= d_v= 8.  Q 是 [1, 16, 512] 维度, 而且W_i^Q 的维度是 [512, 64].

这里有个潜在的问题, 计算复杂度变高了。 比如Q, [1,16,512], 512  前文说是head dimension,  其实是有物理含义的, 对于第一个attention 模块, 512 表达的是 word vector length, 就是用多长的vector 来代表一个单词。  而这个计算最终导致, 增加了O(n*d^2) 的复杂度, 其中 n 是sequence length, d 是word vector representation dimension.

Positional Embedding

讨论到现在, 还没有信息来表达单词和单词相关的位置关系。 这里, 谷歌用了sine 和cosine 函数来表达。 先上截图。

Fig. 7 positional embedding

这里使用了两个构造函数, sin, cosine. pos 用来表示单词的位置信息, 比如 第一个单词啦, 第二个单词什么的。 而 i  用来表达dimension。 现在的例子里, d_{model} 是512, 那 i 应该是 0 到255.  这里呢, 为了好说明, 如果2i= d_{model}, PE 的函数就是sin(pos/10000), 那它的波长就是10000*2pi,  如果i =0, 那么他的波长 就是2pi.  这样的sin, cosin 的函数 是可以通过线性关系互相表达的。

就是这么回事啦。

Auto recursive decoding

在Transformer里有一个概念就是auto recursive decoding. 这个并不好理解, 论文也没有详细讨论,让我思考许久。好在谷歌博客做了一个动图, 一目了然。

Fig. 8 Encoding-decoding的动态过程 source: https://ai.googleblog.com/2017/08/transformer-novel-neural-network.htm

Fig. 8  这个图的encoding  过程, 主要是self attention,  有三层。 接下来是decoding过程, 也是有三层, 第一个预测结果 <start> 符号, 是完全通过encoding 里的attention vector 做出的决策。 而第二个预测结果Je, 是基于encoding attention vector &  <start> attention vector  做出的决策。按照这个逻辑,新翻译的单词不仅仅依赖 encoding attention vector, 也依赖过去翻译好的单词的attention vector。 随着翻译出来的句子越来越多,翻译下一个单词的运算量也就会相应增加。 如果详细分析,复杂度是 (n^2d), 其中n是翻译句子的长度,d是word vector 的维度。

计算复杂度


计算复杂度对比

这里n, 是 sequence length, d 是word representation dimension. 通常 n<<d, 一个句子的长度 n 一般是3 ---30,  但是一个word representation dimension,大概300, 也有更多的, 比如512.

后续

实验结果我就先不分析了, 如果需要,请留言, 我再找时间。欢迎大家留言讨论。

参考文献

[1] Michal Chromiak Blog, https://mchromiak.github.io/articles/2017/Sep/01/Primer-NN/#attention-basis

[2] Google Blog, https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html

[3] Tensor2tensor transformer, https://nlp.stanford.edu/seminar/details/lkaiser.pdf

Gupao
登录 后发表评论
智慧如你,不想发表一点想法咩~