文章目录
- 来与去
* - 早期序列学习任务的痛点
- Transformer的发展
- 注意力机制
* - 早期的注意力机制
- 注意力机制的统一形式
- 注意力机制的实现
- multi-head attention
- 注意力丢掉的位置信息
- 残差模块
- encoder + decoder
- 损失函数
- Transformer架构一览
来与去
它解决的是哪个领域的问题?这个领域没有它之前是什么样子,有了它之后,这个领域怎么发展?
早期序列学习任务的痛点
机器学习模型有很多种分类方式,其中有一种简单粗暴的:按照输入输出的长度进行分类,分为:
- One to One (如输入图片输出类别)
- One to Many (如输入图片输出图片描述或者是字幕)
- Many to One (如输入微博评论输出情感类别)
- Many to Many (分为M to N(如机器翻译) 与 M to M(自编码学习))
众所周知,RNN在面对长距离的依赖时,反向传播会因为多项激活函数导数相乘(使得接近于0),相当于过长序列的学习能力不足:梯度消失问题。
根本原因:循环结构,就会导致链式求导时链变长,导致连乘。
那么,怎么不用循环结构,也能做到序列学习?
它带来了两个好处:
- 串行改并行了,计算效率和可扩展性大大提升
- 彻底解决了梯度消失问题
Transformer是2017年Google brain提出的模型,用注意力机制做到了这一点
; Transformer的发展
Transformer是一种基本模型,可以在它的基础上做各种魔改
如今各种高效Transformer结构如雨后春笋,连每个月的综述都层出不穷,工业界更是早已大规模应用。
不光是应用于语言(NLP)、语音(ASR)等领域(一开始RNN应用在这些NLP ASR领域)
现在更多的用于视觉领域,甚至融合领域(一个模型又会搞NLP又会搞CV)
注意力机制
早期的注意力机制
2014年在Bahdanau的这篇论文里首次提出,解决的是Neural machine translation领域里当时常用模型Seq2Seq的一个瓶颈问题: 随着encoder长度增长,decoder从encoder里获取的信息仍然非常有限。
encoder到decoder的流转例子,这里给出两幅图:
那有了注意力机制之后呢?
详细看看这个attention layer:
可以看到就是在模型输出的时候,构造了一个包含上下文信息的本质是encoder各向量的 权重和向量,这样decoder能从encoder获取到的信息就多了,而且可以想获取哪个多一点就获取哪个多点,学习权重就可以了,权重高代表那个位置的input vector和decoder这个位置的相关度高。
具体细节:主要是3个部分:
- Alignment scores
e t , i = a ( s t − 1 , h i ) e_{t, i} = a(s_{t-1}, h_i)e t ,i =a (s t −1 ,h i )
h i h_i h i 是encoder的隐状态向量,s t − 1 s_{t-1}s t −1 是上一步的decoder的输出,e t , i e_{t,i}e t ,i 刻画的是这个输入向量和当前位置的输出对齐得有多好 - Weights
α t , i = s o f t m a x ( e t , i ) \alpha_{t, i} = softmax(e_{t, i})αt ,i =s o f t m a x (e t ,i )
因为每个时刻t t t其实可以找多个h i h_i h i 获取想要的信息,所以将上述系数进行归一化,就可知道要去每个encoder的i i i时刻采样的比例了。 - Context vector
c t = ∑ i = 1 T α t , i h i c_t = \sum_{i=1}^T \alpha_{t, i} h_i c t =i =1 ∑T αt ,i h i
上下文信息向量,即 加权隐状态向量和
; 注意力机制的统一形式
注意力机制没有必要只是组合不同步骤的RNN隐状态,而是可以包含任意种类的信息。
这个时候,注意力机制分为3个组件: Q(queries)、K(keys)、V(values)
在早期形式里,Q可以类比为上一步的decoder输出即s t − 1 s_{t-1}s t −1 ,V可以类比为encoder的隐状态向量h i h_i h i ,整个注意力机制可以描述为用s t − 1 s_{t-1}s t −1 去查询key-value对,这里key是向量,value是隐状态向量h i h_i h i
- 查询向量q = s t − 1 q=s_{t-1}q =s t −1 :
匹配数据库里的key来计算一个分数,匹配过程可以看作一个点积:
e q , k i = q ⋅ k i e_{q, k_i} = q \cdot k_i e q ,k i =q ⋅k i - softmax操作,将分数转化为权重
α q , k i = s o f t m a x ( e q , k i ) \alpha_{q, k_i} = softmax(e_q, k_i)αq ,k i =s o f t m a x (e q ,k i ) - 注意力计算
a t t e n t i o n ( q , k , V ) = ∑ i α q , k i V k i attention(q, k, V) = \sum_i \alpha_{q, k_i} V_{k_i}a t t e n t i o n (q ,k ,V )=i ∑αq ,k i V k i
在NMT任务里,输入句子的每个词都对应自身的query, key 和 value向量,是3个参数矩阵(要学习的量)去乘上这个时刻的词的表示(即embedding)得到的,它捕捉的是这个word和序列中其它词的相关性。
注意力机制的实现
代码:
from numpy import array
from numpy import random
from scipy.special import softmax
word1 = array([1, 0, 0])
word2 = array([0, 1, 0])
word3 = array([1, 1, 0])
word4 = array([0, 0, 1])
words = array([word1, word2, word3, word4])
random.seed(2022)
WQ = random.randint(3, size=(3, 3))
WK = random.randint(3, size=(3, 3))
WV = random.randint(3, size=(3, 3))
Q, K, V = words @ WQ, words @ WK, words @ WV
scores = Q @ K.transpose()
weights = softmax(scores / K.shape[1] ** 0.5, axis=1)
attention = weights @ V
print(attention)
手绘的原理阐释…
这个阶段,也叫做self-attention机制,每个词都可以采样下所有词的信息
multi-head attention
有几个head,就有几个W Q , W K , W V WQ,WK,WV W Q ,W K ,W V的集合,也就是说同时存在多个self-attention
这样加强了对不同空间的表示能力
下图为有2个head的multi-head attention,每次注意力机制也会得到两组attention矩阵
得到的多个attention矩阵(下图为Z i Z_i Z i ),再拼接好,乘上一个新的权重矩阵W O W^O W O,得到一个总的attention矩阵Z Z Z:
; 注意力丢掉的位置信息
使用上述方法去取代原来的seq2seq,还需要有一点需要注意,这里每个词得到了它的上下文相关的信息,但是因为使用的是权重和,仍然丢失掉了词语间的位置信息。
所以引入了一个新的组件叫做: 位置编码
位置可以看作一个维度和词向量embedding的维度大小相同的时序编码向量,将时序信息与词编码向量相加即得到带时序信息的词向量表示:
那么这个时序向量具体长什么样呢,有一些例子直观感受下:
下图是一个可以表示从0到19这20个位置(20行)的位置编码向量,每个格子的颜色值从-1到1,一共有512维(512列,根据实际情况的word embedding维度来)
为什么左右看上去像两张图?因为左边是sin生成的右边是cos生辰的。
可以看到这个位置编码的生成没有太多好的理论支撑,所以后续也有非常多的工作试图提供更好的位置编码信息,比如:
不是简单的左右拼接而是交织在一起
残差模块
Residual模块的主要作用就是用来防止深度网络的退化,它至少能保证深层网络的效果不会比更低层的训练效果更差。
下图是transformer里的encoder结构图,可以看到有个Add & Normalize的结构,其实就是残差结构,即输出Z Z Z和输入X X X相加,并且再进行了一次LayerNorm操作
这里LayerNorm的作用也是想尽可能地让深度学习底层网络做的不是无用功,具体可以看详解深度学习中的Normalization
; encoder + decoder
这里的动图详解了encoder + decoder如何工作:
需要注意的是,解码阶段里,最下面的decoder的输入是所有encoder的输出, 并且会给decoder 输入含位置编码的向量。且在decoder阶段,self-attention层只被允许比t时刻早期的被采样到,所以会用到mask技术
给注意力权重加一个mask,白色的部分即赋值为− ∞ – \infin −∞,即代表在输出阶段,t t t时刻的注意力只能采样< = t 时刻的向量信息(很符合常识,因为> t >t >t时刻的向量还没生成)
“Encoder-Decoder Attention”层工作原理非常类似于多头注意力机制,除了它的查询矩阵Q是从下面的decoder传过来的(而不是前一时刻的s t − 1 s_{t-1}s t −1 ),还有K和V是来源于encoder的输出
至于最后的输出怎么转化为词,就靠linear + softmax一条龙了:
损失函数
这里和传统的Seq2Seq一样,即比较两个概率分布(预测词的概率分布 vs 真实词的概率分布)的距离,使用交叉熵损失函数或Kullback-Leibler散度函数
Transformer架构一览
使用6个Encoder, 6个Decoder
对每个Encoder: 拥有两个subnet,1个是Multi-Head Attention层,1个是全连接层
每个subnet都加上了Residual block,意味着每个subnet的输出是:
L a y e r N o r m ( x + s u b N e t ( x ) ) LayerNorm(x + subNet(x))L a y e r N o r m (x +s u b N e t (x ))
对每个Decoder:
同样由6个Decoder模块组成,每个Decoder层由两个subent + 1个额外的subnet组成,即在第一个subnet层多了Masked Multi-Head Attention,防止注意力关注后面还没输出的位置的信息,至于具体的处理方法就是上述提到的使用Masked 位置编码向量来实现,保证位置i i i产生的预测只依赖于已知的< i 的输出
注意力机制,即上述提到的self-attention,没有区别:
这里还要除以一个d k \sqrt{d_k}d k ,为什么要除以d k \sqrt{d_k}d k 呢,是因为点积有可能会产生非常大的数,导致softmax反向传播会把梯度推向一个非常小的区域值,为了对抗这个影响作者决定除以维度大小。
多头注意力机制:
也和上面讲到的没有区别
使用位置编码:
和上面位置编码的第一张图一样,左边sin右边cos,后期有很多优化这方面的工作
Original: https://blog.csdn.net/qq_30219017/article/details/122025555
Author: Alanaker
Title: 深入浅出Transformer原理与实现
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/544966/
转载文章受原作者版权保护。转载请注明原作者出处!