传统的seq2seq模型,本质上是一个encoder-decoder结构,编码器和解码器两部分内部都使用RNN网络,编码器和解码器中间通过语义变量相连接;
这样做的目的是通过编码器将整个序列数据的信息编码成一个语义变量,然后解码器通过语义变量和前一时刻解码器的输出来得到当前时刻的输出;传统的seq2seq模型有以下两种结构,区别在于解码器如何使用语义变量
但是传统seq2seq模型中的编码器会将序列中所有时刻的序列数据转换成一个固定长度的语义变量;然后对于不同时刻的输出解码器共用这个语义变量,这就会导致以下问题:
注意力机制的目的是为了克服 encoder 对任意句子只能给出一个固定size的表征,而这个表征在遇到长句时则显得包含信息量不够。
引入注意力机制的seq2seq模型会在根据解码器当前的位置对编码器时刻的输出编码器和解码器之间生成多个语义变量(每一时刻会对应一个语义变量);这样一来每一时刻对应的语义变量能够更加有针对性的保存当前时刻的语义信息;语义变量的数量会随着序列数据长度增加而增加,这就避免了较长的序列数据所产生的数据丢失问题
对于每一个时刻(序列中的一个时刻的数据),注意力机制首先会根据解码器当前的位置计算编码器不同时刻输出对当前数据的影响的权重,然后根据计算得到的权重对不同时刻编码器得到的输出进行加权求和,从而得到解码器当前时刻对应的语义变量
Bahdanau Attention 和 Luong Attention 的区别主要在上文提到的权重的计算方法不同,这里简单给出一张图,不做过多赘述:
Self-Attention 和 传统 Attention 最大的区别在于,Self-Attention 只是在 encoder 或 decoder 内部计算 attention (或者说是权重矩阵)不再参考 decoder端的当前状态,这也是 self 的含义
Self-Attention使用Q,K,V三个矩阵之间的运算代替RNN网络,解决了基于RNN模型的Attention不能并行化的问题,同时每一时刻的输出都能受到任意时刻编码器输出信息的影响。
具体计算方式为:通过Wq,Wk,Wv三个矩阵对序列的所有输入进行变换,得到每个时刻的Q,K,V矩阵;对于每一时刻的输入使用当前时刻节点对应的Q矩阵分别和所有节点的K矩阵进行点乘,然后经过softmax函数进行概率化得到权重矩阵,然后用权重矩阵和V矩阵进行加权求和,最终得到当前时刻的语义向量,Self-Attention 本质上就是一系列矩阵变换
说明:self-attention会丧失序列数据的位置信息,因此在使用self-attention的Transformer会在将位置信息通过位置编码(Positional Encoding)的形式融合进输入数据之中
这里还要说明一点,Self-Attention 本身是非线性变换,原因是Q、K、V三个变量并不是独立的,而是线性相关的(都是由输入向量的embedding变换而来),举个例子就很好理解了
f(x,y)=x,y 是线性变换,而f(x)=xxx是非线性变换,这里三个x分别就代表Q、K、V;因为Q、K、V都是由输入的word embedding得到的,因此Q,K,V 三个矩阵相乘,可以看做是三个以word embedding 为自变量的函数相乘,那么对于这个函数就等价于是一个系数*word embedding^3,这样自然就不是线性变换了
Transformer是谷歌推出的一个文本翻译模型,由上图可知 Transformer也是一个典型的 Encoder-Decoder 结构
Encoder部分是N个相同结构的堆叠,每个结构中又可以细分为如下结构:
Decoder部分同样也是N个相同结构的堆叠,每个结构中又可以细分为如下结构:
注:
在Transformer中,多层堆叠的encoder属于串联结构,将最后一层的输出转化为K,Q矩阵然后传入到每一层decoder的 编码器-解码器注意力结构中,在每一层decoder的编码器-解码器注意力模块中,将解码器中第一个self-attention模块的输出作为V矩阵,和编码器输出的KQ矩阵进行self-atention 并得到最终结果;
将单词转化为向量最简单的方法是 用 one-hot 编码表示每个词语,但是 one-hot 编码有以下问题:
而是 word embedding(词嵌入),是一种维位、稠密、能够表达不同词语语义远近的一种过程 更适合用用于NLP任务 ,词嵌入最常用的方法是 word2vec
注:word embedding 中的参数矩阵也是Transformer需要学习的参数
关于 word2vec 的原理请参考我的这篇文章->#彻底理解# NLP中的word2vec
Self-Attention 没有循环神经网络结构不能记录位置信息,所以加入位置编码,来代表不同位置的信息,最简单的位置编码采用one-hot编码,通过和 embedding 后的输入数据相加得到包含位置编码的输入数据,但当token长度过大时,这种编码方式会令Positional Embedding远大于word embedding,从而降低 word embedding 信息的重要程度
Transformer的 Positional Embedding 有两种方式:相对的编码和绝对编码
正余弦位置编码属于相对位置编码,正余弦位置编码(下式的PE矩阵,第一维长度=token序列长度,第二维长度=emdedding长度)的维度和词向量矩阵的维度相同,计算得到的位置编码向量通过和词向量矩阵的对应位置想加得到包含位置的词向量矩阵;
P E ( p o s , 2 i ) = s i n ( p o s / 1000 0 2 i / d ) P E ( p o s , 2 i + 1 ) = c o s ( p o s / 1000 0 ( 2 i + 1 ) / d ) PE(pos,2i)=sin(pos/10000^{2i/d})\\PE(pos,2i+1)=cos(pos/10000^{(2i+1)/d}) PE(pos,2i)=sin(pos/100002i/d)PE(pos,2i+1)=cos(pos/10000(2i+1)/d)
这种位置编码类似使用二进制对数据进行编码,1,2,3,4,5,6…的二进制编码,数值每增加1第一位反转一次,数值每增加2第二位反转一次,数值每增加4第3位反转一次…(如下图);正弦编码和这个过程你类似
我们会想使用 one-hot 编码初始化位置编码矩阵,然后再和Transformer一起训练(不断更新 positional embedding 矩阵),得到最终的embedding矩阵,其中矩阵的第i行代表第i个位置的embedding,在输入模型前会将 token的word embedding 和 positional embedding 相加然后输入 Transformer模型
其实两者的效果相当,但训练绝对编码会增加额外的训练参数,并且对大雨最大序列长度的token序列无法给出对应的位置编码(压根没有训练这种positional embedding);但是相对位置编码只能给出两个token的距离,并不能记录两个token之间的方向(当前token与前边、后边的token的距离是相同的)
很好理解 类似CNN中的多卷积核的设置,对于每个时刻的节点,设置多个Wq,Wk,Wv矩阵以此生成多个Q,K,V矩阵,每个Q,K,V学习样本的不同的局部特征,最后将多个Q,K,V矩阵得到的信息拼接成一个长向量。
残差网络的作用是:
残差结构可以使梯度在每层网络中传播时,恒大于1,从而避免梯度消失
网络退化是指:过深的网络会使网络结构冗余,从而造成网络性能下降,使用残差网络可以使网络跳过冗余的网络结构
编码器每个单独的结构中包含两个残差网络结构,分别是:
Layer Normalization 是对一个样本内的所有数据进行标准化,常用于处理序列数据的模型中;因为不同样本之间长度可能存在差异,因此RNN和Transformer等处理序列数据的网络不适宜用BN方式进行标准化;
这里在说明一下,Transformer中是对self-attention后的每一个输出节点在每一个batch内进行LN操作
关于几种标准化的对比分析请参考我的这篇文章->#深入理解# Batch Normalization Layer Normalization Instance Normalization Group Normalization 几种标准化的区别
Feedforword 由全连接层和激活函数组成,通过激活函数增加模型的非线性学习能力通过,通过全连接层增加线性学习能力;一般网络都是一个线性学习套一个激活函数增加它的学习能力。
transfomer中的Feedforword 结构一般为两层,经过两层Feedforword 结构数据维度不会改变,是一个先升维再降维的过程
这里补充一点:神经网络中卷积层、全链接层等机构是为了学习样本的线性特征;激活函数是为了学习样本的非线性特征,两者缺一不可
可以看到,Transformer Encoder的输入和输出在形式上还是完全相同,因此,Transformer Encoder同样可以表示为将输入文本中各个字的语义向量转换为相同长度的增强语义向量的一个黑盒
权重共享(weight tying)也叫权重绑定,意思是某些部分共享同一个权重参数
Transformer 在以下两个地方实现了权重共享:
我们知道,Transformer 的 encoder 端和 decoder 端输入的语言是不同的,但是进行词嵌入时词语的one-hot编码只会激活对应语言的embedding(提取参数矩阵中对应行的数据),因此他们可以共用一张大的词表。
以下情况共用一个大词表,能够一定程度上降低参数矩阵的维度(参数矩阵行数);
而像中英这样相差较大的语系,语义共享作用可能不会很大(两种语言有较少交叉的token)。
embedding 层可以说是通过onehot去取到对应的embedding向量,FC层可以说是相反的,通过向量(定义为 x)去得到它可能是某个词的softmax概率,取概率最大(贪婪情况下)的作为预测值。
在FC层的每一行量级相同的前提下,理论上和 x 相同的那一行对应的点积和softmax概率会是最大的
因此,Embedding层和FC层权重共享,Embedding层中和向量 x 最接近的那一行对应的词,会获得更大的预测概率。实际上,Decoder中的Embedding层和FC层有点像互为逆过程;
但是两个地方的参数不能够直接共享,具体通过何种方式在网上没有找到,本人猜测是通过求逆矩阵后实现的参数共享。
通过这样的权重共享可以减少参数的数量,加快收敛。
整个Transformer中包含三种类型的attention:
这三种类型的attention均涉及到mask操作,但目的并不相同。
在Encoder的self-attention,考虑到batch的并行化,通常会进行padding(如果序列长度小于最大序列长度会使用“0”进行补齐操作),因此会对序列中mask=0的token进行mask后在进行attention score的softmax归一化;实现mask的的方式是让这些token在计算权重时,权重为零。
在Decoder中的self-attention,为了避免预测时后续tokens的影所以必须令后续tokens的mask=0,其具体做法为构造一个三角矩阵。
在Decoder中的encode-decoder attention中,涉及到decoder中当前token与整个encoder的sequence的计算,所以encoder仍然需要考虑mask。
综上,无论对于哪个类型的attention,在进行sotmax归一化前,都需要考虑mask操作。
Transformer在基于机器翻译任务进行训练时,优化器采用了Adam,lr的更新采用了自定义的warm-up策略:
L r = d m o d e l − 0.5 ∗ min ( s t e p n u m − 0.5 , s t e p n u m ∗ w a r m u p s t e p s − 1.5 ) Lr=d_{model}^{-0.5}*\min(step_{num}^{-0.5},step_{num}*warmup_{steps}^{-1.5}) Lr=dmodel−0.5∗min(stepnum−0.5,stepnum∗warmupsteps−1.5)
而warm-up也是bert等预训练模型进行fine-tune的标准策略。
常见的attention计算方法分为:
两者参数量和整体计算量相当,但考虑到multi-相乘计算有成熟的加速算法,因此Transformer采用了前者。值得注意的是,Transformer在朴素的相乘计算基础上,引入了缩放因子,即:
s c o r e ( s i , t j ) = < W 1 s i , W 2 t j > ( d k ) score(s_i,t_j)=\frac{
其中 d k d_{k} dk 为各位置的特征维度。,当特征维度很大时,不同矩阵乘法得到的值之间会有很大差距,因此在做softmax时会产生较大波动,为了使softmax后的个分数相对平滑,使用缩放因子消除特征维度对softmax的影响
为了提高Transformer的泛化能力,Transformer训练时采用了大量的正则化策略:
Transformer 在两个地方实现的非线性变换
Transformer 在两个地方实现的线性变换
为什么sine和cos都被用到了?
只用它们的组合才能用sin(x)和cos(x)的组合线性表示sin(x+k), cos(x+k)。
Transformer 的空间复杂度
softmax 的空间复杂度为 O ( n 2 ) O(n^2) O(n2)
相似度计算 等价于大小(n,n) 和(n,d)的矩阵相乘 因此空间复杂度为 O ( n 2 d ) O(n^2d) O(n2d)
加权平均可以看作大小为(n,n)和(n,d)的两个矩阵相乘,得到一个(n,d)的矩阵,因此空间复杂度为 O ( n 2 d ) O(n^2d) O(n2d)
因此,Transformer的空间复杂度为 O ( n 2 d ) O(n^2d) O(n2d)
参考文章: