前面介绍NLP领域词向量技术的时候提到了目前最炙手可热的语言模型BERT,而BERT之所以能取得这样的瞩目成就,离不开其采用的超强特征提取器Transformer。
Bert只使用了transformer的Encoder部分
实际上,Transformer技术的早在2017年Google的「Attention is All You Need」论文中就被提出了,在该论文中提出的Transformer是一个被用于机器翻译领域的Encoder-Decoder框架。
在「词向量(2)--从ELMo到Bert」文章中,简单介绍了NLP领域特征抽取器的进化之路,Transformer凭借自身的self-attention结构+位置向量等技术,在抛弃RNN结构后仍旧可以提取文本中序列信息,且易于并行以及堆叠更深的模型。
下面就正式开始,介绍一下今天的主角transformer。
整体结构是由两个编码器堆和解码器堆构成的:
通过以上的结构图可以较完整的看到Transformer的整个结构,其基本的构成单元就是解码器/编码器,通过看源码也可以知道通过for循环结构将各个基本单元进行堆叠构成了以上的复合结构。
下面,让我们由大见小的,逐一脱下Transformer的「神秘外衣」......如果没脱下来,请移步其它直播间(文章)。
编码器部分主要由:Multi-Head Attention与前向传播网络两部分构成,且每一部分的输出都进行残差网络(Residual)+add&Norm处理。
其中,Multi-Head Attention(多头自注意力机制)是其中比较独特、关键的组成。
Encoder内部没有使用RNN,取而代之的是一种基于self-attention(自注意力)的复合结构来捕获序列长距离的依赖,所以下面着重介绍自注意力这部分。
Attention机制最早被应用于NLP的机器翻译领域中,通俗来讲:它可以把两个你想要联系起来的不同序列(Query,Source),通过某种加权的形式进行联系。
为了刻画两个序列的联系(相似)---需要设计一个函数:将目标模块Query和源模块Source联系起来,然后通过一个softmax函数进行归一化得到概率分布。
一般的描述中,习惯用Q、K、V三个向量来当做Attention的输入,暂时不用纠结于这三个具体的来源:
Attention机制的计算一般可以分为三步:
例如:机器翻译任务中从源语言翻译成目标语言,V表示源语言序列,而Q就是待生成的目标语言序列;每产生Q中的一个翻译词时都需要计算与输入V中哪个词最有关系,这便是注意力权重K;Attention层之上,再经过一个softmax预测真实的翻译词。
目前Attention在NLP中已经有广泛的应用。它有一个很大的优点就是通过可视化attention矩阵来告诉大家神经网络在进行任务时关注了序列的哪些部分,可视化的解释模型提取的特征。
简单来说,当Q=K=V时的注意力机制就是:自注意力机制。
Dot-product Attention也是Attention的一种形式,其用于描述QK相似的函数使用点乘(Dot-product)。
而Multi-Head Attention是由多个Scaled Dot-Product Attention单元拼接而成的。
Scaled则是指在softmax计算之前,进行尺度缩放,除以跟Q和K的维度有关的一个量(
),用以防止点积时数值过大。
把很多Scaled Dot-Product Attention单元堆叠,如同卷积一样形成通道形式,然后将结果级联到一起,再经过一个线性映射,这便是Multi-Head Attention的过程。
看一下Transformer中self-attention的计算工程,输入数据我们以矩阵为例,整个计算过程可以用一下公式来表示:
第一步:获得Q/K/V矩阵:
通过阅读相关实现的代码,该过程通过定义一个全连接网络来实现。
Q与K的转置做点乘,除以dk的平方根(Scaled),做一个softmax得到归一化的值,对V做点乘得到输出Z,那么这个Z就是该步的注意力矩阵。
得到self-attention的值后,进行拼接,然后进行一个线性变换,即可获得Multi-Head Attention:
拼接多个Z之后,为了使得输出与输入结构维度相同,所以选择乘以一个W0 做线性变换到达最终的输出Z。
Google 提出的多头 Attention 通过多次计算来捕获不同子空间上的相关信息,Self-Attention 的特点在于无视词之间的距离直接计算依赖关系,在序列内部做 Attention,寻找序列内部的联系,能够学习一个句子的内部结构,实现也较为简单并行可以并行计算。
LayerNorm操作可以描述为:
LayerNorm(x+SubLayer(x))
其中,SubLayer(x)即为自注意力的输出,LayerNorm即为layer normalization操作。
在CNN中接触过一种Batch Normalization[2015年提出]操作,那么与layer normalization[2016年提出]有什么异同呢?
可以简单的理解为对某一次批次的训练数据X的每一列特征进行归一化(后续还有重构的操作,BN只能用于train阶段,不能用于predict阶段-因为预测数据不是批量的),其优点是BN具有提高网络泛化能力的特性,有时可以替代drop out、L2正则项。
layer normalization是对数据集中的一个样本(各个特征的值)进行归一化,所以没有NB只能处理批量数据这样的限制,特别适用于RNN。
一般认为:LN用于RNN效果比较明显,但是在CNN上,不如BN。
Decoder功能相比Encoder相比比较特殊的一点,就是包含一些特殊的self-attention结构,本部分我们着重关注self-attention的不同之点。
Encoder 中只有一种类型 Self-attention 不同的是,Decoder 的 attention 实际包含两种:
为什么需要Mask呢?
为了不泄露序列的未来信息给语言模型,这是训练(单向)语言模型的核心点。
我们知道在一般的RNN结构中,通过序列之前的递归的依赖关系,可以捕获文本的序列信息,且正是这种递归依赖关系,保证的后面序列的信息不会被之前序列信息对应的RNN单元捕获到。
而在Transformer中,我们使用self-attention机制来捕获序列之间的依赖关系,并没有RNN中那种递归依赖的限制,因此,需要构建一个「遮蔽机制」,即MASK。
如上图所示:在生成x1的时候,输入只有起始符<s>,在生成x1-x2的时候,输入的是<s>-x1 .... 以此类推。
为了描述以上这种输入序列-输出序列的依赖关系,在MASK中,直接将两个序列中不相关的元素对应的矩阵权重置为0(上图中空白的上三角)。
通过观察softmax图像可知,当x接近于负无穷的时候,softmax(x)无限接近于0,所以可以将需要隐藏的对应位置的权重初始化为为负无穷即可,具体代码实现可参考相关代码。
Transformer相关的知识,就总结到这里,其中也非常感谢张俊林、苏神等大佬的对Transformer相关知识的分享...
最近一直在忙Embedding召回相关的事宜,所以后续会趁热写一下关于自己了解到的Embedding在业界推荐系统中的使用以及自己的一些小感悟。
9.22
历史文章推荐