文章目录
- Encoder-decoder结构
- Transformer结构
- Transformer的子结构
- 自注意力机制(Self attention)
- Mask
- 多头注意力(multi-head attention)
- 位置编码(Positional Encoding)
- Feed Forward
- Transformer详析
- Transformer整体结构
- 其他问题
- transformer训练与测试过程的不同
- mask的原理
Transformer结构是谷歌于2018年提出用于nlp的深度学习模型结构,同时成为了之后bert的基础,那么transformer到底是什么样子的,它的提出解决了什么问题,带来了什么变化,我们在下面详细解释。
Encoder-decoder结构
机器翻译遵循的模型一般是encoder-decoder结构,结构图如下所示
- encoder是编码器,通常是RNN结构或者CNN结构(Image Captioning或者TextCNN),但是RNN或者CNN结构往往存在一些问题:
- RNN结构由于存在前后依赖,无法进行并行计算
- CNN难以捕捉全局信息
- decoder是解码器,通常是RNN结构
因为以上的问题 ,Google提出使用attention结构来代替RNN和CNN的结构,这便是Transformer
Transformer结构
我们先来看Transfomer的结构图
如图左边部分便是transformer的encoder结构,右边便是decoder结构。突然看到这张图,可能很多人都很懵,那我们在下面的内容里面从各个子结构来详细解释这张图
Transformer的子结构
自注意力机制(Self attention)
如果你还不熟悉attention机制的话,请看这篇博文:
快速理解NLP中的Attention机制
我们现在把attention机制看作是:一个查询( Q Q Q)到一系列键值对( K → V K \to V K→V)的映射,其中 Q ( q 1 , q 2 , q 3 . . . . , q n ) Q(q_1,q_2,q_3....,q_n) Q(q1,q2,q3....,qn) Q ∈ R ( n , d k ) Q \in \mathbb R^{(n, d_k)} Q∈R(n,dk) K ( k 1 , k 2 , k 3 . . . . , k m ) K(k_1,k_2,k_3....,k_m) K(k1,k2,k3....,km) K ∈ R ( m , d k ) K \in \mathbb R^{(m, d_k)} K∈R(m,dk) V ( v 1 , v 2 , v 3 . . . . , v m ) V(v_1,v_2,v_3....,v_m) V(v1,v2,v3....,vm) V ∈ R ( m , d v ) V \in \mathbb R^{(m, d_v)} V∈R(m,dv) _注意此处 Q , K Q,K Q,K_的向量长度是一样的。
所以attention的步骤主要包含3步:
- Q K T QK^T QKT 这一步就是将 Q Q Q中的隐层向量和 K K K中的隐层向量相乘,得到n*m维的矩阵,表示二者的相似度
- s o f t m a x ( Q K T ) d k \frac {softmax(QK^T)}{\sqrt {d_k}} dk softmax(QKT)对 Q K T QK^T QKT进行softmax操作,得到n*m维的矩阵,表示输入的每个词在 q i q_i qi上的权重, d k \sqrt d_k d k是避免结果太大的scaled操作
- 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) = \frac {softmax(QK^T)}{\sqrt {d_k}}V attention(Q,K,V)=dk softmax(QKT)V ,得到n* d v d_v dv的矩阵,其中每一列表示一个decoder的RNN单元的输入
当 Q = K = V Q=K=V Q=K=V时,这时的注意力机制就被称为自注意机制了,我们假设一个句子是 ( x 1 , x 2 , x 3 , … … , x t ) = Q = K = V (x_1,x_2,x_3,……,x_t)=Q=K=V (x1,x2,x3,……,xt)=Q=K=V
- x 1 x k T x_1{x_k}^T x1xkT就是 x 1 x_1 x1和 x k x_k xk的点积,表示的是二者的相似程度
- s o f t m a x ( x 1 K T ) d k \frac {softmax(x_1K^T)}{\sqrt {d_k}} dk softmax(x1KT)表示 x 1 x_1 x1这个词和其他词相似度权重
- x 1 ′ = s o f t m a x ( x 1 K T ) d k V {x_1}'=\frac {softmax(x_1K^T)}{\sqrt {d_k}}V x1′=dk softmax(x1KT)V表示新的 x 1 x_1 x1作为接下来的输入
采用self-attention作为神经网络的输入结构比使用RNN好在:
- 每一层的计算复杂程度大大的降低
- 避免了RNN前后的依赖,使得可以快速并行计算
- 避免了太长句子的词语前后依赖给RNN带来的性能问题
Mask
实际的attention因为下面两个原因可能会遇到一些问题
- 神经网络输入的单个样本要求是等长的
- 为了保证句子长度一致,较短的句子我们可能采取补0的操作
补0的向量并没有实际含义,因此不能参与到attention的权重分配里面来,那我们该怎么做呢?请看下面的代码
In [2]: def softmax(x):
...: x_exp = np.exp(x)
...: return x_exp/np.sum(x_exp)
...:
In [3]: q = np.array([0.9, 0.7, 0.2])
In [4]: k = np.array([[0.2, 0.1, 0.7],[-100000,-100000,-100000]])
In [5]: softmax(np.dot(q, k.T))
Out[5]: array([1., 0.]))
明显当 k i k_i ki里面的元素都趋向于负无穷大时,第 i i i个词语的权重会接近于0
这就是mask操作,将补0的词语进行同上的处理使之无法参与attention计算
将Mask和注意力机制结合后的计算过程如上图所示,论文中称之为Scaled Dot-Product Attention
多头注意力(multi-head attention)
为了让模型有更多的表示子空间,transformer结构引入了multi-head attention。具体操作是在 Q , K , V Q,K,V Q,K,V进行attention操作之前,先对attention进行线性的变换
假设我们的输入是 ( Q , K , V ) (Q,K,V) (Q,K,V), 而且 Q = K = V Q=K=V Q=K=V Q ∈ R ( m , d m o d e l ) Q \in \mathbb R^{(m,d_{model})} Q∈R(m,dmodel) K ∈ R ( m , d m o d e l ) K \in \mathbb R^{(m,d_{model})} K∈R(m,dmodel) V ∈ R ( m , d m o d e l ) V \in \mathbb R^{(m,d_{model})} V∈R(m,dmodel)
首先我们先进行线性变换,假设进行attention操作的是 ( Q ∗ , K ∗ , V ∗ ) (Q^*,K^*, V^*) (Q∗,K∗,V∗) 其中:
- Q ∗ = Q W i Q Q^* = Q{W_i}^Q Q∗=QWiQ W i Q ∈ R ( d m o d e l , d k ) {W_i}^Q \in \mathbb R^{(d_{model}, d_k)} WiQ∈R(dmodel,dk) Q ∗ ∈ R ( m , d k ) Q^* \in \mathbb R^{(m, d_k)} Q∗∈R(m,dk)
- K ∗ = K W i K K^* = K{W_i}^K K∗=KWiK W i K ∈ R ( d m o d e l , d k ) {W_i}^K \in \mathbb R^{(d_{model}, d_k)} WiK∈R(dmodel,dk) K ∗ ∈ R ( m , d k ) K^* \in \mathbb R^{(m, d_k)} K∗∈R(m,dk)
- V ∗ = V W i V V^* = V{W_i}^V V∗=VWiV W i V ∈ R ( d m o d e l , d v ) {W_i}^V \in \mathbb R^{(d_{model}, d_v)} WiV∈R(dmodel,dv) K ∗ ∈ R ( m , d v ) K^* \in \mathbb R^{(m, d_v)} K∗∈R(m,dv)
然后, 进行 h h h次线性变换,得到 h h h个attention的结果:
- h e a d i = A t t e n t i o n ( Q W i Q , K W i K , V W i V ) head_i = Attention(Q{W_i}^Q, K{W_i}^K, V{W_i}^V) headi=Attention(QWiQ,KWiK,VWiV)
- h e a d i ∈ R ( m , d v ) head_i \in \mathbb R^{(m, d_v)} headi∈R(m,dv)
最后, 将上面的结果进行拼接,并乘 W O {W^O} WO
- 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 2 , . . . . . . , h e a d h ) W O MultiHead(Q,K,V) = Concat(head_1, head_2,......,head_h)W^O MultiHead(Q,K,V)=Concat(head1,head2,......,headh)WO
- Concat后的向量的向量空间是 R ( m , h d v ) \mathbb R^{(m, hd_v)} R(m,hdv)
- W O ∈ R ( h d v , d m o d e l ) W_O \in \mathbb R^{(hd_v, d_{model})} WO∈R(hdv,dmodel)
因此各位可以看到在经过Multi-head attention后,输出的维度会变回 ( m , d m o d e l ) (m, d_{model}) (m,dmodel) ,可以继续作为下一个self-attention的输入
位置编码(Positional Encoding)
上面的self-attention结构有一个重要的问题,那就是它忽略了词语之间的位置信息,不能体现词语的先后顺序。因此,论文引入了Positional Encoding的机制。即给每个词语一个位置向量。位置向量的计算方式如下:
P E ( p o s , k ) PE(pos, k) PE(pos,k)指的是第pos个词向量的第 k k k个元素的值
最后将词向量和位置向量进行拼接或者相加形成新的向量
Feed Forward
这其实就是对矩阵的每一个数字进行一个变换,可以理解为一维的卷积, x x x是矩阵中的每个数
Transformer详析
我们从encoder的左边的底部开始看
- 接收输入后首先进行Positional Encoding,形成新的输入 I I I(即 Q , K , V Q,K,V Q,K,V)
- 此处有一个残差连接,将 I I I传递给Multi-Head的后面
- Q,K,V进行多头注意力的处理,然后和之前的残差相加以及规范化
- 上一步的结果进入feed forward层,这里也有个残差连接
- 输出的结果继续和残差相加以及规范化得到encoder的输出
我们再来看decoder部分
- 最低端是输入,需要提醒的是,这里的输入在训练和测试过程是不一样的,下面有详细说明
- 接下来进入Masked Multi-Head Attention来做第一层的attention的处理,这里的masked和前面提到的mask是一样的,为了避免生成当前词时看到后面的词,具体怎么实现的,后面会有说明
- 依然是加上残差连接传过来的值,然后作为Q进入下一层attention
- 这一层attention接受encoder的输入作为K,V,然后进行attention(这里不是自注意),然后进行下一个Feed Forward计算
- 继续加上残差连接,然后进入一个线性变换最后通过softmax输出当前句子各个词的概率分布
以上便是Transformer的整体结构
Transformer整体结构
真实的Transformer翻译模型的结构并非上述的一个encoder结构连接一个decoder结构,encoder和decoder都各有六个,连接方式如下图所示:
其他问题
transformer训练与测试过程的不同
transformer的训练和测试过程是有所不同的,体现在如下几个方面:
- 训练是运算是并行的,而测试时运算是串行的
- 训练时decoder的输入其实是整个groudtruth,在生成第 i i i个词时通过mask来使训练看不到 i i i之后的词;测试时这个时间步的decoder的输入是之前时间步的decoder生成的内容
- 训练过程中,一个句子各个词的概率分布其实是通过上面的宏观结构一次生成的;在测试的时候上面的结构一次只能生成一个词的概率分布
mask的原理
mask其实让很多人难以理解,包括在attention阶段的mask以及masked multi-head attention,我接下来会写一篇新的博客详细介绍。
参考文档:attention is all you need
整体结构图片来源
其他图片来源