【Review】Transformer:Attention is all you need

Motivation

在自然语言处理等处理序列信息的场景中,常用的模型是包含Encoder和Decoder两部分的递归神经网络(RNN),有时还会加入注意力机制(attention mechanism)。但是这种网络天然的序列式或者说串行式处理数据的方法(序列后部的计算结果依赖于序列之前的计算),使得计算非常难以并行,即限制的了GPU的运算,又加大了对内存大小的要求。
如果能完全摒弃递归或者卷积的计算,那么网络的可并行程度会大大增加。而Transformer正是基于这个动机,设计了一种基于注意力机制的序列处理网络结构。这个模型不仅并行度高,大大缩减了模型训练时间,而且其中使用的多头注意力机制(multi-head attention mechanism)还能更好地切合序列模型的应用场景,使模型有能力模拟距离较长的输入间的依赖关系。

Structure

Figure1: Attention is all you need
上图显示了Transformer的整体结构,可以看到它遵循了主流的Encoder-Decoder模式,但是这两个部分的具体网络结构却和常见的序列模型不大相同。接下来我会分小块详细阐述我的理解。

Encoder block

Encoder由N个一样的block组成。每个block大致分为两个部分,第一个部分是一个有residual connection的multi-head self-attention module。
输入序列经过embedding和positional encoding后,会被输入Encoder的第一个block。 (后面会提到这两个部分)
之所以叫做multi-head是因为它其实是基于多个并列的参数矩阵的结构,每个矩阵使用点乘的方式来模拟词与词之间不同的关联性。这个设计的初衷是,使用单个的Attention层难以完全捕捉到输入的各个方面的相关性。例如机器翻译句子‘“最终正义彻底战胜了邪恶”,一方面两个主体“正义”和“邪恶”有很强的关联性,另一方面”正义”是“战胜”这个词的主语,也理应把这两个词连在一起理解。使用多个并列的Attention模块可以很好地解决这个问题。

scaled dot-product

并列的每个Head都是一个Scaled Dot-Product Attention模块,结构如下:
Scaled Dot-Product Attention
对于Encoder而言,Q, K, V对应的都是输入序列。假设一个长度为t的输入序列,每个时间点的feature维度为k,通常会把N个这样的序列叠在一起处理,组成一个维度为 [N, t, k] 的三维矩阵。

def Scaled Dot-Product Attention(Q, K, V):
		# mat_re的维度是[N, t, t]。代表序列中词与词之间的关系,可以类似成是一个协方差矩阵。
		mat_re = Q * np.transpose(K) / math.qrt(K.shape[-1])
		# 有了词与词之间的关联性以后,把它作为权重, 用权重和的方式,基于序列所有其他词的特征及他们的关联性,计算/更新每个词的特征。
		# 在第三个维度上做softmax。即,N个序列的每个词都拥有一个概率分布,显示它和同序列中其他词的相关性
		# 用每个词的概率分布作为权重,把同序列中其他词的各维feature按相应权重结合起来,作为这个词新的features,feature的维度与输入时的一致。out维度为[N, t, k]
		out = tf.softmax(mat_re, axis=2) * V 
		return out   

通过以上代码可以明显看出,它不是用递归神经网络的方式处理输入,而是直接通过矩阵乘法计算同序列中,词与词之间的相互关系,以此关系的远近做为权重,重新计算每个词的各维features的值。

multi-head attention

下图为原论文中的多头注意力机制(multi-head attention mechanism)的结构。可以看出,它是由多个并列的Scaled Dot-Product Attention组成的。V(Value), K(Key) 和Q(Query)会经过各自独立的简单线性变换,然后进入相应的Scaled Dot-Product Attention。(每个Scaled Dot-Product Attention会对应三个独立的linear transform matrix)
h个Scaled Dot-Product Attention模块的输出会连接起来,经过线性变换后,输入到下一个block。
Multi-Head Attention

def AttentionHead(Q, K, V, feature_dim):
	# 做线性转换,转换后的维度统一为feature_dim
	q_dim = Q.shape[-1]
	Q = LinearTransform(Q, q_dim, feature_dim)
	k_dim = K.shape[-1]
	K = LinearTransform(K, k_dim, feature_dim)
	v_dim = V.shape[-1]
	V = LinearTransform(V, v_dim, feature_dim)
	# 用之前实现的Scaled Dot-Product Attention来完成相关性计算和features更新
	# 输出维度为[N, t, k]
	out = Scaled Dot-Product Attention(Q, K, V)
	return out

以上是单头(Single-Head)模块,Multi-Head Attention仅需在基础上稍加组装。如下:

