文章目录
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}} dksoftmax(QKT)对 Q K T QK^T QKT进行softmax操作,得到n*m维的矩阵,表示输入的每个词在 q i q_i qi上的权重, d k \sqrt d_k dk是避免结果太大的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)=dksoftmax(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}} dksoftmax(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′=dksoftmax(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,我接下来会写一篇新的博客详细介绍。