BERT
本文主要记录个人在学习BERT算法的一些内容,以加深个人对此算法的理解。
注意力机制
注意力机制即Attention机制,其核心在于从众多信息中找到对于当前任务目标更重要更关键的信息。
根据注意力机制的作用方式,可以将其简单分成自注意力和互注意力,下面以自注意力为例分析一下注意力的计算方法,下面的动图是一个序列长度为3的计算示例。
将每个输入乘以三个不同的矩阵
图中
Transformer
Position Encoding
position encoding即位置编码,由于attention架构的限制,不同时间的输入在模型看来没有区别,序列的位置信息在输入时丢失了。为解决这个问题,需要在模型输入前加入位置编码(position encoding),即是将输入信息加上一个与位置相关的矩阵,使得位置信息被加入到输入信息中。在学术界,什么形式的位置编码是更优的还有争论,这里给出谷歌团队在transformer模型中使用的位置编码矩阵,其计算公式如下:
Seq2seq模型
seq2seq模型是一种深度学习中常见的模型,其输入一个序列(squence)输出一个序列(sequence),其在自然语言处理中有着广泛的应用。根据输入输出的大小,常见的seq2seq有以下几种:输出序列长度与输入序列长度一致、输出序列长度与输入不一致但长度固定(比如输出大小始终为1)、输出序列长度不固定(由模型决定每次的输出长度)。Transformer就属于最后一种,模型自己来判断输出长度。
一般的seq2seq模型有两部分,通常称为Encoder和Decoder。在Encoder部分,模型会通过计算得到一个和输入相同大小的序列向量,接下来将处理后的结果传递给Decoder,Decoder来输出预测的序列。由于输出长度不固定,一般Decoder(Autoregressive的做法)是一个输入对应一个输出,将上一步的输出作为下一步的输入循环操作,直到某次输出了循环结束的判定符结束循环。 当然,以上这个Decoder做法是传统的Seq2seq的做法,在Transformer出现后,还有另一种Decoder结构(Non autoregressive),不使用循环直接输出,将结束符后的序列丢掉或者再另外训练一个预测模型来控制输入。这种新方法可以更方便的并行以及控制输出长度,但通常结果略差。
Transformer 架构
transformer架构的示意图如下
整体模型架构看起来很复杂,但拆开分析后就可以发现每部分都比较简单直接。整个模型左半边是Encoder,右半边是Decoder。
我们按照计算数据的路径,先分析Encoder部分。首先,输入Inputs会经历一层Input Embedding层,这一部分主要是针对自然语言处理的操作,把one-hot形式的数据转换成词向量的形式,这样更能体现不同词之间的关联性而且能起到降低维度减少计算量的效果。接下来的计算结果与位置编码Positional Encoding相加,相加后的整体作为下一步的输入。接下来,数据会经历N个结构相同的模块,每个模块内部可以分为以下几部分小的结构(参考上面图示可以方便理解):
模块中的第一部分就是刚刚前面介绍的注意力架构,这里使用的是多头注意力(Multi-Head),具体是在计算时对注意力做一些变形,每个输入产生多组Q、K、V(生成几组就是几个头),每组各自计算互不影响,最后把输出拼接在一起作为总输出(可能要再乘一个矩阵来调整形状)。
在经历注意力层后,模型将注意力的输出和输入加在一起,整体作为阶段性的输出结果,这一部分的设计思路类似于残差网络(ResNet)中的残差模块(Residual)。
接下来,模型使用了Normalization层,这里Transformer使用的是的Layer Normalization,即计算每一个数据不同维度的均值和标准差,把均值调整到0标准差调整到1变到标准分布。
在Layer Normalization后,数据经历了一个Feed Forward层,这一部分就是一个全连接层。
Feed Forward层后,Transformer模型再次使用一个残差Residual结构以及一个Layer Normalization结构,得到模块的输出。
整个Transformer的Encoder部分使用了N次上述的模块,最终得到的输出要传递给Decoder做后续计算。
下面,我们继续分析一下Transformer后续的Decoder部分。从图中我们看到对于输入的处理没有变化,先经历一个Embedding层后与位置编码相加。在后续的多次重复模块中,Decoder的结构其实可以看成对Encoder这一部分进行了微调,下面是具体的改动:
- 注意力机制层改为了Masked Multi-Head Attention,这里的Mask是对部分数据进行了遮挡,任一个输出只能看到在这一时刻之前的输入(之后的收入被遮挡掉,原始的Attention会看到每一个时刻的输入),这样做的原因是Decoder看到的输入是一个一个产生的,计算前边的时候看不到后续的输出。
- 在第一次残差结构以及Layer Normalization后,加入了Encoder-Decoder传递信息的模块,这一部分通常称作Cross attention互注意力项,其中注意力的K和V矩阵来自Encoder,Q矩阵来自Decoder ,其余计算与自注意力一致。(原始论文中每个Decoder模块使用的都是Encoder最后的输出,但这个可以自己调整结构)
- 在所有模块的结束后加了一个线性变化以及Softmax层用以输出概率。
BERT
BERT模型结构
BERT全称是Bidirectional Encoder Representation from Transformers,在Bert这一算法诞生之前,自然语言处理基本都是每个任务各自训练各自的模型,而Bert诞生后,人们可以在一个大的数据集中训练好一个模型,然后迁移到其他问题上,这样在减小了每个问题的训练难度的同时也提高了准确度。在原BERT论文发表时,其就在多个NLP任务中取得很远超以往的精确度,也引领了这几年的NLP领域甚至CV领域的发展方向。
以往的预训练模型基本都是单向的(从左到右),这样模型只能单方向地获取上下文信息,Bert模型对此进行了调整,使用了双向的Transformer来构建模型,因此可以生成融合左右上下文信息的深层双向语言特征。具体到模型细节上,BERT其实就是多层的双向Transformer encoder堆叠在一起,把模型加到很深内部结构没做改动。具体到训练使用BERT模型时,先在一个大型数据集(没有标注)上自监督学习预训练,然后把这个训练好的模型参数作为初始化参数拿到其他某一个具体的任务中进行微调(数据有标注),最终得到下游任务的BERT模型。
BERT的预训练
由于在NLP领域没有像ImageNet这样的超大高质量数据集,所以BERT使用了两个自监督性质的任务来预训练,分别是Masked Language Model(MLM)和Next Sentence Prediction(NSP)。实际上,有一些后续研究证明,使用BERT对NLP任务预训练,大量的没有标注的训练集要比少量有标注的训练集训练出的效果更好。
MLM会随机(15%的概率)对序列中的token进行覆盖(替换成mask token),然后预测出原有位置的单词(类似英文考试中完形填空)。为避免出现模型对mask token敏感而对其他token不敏感的问题,实际训练时选中的token使用三种不同的替换方法:约80%替换成mask token,约10%替换成一个随机的其他文本token,约10%保持不变使用原来的token,这样预训练好的BERT模型会对所有的token敏感,能够有效的抽取表征信息。
在一些问答、语义总结推断等问题中,模型需要理解不同句子之间的关系,而MLM倾向于抽取token层次的表征,不能直接获取句子层次的表征。为了使模型能够有能力理解句子间的关系,BERT还使用了NSP来预训练,来预测判断两个句子是否连在一起。具体的做法是:对于每一个训练样例,我们在语料库中挑选出句子A和句子B来组成,50%的时候句子B就是句子A的下一句,剩下50%的时候句子B是语料库中的随机句子。接下来把训练样例输入到BERT模型中,进行二分类的预测。