Transformer在做一个什么事情?
它提出的契机是使用在机器翻译上的。
什么是机器翻译?输入是中文我爱你,经过transformer黑盒的操作,我们得到了英文的翻译结果,I LOVE YOU。
简单来说就是输入,处理,输出。
再细化:
这六个Encoder/Decoder结构上完全相同,参数不同,参数独立训练。
原论文结构:
Encoder结构讲解:
输入部分:
1.Embedding
输入12个字(我爱你…)按字切分,每个字对应512维度字向量。对于字向量我们可以使用word2vector或者随机初始化,当你数据很大时,使用word2vector与随机初始化差别不大。
2.位置嵌入
为什么需要位置编码?先从RNN来讲:
RNN的输入参数U,隐层参数W,输出参数V这是一套参数。对于RNN所有的timesteps它都共享一套参数。也就是说你有100个timesteps(一百个词,一百个字)但是你只有一套参数,更新的时候是更新的一套U、W、V。
RNN展开的这种结构天然的时序关系是很符合的,先处理某些东西,后处理某些东西,但是对于transformer来说,transformer 中Multi-Head Attention,在处理的时候,因为它是有并行化的,一句话所有单词是可以一起处理的。并不是RNN一个一个处理。并行处理增快了速度,但是忽略了单词之间的先后关系。所以transformer相比与RNN就缺少了某些东西告诉模型,某些东西是在前面的,某些东西是在后面的。
对于“爱”这个字,512个维度,对于偶数位置,使用sin,对于奇数位置,使用cos,就是位置编码
得到位置编码之后,我们将位置编码512维度和词向量/字向量维度的512个维度相加,得到一个最终的一个512维度作为我们整个transformer的输入:
为什么位置嵌入会有用?
正余弦这个函数对于同一个位置,同一个position,不同的向量,我们使用不同的sin或cos这个体系的是绝对位置信息。
以“我爱你”为例,pos代表“我”,k代表“爱”,pos+k代表“你”,也就是说“我爱你”中的“你”可以被position中“我”和“爱”线性组合起来。这样的线性组合意味着位置向量中蕴含了相对位置信息。
但是这种相对位置信息会在注意力机制那里消失
注意力机制
1.基本的注意力机制
这是一张在解释注意力机制很经典的图,人类在看一张图片的时候肯定有很关注的部位和不怎么关注的部分,颜色越深表示很受关注,浅的话就不怎么受关注,我们看的时候就可能会先去看这个婴儿脸的部分。进而把人抽象成一句话——“婴儿在干嘛?”。我们在判断“婴儿在干嘛”这句话更关注图片中的哪个区域,和图中哪些区域更相似更关注?
单抽出“婴儿在干嘛”中的“婴儿”,把图片分为左上、左下、右上、右下四个区域。这里的Q代表“婴儿”的某种向量;“Key1、Key2、Key3、Key4”分别代表左上、左下、右上、右下四个区域对应的某种向量;“V1,V2,V3,V4”分别代表左上、左下、右上、右下四个区域对应的某种值向量。首先是婴儿首先和左上、左下、右上、右下分别做点乘得到某个值。
为什么要做点乘?
其实在做相似度计算的时候有三种方式——点乘、MLP、cos相似性。
点乘是一个向量在另一个向量投影的长度,它可以反映两个向量的相似度,也就是说,两个向量越相似,它的点乘就越大。
婴儿和左上、左下、右上、右下四个区域哪个点乘的结果越大,点乘的结果越大,说明距离越靠近,越相似,越关注。
得到婴儿和四个区域的点乘结果后和V矩阵相乘,最后得到的就是Attention Value,就是最后的加权和。
首先输入是“爱”,“爱”和“我”、“不”、“爱”、“你”做F函数(cos,点乘,mlp)得到点乘的结果,做softmax得到a1,a2,a3,a4,做softmax后得到一个相似度,a1,a2,a3,a4相加是为1的。和value值相乘后相加得到最后的结果Attention Value。
2.在Transformer中这怎么操作的
在只有单词向量的情况下,如何获取QKV?在Transformer的情况下怎么操作?
如只有单词向量,如Thinking对应X1,Machines对应X2。X1,X2是加上了Position Encoding。
如有一个四维的单词向量,如何获取Q,K,V。 X 1 X_1 X1乘一个 W q W^q Wq矩阵的参数得到 q 1 q_1 q1, X 1 X_1 X1乘一个 W k W^k Wk的参数得到 k 1 k_1 k1, X 1 X_1 X1乘一个 W v W^v Wv的参数得到 v 1 v_1 v1。 X 1 , X 2 X_1,X_2 X1,X2使用的是同一套矩阵参数。 X 2 X_2 X2得到 q 2 , k 2 , v 2 q_2,k_2,v_2 q2,k2,v2。
计算QK相似度,得到attention值
q
1
和
k
1
q_1和k_1
q1和k1相乘得到112,
q
1
q_1
q1和
k
2
k_2
k2相乘得到96。得到这个值后除以
d
k
\sqrt{d_k}
dk,除以
d
k
\sqrt{d_k}
dk的原因是,qk相乘的值很大,softmax在反向传播的时候值很小,值很小就容易造成梯度的消失。
为什么除以
d
k
\sqrt{d_k}
dk不除以别的值呢?为了把方差控制为1,得到softmax后就可以得到加权和,0.88乘以
v
1
v_1
v1得到
z
1
z_1
z1,0.12乘以
v
2
v_2
v2得到
z
2
z_2
z2
实际代码使用矩阵,方便并行
X矩阵是几个单词一起输入的结果,一行代表一个单词。
我们在上图只使用了一套参数,但实际操作中我们会用多套,如下图我们用了两套:
两套参数分别得到各自的Q、K、V。
为什么这么做呢?原作者发现这样的效果很好,其次,多头相当于把原始信息打到了不同的空间,原来是一个空间,现在是多个空间,保证Transformer可以捕捉到不同子空间的信息,X矩阵流经不同的参数的时候得到不同的Z。
多个头就会有多个输出,需要合在一起输出(把这八个头合在一起输出,就相当于通道维度的连接)
残差和LayNorm
X
1
X_1
X1和
X
2
X_2
X2代表词向量作为输入,和位置编码对位相加,得到新的
X
1
,
X
2
X_1,X_2
X1,X2,经过注意力层,得到输出结果
Z
1
,
Z
2
Z_1,Z_2
Z1,Z2。把经过位置编码的
X
1
和
X
2
X_1和X_2
X1和X2连接起来不动和
Z
Z
Z对位相加,
X
X
X与
Z
Z
Z相加后作为残差的结果,经过LayerNorm后作为输出。
残差
X
X
X作为我们的输入,经过两层网络,这两层网络我们可以归成为一个函数,为
F
(
x
)
F(x)
F(x),经过两层网络后,它的输出结果就是
F
(
X
)
F(X)
F(X)。没有残差网络的话,直接
F
(
X
)
F(X)
F(X)输出就好了,有了残差网络后,需要将
X
X
X原封不动的拿下来,然后用+号和
F
(
X
)
F(X)
F(X)对位相加,
F
(
X
)
+
X
F(X)+X
F(X)+X就是现在真正的输出。
为什么残差结果会有用?
这一张图片和上一张图片一样,A,就是输入,经过B,C两层网络,D就是输出,+号就是残差网络对位相加的过程。
针对上面的网络做链式求导:
我们对损失函数
L
L
L,对
X
A
o
u
t
X_{Aout}
XAout就是
X
X
X的输出做一个求导,
A
A
A的输出其实就是
X
X
X。
D D D的输入就是 F ( X ) F(X) F(X)加上 X X X, F ( X ) F(X) F(X)就是 C ( B ( X A o u t ) ) C(B(X_{Aout})) C(B(XAout)), X X X就是 X A o u t X_{Aout} XAout。
然后将下式带入上式,得到求导结果。
梯度消失一般情况下是因为连乘产生梯度消失(一个梯度趋于零导致其他都趋于零),但是在残差网络结构中,即使这里的连乘再多,即使变为0,但是前面有一个1,确保梯度不会为零,所以缓解了梯度消失的出现。这也是为什么网络结构如果用到了残差网络可以变得更深的原因。
在RNN中,很少见到多个RNN叠加在一起,复杂一点,一个双向LSTM,再复杂一点就是双层双向LSTM(训练时已经很慢了),所以单纯的用RNN网络先把模型做深非常难,再复杂一点就是谷歌的JNMD(使用了很多技巧去训练这个网络,因为太难学了),这时候残差的作用就出来了,因为它缓解了梯度消失,所以它可以把CV模型往深了做,不会出现模型难训练的效果。
Layer Normalization
为什么在这里使用Layer Normalization,而不是使用传统的BN(Base Normalization),BN在NLP中效果很差,所以不用,但在CV效果还行,一般在CV中使用BN,在NLP中使用LN。
什么是BN,以及使用场景
无论是在机器学习还是深度学习中,我们做特征处理的时候都叫Feature Scaling,Feature Scaling就是为了消除量纲的影响,让模型收敛的更快。
BN理解的重点在于针对整个batch中的样本在同一维度特征在做处理。
x
1
,
x
2
,
x
3
.
.
.
x^1,x^2,x^3...
x1,x2,x3...代表不同的样本,每一行代表一个特征(如第一行代表体重,第二行代表身高,第三行代表成绩)。在做BN的时候是针对batch中的所有样本的每一个特征做BN(如体重做BN,身高做BN,成绩做BN),我们针对的是同一个维度。
每个样本的第一行都是体重,所以在做BN的时候,就很容易理解。如果BN用在NLP问题中,就会出现很大的问题。
BN优点:
第一个就是可以解决内部协变量偏移。
- 输入每层的各层分布因为有隐层处理,各层输出之后,作为输入的时候,分布不同,增加了学习难度。BN缓解了这个问题
第二个优点就是缓解了梯度饱和问题(如果使用sigmoid激活函数的话),加快收敛。
BN缺点:
第一个,batch_size较小的时候,效果差
- BN的一个假设是使用整个batch中的样本的均值和方差来模拟全部数据的均值和方差。(假设一个班里有50个人,一个batch有10个人,那么就是用这10个人的均值和方差来模拟这50个人的均值和方差),如果batch_size比较小(比如只有一个人,那么这一个人的均值和方差就不能代表全班的),所以效果不好。
第二个缺点就是BN在RNN中效果比较差。这一点和第一点原因很类似。
- batch_size为10,9个样本长度为5,一个样本长度为20(9个样本的句子长度为5个单词,一个样本句子长度为20个单词),那么我在输入的时候,前5个单词的均值和方差可以用10个样本算出来。但是6到20个单词怎么用均值和方差算出来呢,前9个没有,只有最后一个有,如果只用一个样本去算,就回到了第一个样本里面,batch_size很小的时候,效果很差。这就是BN在RNN效果不好的原因,RNN的输入是动态的,所以它不能有效的得到batch_size中的均值和方差。
为什么使用layer-norm
理解:为什么LayerNorm单独对一个样本的所有单词做缩放可以起到效果。
这里面最后一个句子是20个单词,LN所做的就是针对这20个单词去做缩放,去做它的均值和方差,而BN针对的是对第一个单词(也就是一列)做均值和方差,对第二个单词做均值和方差,这样BN的操作就是在NLP中有问题的。
把BN引申到RNN
如果针对同一位置做均值和方差,比如“我”和“今”做均值和方差,“爱”和“天”去做均值和方差。它默认的是“我”和“天”代表同样的语义信息,“爱”和“天”代表同样的语义信息。BN在对
x
1
,
x
2
,
x
3
x^1,x^2,x^3
x1,x2,x3的那张图做均值方差的时候,第一行做均值方差是体重,第二行做均值方差是身高。对比上图,我们不能这么类比过来,“我”和“今”是不同的语义信息,所以不能这么类比。
而LN针对的是所有单词“我爱中国共产党”去做均值和方差,它的假设是认为“我爱中国共产党”在同样的一个语义信息里面。我们在获取“我爱中国共产党”这个句向量的时候,一个常规的做法是去做加权的词向量,加权词向量一个基本的假设是认为“我爱中国共产党”这句话中所有的词是在一个语义信息中的,所以可以认为它是理解的。
前馈神经网络
是这张图的上半部分,
z
1
z_1
z1通过一个Feed Forward,
z
2
z_2
z2通过一个Feed Forward,这里是两层的全连接。然后再过一个残差和Normalization这就是前馈神经网络。
这就是整个Encoder的过程。
Decoder
Decoder和Encoder一样,由完全相同的n个大模块堆叠而成。
比Encoder多了一个注意力层。
需要对当前单词和之后的单词做mask,抹掉。
为什么需要做mask呢?
我在输入"LOVE"这个单词的时候,输出为"YOU",如果decoder的输入没有mask,
只是和Encoder一模一样的多头注意力机制,那么就会是下图的操作:
那么"S",“I”,“LOVE”,“YOU”,"NOW"为生成"YOU"这个预测结果都会提供信息,但是生成的模型在预测阶段就会出现问题。
在预测阶段,如果是同样预测“YOU”,是没有ground truth,是没有后面的“YOU”和“NOW”这个信息的,模型看不见未来的单词,如果不mask掉"YOU"和"NOW",这个模型在训练的时候和预测的时候存在gap,也就是说在训练的时候可以看到后两个单词,但是在预测的时候却看不到,这个模型的效果就不好。
训练的时候把“YOU”和“NOW”给到的信息叉掉,我们不用它,不看到它,在预测的时候也看不到它,那么gap就消失了,保持了一致性。这是Masked Multi-Head Attention。
交互层
Encoder和Decoder是如何交互的?
所有Encoder的输出,生成某种值,和每一个Decoder去做交互。
这里有两个Encoder,输出得到的值生成K,V矩阵(Encoder生成的是K,V矩阵),Decoder生成的是Q矩阵,多头注意力机制一定是有Q,K,V三个矩阵,Q来自于本身,K,V来自Encoder。