def MultiheadAttention(Q, K, V, feature_dim, model_dim, n_head):
	# 生成一系列的单头attention输出,并把它们在feature维度(最后一维)连接起来
	multi_attentions = [np.concatenate(multi_attentions, AttentionHead(Q, K, V, feature_dim), axis=-1) for i in range(n_head)]
	#  通过线性变换把feature维度转变回最初输入的维度, out维度为 [N, t, k]
	out = LinearTransform(x, feature_dim*n_head, model_dim)
	return out

至此,我们已经把transformer的关键组成部分解释了,下面来从更高的模块角度解释它的结构。

the overall Encoder Block

在这里插入图片描述
有了前文对Mlti-Attention机制的了解,Encoder包含的block结构其实已经很容易理解了。它主要由两部分组成:Mlti-Attention子模块和一个简单的正向神经子网络(序列内的不同位置间共享,但不同layer或者说block拥有的position-wise feed-forward network不同)。这两个子模块分别都加上了一个residual连接,并且在输出到下一个子模块前使用了layer normalization。

def Encode_block(x):
	# 每个encoder模块的三个输入都是一样的 
	# x维度[N, t, k]
	# atten 维度[N, t, k]
	atten = MultiheadAttention(x, x, x, feature_dim, model_dim, n_head)
	# residual connection 加上 layer normalization
	x = x + layer_norm(atten)
	# 普通的feed forward 子模块
	# 注意,此处的feed forward对序列每个位置而言都一样,即每一个词位置(t维度)都共用一个feed forward network。
	#当然,每个block中的position-wise feed-forward network有所不同。
	ff_out = position_wise_feed_forward(x)
	# out维度[N, t, k]
	out = x + layer_norm(ff_out)
	return out

position-wise feed-forward network

对于某一个block里的feedforward网络,它会在每一个word对应的feature纬度上做transform。这个sequence的不同word上,使用相同的feedforward网络,对每个word的feature做变换。
例如,现在有N个sequence,每个sequence拥有t个word,每个word的纬度是k维:[N,t, k]。那么feedforward网络会在第三维上进行连接,weight matrix的维度是[k, dims]和[dims, k]。
position-wise 也指它在position纬度上卷积。

Decoder block

在这里插入图片描述
可以看到Decoder block和Encoder block的结构很相似,都使用了Mlti-Attention子模块和一个同位置共享的的正向神经子网络来组成。但因为功能不同,Decoder有以下几个特别点:

  1. Masked Multi-Head Attention
    在训练transformer时,我们只能给Decoder输入当前word之前的信息,不能把需要predict的label(current word)也给它。因此,Decoder的输入序列需要根据当前需要predict的词的位置来进行mask。当前待predict的位置和其之后对应的attention均需要被mask为0。
  2. the input of middle Multi-Head Attention
    中间的Mlti-Attention子模块用于结合Encoder的信息。它的三个输入:Key, Value, Query分别对应encoder output, encoder output 和decoder前一个Multi-Head模块的输出。可以理解为是用encoder学到的信息组成了一个信息库,而decoder学到的关于outputs的信息作为查询,来获取下一个时间点的预测。

positional encoding

以上的章节已经涵盖了transformer的核心结构,但是为了让这个结构更加适合序列应用场景,transformer有一个特别的设计叫做positional encoding。这个设计的初衷是,序列中元素的顺序会使序列有不同的含义,因此模型在模拟使应该利用元素间的顺序。例如,“蛇吃了人”和“人吃了蛇”这两句话有非常不同的含义。但目前为止提到的结构模块都无视了元素间的相对顺序(Multi-Head Attention直接计算每个元素与其他元素间的关系,而position-wise feed-forward network只与元素单独的位置有关)。为了模拟元素间的相对顺序对序列含义的影响,transformer在输入数据经过embedding后,添加了positional encoding这个步骤。
positional encoding的公式如下:
P E ( p o s , 2 i ) = sin ⁡ ( p o s / 100 0 2 i / d m o d e l ) PE(pos, 2i) = \sin(pos / 1000^{2i/d_{model}}) PE(pos,2i)=sin(pos/10002i/dmodel)
P E ( p o s , 2 i + 1 ) = sin ⁡ ( p o s / 100 0 ( 2 i + 1 ) / d m o d e l ) PE(pos, 2i+1) = \sin(pos / 1000^{(2i+1)/d_{model}}) PE(pos,2i+1)=sin(pos/1000(2i+1)/dmodel)
pos代表了元素在序列中的位置,而i是代表了元素的feature的各个维度。因为三角函数是周期的,以此它可以体现元素与元素之间的相对位置关系。

summary

https://mlexplained.com/2017/12/29/attention-is-all-you-need-explained/
basic : https://medium.com/inside-machine-learning/what-is-a-transformer-d07dd1fbec04
attention+en-decoder: https://nlp.stanford.edu/pubs/emnlp15_attn.pdf
http://jalammar.github.io/illustrated-transformer/
https://github.com/tensorflow/nmt
https://skymind.ai/wiki/attention-mechanism-memory-network

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值