背景
在NLP自然语言处理学习或者发展过程中,在13年word2vec word embedding后,人们发现一个单词通过Word Embedding表示,很容易找到语义相近的单词,但单一词向量表示,不可避免一词多义问题。于是迎来Google的ELMO transformer BERT等动态表征模型,BERT模型更是刷新了GLUE benchmark的11项测试任务最高记录。
本篇博客将试图理清 word2vec 到 ELMO GPT 再到 BERT与attention transformer 几个模型的思路,上图是上面出现的几个关键词的关系。
word2vec
先来简单回顾一下word2vec模型,在上一篇博客 中有提到为什么会使用embedding以及模型的训练方法,这里再简单复述一下。
w2v结构与训练方法
word2vec分为skip-gram与cbow两种,CBOW模型是将中心词的上下文作为输入来进行预测,而Skip-gram是根据中心词来预测其上下文单词。训练完成之后可以得到一下类似的结果。
训练方法如下,取语料库句子中顺序单词,用上一个单词预测下一个,如下图
假如我们想要学习的词向量维度为300,则需要将隐层的神经元个数设置为300。隐层的权重矩阵就是词向量,我们模型学习到的就是隐层的权重矩阵。
得到了单词的embedding向量,我们就可以用其去代替之前诟病已久的one-hot向量,去完成一些NLP任务,比如QA 文本分类 文章相似度等等。这项技术也应用到了很多领域,比如推荐。
NLP任务在使用Word Embedding的时候也类似图像有两种做法,一种是Frozen,就是Word Embedding那层网络参数固定不动;另外一种是Fine-Tuning,就是Word Embedding这层参数使用新的训练集合训练也需要跟着训练过程更新掉。
w2v总结
毫不夸张的说word2vec是embedding界开天辟地的大事件,从这之后一切事物都可embedding 了,在这之后的任何embedding 都能看到word2vec的影子。
不过w2v也存在问题---多义词问题。我们知道,多义词是自然语言中经常出现的现象,也是语言灵活性和高效性的一种体现。多义词对Word Embedding来说有什么负面影响?
如上图所示,比如多义词Bank,有两个常用含义,但是Word Embedding在对bank这个单词进行编码的时候,是区分不开这两个含义的,因为它们尽管上下文环境中出现的单词不同,但是在用语言模型训练的时候,不论什么上下文的句子经过word2vec,都是预测相同的单词bank,而同一个单词占的是同一行的参数空间,这导致两种不同的上下文信息都会编码到相同的word embedding空间里去。
所以word embedding无法区分多义词的不同语义,后面ELMO等动态模型设计初衷就是为了解决这个问题。
ELMO (Embedding from Language Models)
首先我们来看看ELMO模型的设计动机,作者认为一个预训练的词表示应该能够包含丰富的句法和语义信息,并且能够对多义词进行建模。而传统的词向量(例如word2vec)是上下文无关的。例如下面"apple"的例子,这两个"apple"根据上下文意思是不同的,但是在word2vec中,只有apple一个词向量,无法对一词多义进行建模。
ELMO的本质思想是:事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分,不过这没关系。在实际使用Word Embedding的时候,单词已经具备了特定的上下文了,这个时候可以根据上下文单词的语义去调整单词的Word Embedding表示,这样经过调整后的Word Embedding更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。
所以ELMO本身是个根据当前上下文对Word Embedding动态调整的思路。
ELMO结构
在EMLo中,使用的是一个双向的LSTM语言模型,由一个前向和一个后向语言模型构成,目标函数就是取这两个方向语言模型的最大似然。
Forward language model采用的是利用前面的信息估计后面的信息的方式,Backward language model采用的是利用后面的信息估计前面的信息的方式。
ELMO采用了典型的两阶段过程,第一个阶段是利用语言模型进行预训练;第二个阶段是在做下游任务时,从预训练网络中提取对应单词的网络各层的Word Embedding作为新特征补充到下游任务中。
上图展示的是其预训练过程,它的网络结构采用了双层双向LSTM,目前语言模型训练的任务目标是根据单词 W_i 的上下文去正确预测单词 W_i , W_i之前的单词序列Context-before称为上文,之后的单词序列Context-after称为下文。
图中左端的前向双层LSTM代表正方向编码器,输入的是从左到右顺序的除了预测单词外 W_i的上文Context-before;
右端的逆向双层LSTM代表反方向编码器,输入的是从右到左的逆序的句子下文Context-after;每个编码器的深度都是两层LSTM叠加。
这个网络结构其实在NLP中是很常用的。使用这个网络结构利用大量语料做语言模型任务就能预先训练好这个网络,如果训练好这个网络后,输入一个新句子S_{new} ,句子中每个单词都能得到对应的三个Embedding:最底层是单词的Word Embedding,往上走是第一层双向LSTM中对应单词位置的Embedding,这层编码单词的句法信息更多一些;再往上走是第二层LSTM中对应单词位置的Embedding,这层编码单词的语义信息更多一些。也就是说,ELMO的预训练过程不仅仅学会单词的Word Embedding,还学会了一个双层双向的LSTM网络结构,而这两者后面都有用。
ELMO模型使用方法
上面介绍的是ELMO的第一阶段:预训练阶段。那么预训练好网络结构后,如何给下游任务使用呢
上图展示了下游任务的使用过程,比如我们的下游任务仍然是QA问题,此时对于问句X,我们可以先将句子X作为预训练好的ELMO网络的输入,这样句子X中每个单词在ELMO网络中都能获得对应的三个Embedding,之后给予这三个Embedding中的每一个Embedding一个权重a,这个权重可以学习得来,根据各自权重累加求和,将三个Embedding整合成一个。然后将整合后的这个Embedding作为X句在自己任务的那个网络结构中对应单词的输入,以此作为补充的新特征给下游任务使用。
对于上图所示下游任务QA中的回答句子Y来说也是如此处理。因为ELMO给下游提供的是每个单词的特征形式,所以这一类预训练的方法被称为“Feature-based Pre-Training”。
ELMO总结与对比
与之前不同的是,EMLO模型不仅去学习单词特征,还有句法特征与语义特征。后两者的特征是来自LSTM的隐含输出向量。这里的模型目标是预测对应位置的下一个单词(也就是T1的向量应该预测出E2的单词)。
模型训练完,我们就可以得到单词,句法以及语义特征。也就是在一个句子中每一个单词将会对应三个向量,然后三者共同构建成下游任务的输入。比如下游任务就是一个对话系统。
总结就是ELMo模型是通过语言模型任务得到句子中的单词的向量,这个向量是结合左向右向的信息,但是仅仅是拼接的实现。
在后面来看,ELMO对比BERT有以下不足:
- LSTM抽取特征能力远弱于Transformer
- 拼接方式双向融合特征 融合能力偏弱
那么站在现在这个时间节点看,ELMO有什么值得改进的缺点呢?首先,一个非常明显的缺点在特征抽取器选择方面,ELMO使用了LSTM而不是新贵Transformer,Transformer是谷歌在17年做机器翻译任务的“Attention is all you need”的论文中提出的,引起了相当大的反响,很多研究已经证明了Transformer提取特征的能力是要远强于LSTM的。如果ELMO采取Transformer作为特征提取器,那么估计Bert的反响远不如现在的这种火爆场面。另外一点,ELMO采取双向拼接这种融合特征的能力可能比Bert一体化的融合特征方式弱,但是,这只是一种从道理推断产生的怀疑,目前并没有具体实验说明这一点。
我们如果把ELMO这种预训练方法和图像领域的预训练方法对比,发现两者模式看上去还是有很大差异的。除了以ELMO为代表的这种基于特征融合的预训练方法外,NLP里还有一种典型做法,这种做法和图像领域的方式就是看上去一致的了,一般将这种方法称为“基于Fine-tuning的模式”,而GPT就是这一模式的典型开创者。
Transformer (Attention is all you need)
Attention Is All You Need是一篇Google提出的将Attention思想发挥到极致的论文。这篇论文中提出一个全新的模型,叫 Transformer,抛弃了以往深度学习任务里面使用到的 CNN 和 RNN ,目前大热的Bert就是基于Transformer构建的,这个模型广泛应用于NLP领域,例如机器翻译,问答系统,文本摘要和语音识别等等方向。
Transformer整个网络结构完全是由Attention机制组成。更准确地讲,Transformer由且仅由self-Attenion和Feed Forward Neural Network组成。一个基于Transformer的可训练的神经网络可以通过堆叠Transformer的形式进行搭建,作者的实验是通过搭建编码器和解码器各6层,总共12层的Encoder-Decoder,并在机器翻译中取得了BLEU值得新高。
作者采用Attention机制的原因是考虑到RNN(或者LSTM,GRU等)的计算限制为是顺序的,也就是说RNN相关算法只能从左向右依次计算或者从右向左依次计算,这种机制带来了两个问题:
- 时间片t的计算依赖 t-1时刻的计算结果,这样限制了模型的并行能力;
- 顺序计算的过程中信息会丢失,尽管LSTM等门机制的结构一定程度上缓解了长期依赖的问题,但是对于特别长期的依赖现象,LSTM依旧无能为力。
Transformer的提出解决了上面两个问题,首先它使用了Attention机制,将序列中的任意两个位置之间的距离是缩小为一个常量;其次它不是类似RNN的顺序结构,因此具有更好的并行性,符合现有的GPU框架。
论文中给出Transformer的定义是:Transformer is the first transduction model relying entirely on self-attention to compute representations of its input and output without using sequence aligned RNNs or convolution。
Transformer结构
Transformer的本质上是一个Encoder-Decoder的结构,如下图
如论文中所设置的,编码器由6个编码block组成,同样解码器是6个解码block组成。与所有的生成模型相同的是,编码器的输出会作为解码器的输入。如下图所示,
Encoder层
我们继续分析每个encoder的详细结构,Encoder结果如下图
在Transformer的encoder中,数据首先会经过一个叫做‘self-attention’的模块得到一个加权之后的特征向量Z,这个Z便是论文公式1中的Attention(Q,K,V) :
得到Z之后,它会被送到encoder的下一个模块,即Feed Forward Neural Network。这个全连接有两层,第一层的激活函数是ReLU,第二层是一个线性激活函数,可以表示为:
Decoder的结构如下图所示,它和encoder的不同之处在于Decoder多了一个Encoder-Decoder Attention,两个Attention分别用于计算输入和输出的权值:
- Self-Attention:当前翻译和已经翻译的前文之间的关系;
- Encoder-Decnoder Attention:当前翻译和编码的特征向量之间的关系。
现在我们知道了模型的主要组件,接下来我们看下模型的内部细节。首先,模型需要对输入的数据进行一个embedding操作,也可以理解为类似w2v的操作,enmbedding结束之后,输入到encoder层,self-attention处理完数据后把数据送给前馈神经网络,前馈神经网络的计算可以并行,得到的输出会输入到下一个encoder。
Self-Attention
详细看一下self-attention,其思想和attention类似,但是self-attention是Transformer用来将其他相关单词的“理解”转换成我们正在处理的单词的一种思路,我们看个例子: The animal didn't cross the street because it was too tired
这里的it到底代表的是animal还是street呢,对于我们来说能很简单的判断出来,但是对于机器来说,是很难判断的,self-attention就能够让机器把it和animal联系起来,接下来我们看下详细的处理过程。
- 首先,self-attention会计算出三个新的向量,在论文中,向量的维度是512维,我们把这三个向量分别称为Query、Key、Value,这三个向量是用embedding向量与一个矩阵相乘得到的结果,这个矩阵是随机初始化的,维度为(64,512)注意第二个维度需要和embedding的维度一样,其值在BP的过程中会一直进行更新,得到的这三个向量的维度是64低于embedding维度的。
那么Query、Key、Value这三个向量又是什么呢?这三个向量对于attention来说很重要,当理解了下文后,将会明白这三个向量扮演者什么的角色。
- 计算self-attention的分数值,该分数值决定了当我们在某个位置encode一个词时,对输入句子的其他部分的关注程度。这个分数值的计算方法是Query与Key做点成,以下图为例,首先我们需要针对Thinking这个词,计算出其他词对于该词的一个分数值,首先是针对于自己本身即q1·k1,然后是针对于第二个词即q1·k2
- 接下来,把点成的结果除以一个常数,这里我们除以8,这个值一般是采用上文提到的矩阵的第一个维度的开方即64的开方8,当然也可以选择其他的值,然后把得到的结果做一个softmax的计算。得到的结果即是每个词对于当前位置的词的相关性大小,当然,当前位置的词相关性肯定会会很大。
- 下一步就是把Value和softmax得到的值进行相乘,并相加,得到的结果即是self-attetion在当前节点的值。
在实际的应用场景,为了提高计算速度,我们采用的是矩阵的方式,直接计算出Query, Key, Value的矩阵,然后把embedding的值与三个矩阵直接相乘,把得到的新矩阵Q与K相乘,乘以一个常数,做softmax操作,最后乘上V矩阵
这种通过 query 和 key 的相似性程度来确定 value 的权重分布的方法被称为scaled dot-product attention。上图最后得到的Z即为上节提到的Attention值。
总结归纳一下,Query,Key,Value的概念取自于信息检索系统,举个简单的搜索的例子来说。当你在某电商平台搜索某件商品(年轻女士冬季穿的红色薄款羽绒服)时,你在搜索引擎上输入的内容便是Query,然后搜索引擎根据Query为你匹配Key(例如商品的种类,颜色,描述等),然后根据Query和Key的相似度得到匹配的内容(Value)。
self-attention中的Q,K,V也是起着类似的作用,在矩阵计算中,点积是计算两个矩阵相似度的方法之一,因此式Attention中使用了QK^T进行相似度的计算。接着便是根据相似度进行输出的匹配,这里使用了加权匹配的方式,而权值就是query与key的相似度。
Muti-Headed Attention
这篇论文更牛逼的地方是给self-attention加入了另外一个机制,被称为“multi-headed” attention,该机制理解起来很简单,就是说不仅仅只初始化一组Q、K、V的矩阵,而是初始化多组,tranformer是使用了8组,所以最后得到的结果是8个矩阵。
这给我们留下了一个小的挑战,前馈神经网络没法输入8个矩阵呀,这该怎么办呢?所以我们需要一种方式,把8个矩阵降为1个,首先,我们把8个矩阵连在一起,这样会得到一个大的矩阵,再随机初始化一个矩阵和这个组合好的矩阵相乘,最后得到一个最终的矩阵。
这就是multi-headed attention的全部流程了,这里其实已经有很多矩阵了,我们把所有的矩阵放到一张图内看一下总体的流程。
- 将数据X分别输入到图13所示的8个self-attention中,得到8个加权后的特征矩阵Z_i,iin{1,2,..8} 。
- 将8个Z_i按列拼成一个大的特征矩阵;
- 特征矩阵经过一层全连接后得到输出Z。
Positional Encoding
到目前为止,transformer模型中还缺少一种解释输入序列中单词顺序的方法。为了处理这个问题,transformer给encoder层和decoder层的输入添加了一个额外的向量Positional Encoding,维度和embedding的维度一样,这个向量采用了一种很独特的方法来让模型学习到这个值,这个向量能决定当前词的位置,或者说在一个句子中不同的词之间的距离。这个位置向量的具体计算方法有很多种,论文中的计算方法如下
其中pos是指当前词在句子中的位置,i是指向量中每个值的index,可以看出,在偶数位置,使用正弦编码,在奇数位置,使用余弦编码。最后把这个Positional Encoding与embedding的值相加,作为输入送到下一层。
Layer normalization
在transformer中,每一个子层(self-attetion,ffnn)之后都会接一个残缺模块,并且有一个Layer normalization
残缺模块相信大家都很清楚了,这里主要讲解下Layer normalization。
Normalization有很多种,但是它们都有一个共同的目的,那就是把输入转化成均值为0方差为1的数据。我们在把数据送入激活函数之前进行normalization(归一化),因为我们不希望输入数据落在激活函数的饱和区。
说到 normalization,那就肯定得提到 Batch Normalization。BN的主要思想就是:在每一层的每一批数据上进行归一化。我们可能会对输入数据进行归一化,但是经过该网络层的作用后,我们的数据已经不再是归一化的了。随着这种情况的发展,数据的偏差越来越大,我的反向传播需要考虑到这些大的偏差,这就迫使我们只能使用较小的学习率来防止梯度消失或者梯度爆炸。
BN的具体做法就是对每一小批数据,在批这个方向上做归一化。如下图所示:
可以看到,右半边求均值是沿着数据 batch_size的方向进行的,其计算公式如下:
那么什么是 Layer normalization 呢?它也是归一化数据的一种方式,不过 LN 是在每一个样本上计算均值和方差,而不是BN那种在批方向计算均值和方差,下面是LN图解与公式:
Decoder层
上图是transformer的一个详细结构,相比本文一开始结束的结构图会更详细些,接下来,我们会按照这个结构图讲解下decoder部分。
可以看到decoder部分其实和encoder部分大同小异,不过在最下面额外多了一个masked mutil-head attetion,这里的mask也是transformer一个很关键的技术,我们一起来看一下。
Mask
mask 表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 padding mask
和 sequence mask
。
其中,padding mask
在所有的 scaled dot-product attention 里面都需要用到,而 sequence mask
只有在 decoder 的 self-attention 里面用到。
- Padding Mask 什么是 padding mask 呢?因为每个批次输入序列长度是不一样的也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充 0。但是如果输入的序列太长,则是截取左边的内容,把多余的直接舍弃。因为这些填充的位置,其实是没什么意义的,所以我们的attention机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。 具体的做法是,把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 softmax,这些位置的概率就会接近0 而我们的 padding mask 实际上是一个张量,每个值都是一个Boolean,值为 false 的地方就是我们要进行处理的地方。
- Sequence mask
文章前面也提到,sequence mask 是为了使得 decoder 不能看见未来的信息。也就是对于一个序列,在 time_step 为 t 的时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。因此我们需要想一个办法,把 t 之后的信息给隐藏起来。
那么具体怎么做呢?也很简单:产生一个上三角矩阵,上三角的值全为0。把这个矩阵作用在每一个序列上,就可以达到我们的目的。
- 对于 decoder 的 self-attention,里面使用到的 scaled dot-product attention,同时需要padding mask 和 sequence mask 作为 attn_mask,具体实现就是两个mask相加作为attn_mask。
- 其他情况,attn_mask 一律等于 padding mask。
输出层
当decoder层全部执行完毕后,怎么把得到的向量映射为我们需要的词呢?
只需要在结尾再添加一个全连接层和softmax层,假如我们的词典是1w个词,那最终softmax会输入1w个词的概率,概率值最大的对应的词就是我们最终的结果。
Transformer总结
优点:
- 虽然Transformer最终也没有逃脱传统学习的套路,Transformer也只是一个全连接(或者是一维卷积)加Attention的结合体。但是其设计已经足够有创新,因为其抛弃了在NLP中最根本的RNN或者CNN并且取得了非常不错的效果,算法的设计非常精彩,值得每个深度学习的相关人员仔细研究和品位。
- Transformer的设计最大的带来性能提升的关键是将任意两个单词的距离是1,这对解决NLP中棘手的长期依赖问题是非常有效的。
- Transformer不仅仅可以应用在NLP的机器翻译领域,甚至可以不局限于NLP领域,是非常有科研潜力的一个方向。(4)算法的并行性非常好,符合目前的硬件(主要指GPU)环境。
缺点:
- 粗暴的抛弃RNN和CNN虽然非常炫技,但是它也使模型丧失了捕捉局部特征的能力,RNN CNN Transformer的结合可能会带来更好的效果。
- Transformer失去的位置信息其实在NLP中非常重要,而论文中在特征向量中加入Position Embedding也只是一个权宜之计,并没有改变Transformer结构上的固有缺陷。
GPT (Generative Pre-Training)
GPT是“Generative Pre-Training”的简称,从名字看其含义是指的生成式的预训练。GPT也采用两阶段过程,第一个阶段是利用语言模型进行预训练,第二阶段通过Fine-tuning的模式解决下游任务。
上图展示了GPT的预训练过程,其实和ELMO是类似的,主要不同在于两点:
- 首先,特征抽取器不是用的RNN,而是用的Transformer,上面提到过它的特征抽取能力要强于RNN,这个选择很明显是很明智的;
- 其次,GPT的预训练虽然仍然是以语言模型作为目标任务,但是采用的是单向的语言模型,所谓“单向”的含义是指:语言模型训练的任务目标是根据W_i, W_i同时使用了上文和下文,而GPT则只采用Context-before这个单词的上文来进行预测,而抛开了下文。
这个选择现在看不是个太好的选择,原因很简单,它没有把单词W_i的下文融合进来,这限制了其在更多应用场景的效果,比如阅读理解这种任务,在做任务的时候是可以允许同时看到上文和下文一起做决策的。如果预训练时候不把单词的下文嵌入到Word Embedding中,是很吃亏的,白白丢掉了很多信息。
上图展示了GPT在第二阶段如何使用。
首先,对于不同的下游任务来说,本来你可以任意设计自己的网络结构,现在不行了,要向GPT的网络结构看齐,把任务的网络结构改造成和GPT的网络结构是一样的。
然后,在做下游任务的时候,利用第一步预训练好的参数初始化GPT的网络结构,这样通过预训练学到的语言学知识就被引入到你手头的任务里来了,这是个非常好的事情。再次,你可以用手头的任务去训练这个网络,对网络参数进行Fine-tuning,使得这个网络更适合解决手头的问题。
GPT论文给了一个改造施工图如上,其实也很简单:
- 对于分类问题,不用怎么动,加上一个起始和终结符号即可;
- 对于句子关系判断问题,比如Entailment,两个句子中间再加个分隔符即可;
- 对文本相似性判断问题,把两个句子顺序颠倒下做出两个输入即可,这是为了告诉模型句子顺序不重要;
- 对于多项选择问题,则多路输入,每一路把文章和答案选项拼接作为输入即可。
从上图可看出,这种改造还是很方便的,不同任务只需要在输入部分施工即可。
GPT总结
其实GPT的工作已经相当的成熟,与bert相比,区别并不是很大。
那么站在现在的时间节点看,GPT有什么值得改进的地方呢?其实最主要的就是那个单向语言模型,如果改造成双向的语言模型任务可能会好很多。当然,即使如此GPT也是非常非常好的一个工作,跟Bert比,其作者炒作能力亟待提升。
Bert (Bidirectional Encoder Representations from Transformers)
BERT,基于Transformer的双向语言模型。同样,BERT采用跟GPT一样的“Fine-Tuning Approaches”预训练模式,分两个阶段:
第一阶段采用双层双向Transformer模型通过MLM和NSP两种策略进行预训练;
第二阶段采用Fine-Tuning的模式应用到下游任务。
有人戏称:Word2Vec ELMo GPT = BERT,不过也并无道理,BERT吸收了这些模型的优点:“完形填空”的学习模式迫使模型更多依赖上下文信息预测单词,赋予了模型一定的纠错能力;Transformer模型相比LSTM模型没有长度限制问题,具备更好的能力捕获上下文信息特征;相比单向训练模式,双向训练模型捕获上下文信息会更加全面;
和GPT的最主要不同在于在预训练阶段采用了类似ELMO的双向语言模型,当然另外一点是语言模型的数据规模要比GPT大。
- 预训练:1)Masked LM的任务。就是随便盖住一个单词来预测。2)是否是下一句。判断两句时候相连的二分类问题。
- 网络结构:全连的网络结构(与GPT比较)。
- 输入向量:词向量,段落(segment)向量与位置(position)向量。与transformer不同的是,这些向量都要模型学习。
- 输入的格式:输入的格式xuewei的变化。增加了cls与sep。
Bert结构与预训练
BERT采用了MLM和NSP两种策略用于模型预训练。为了证明这两种策略的效果,谷歌额外增加了两组对照实验。
对照组一:No NSP,保留MLM,但没有NSP;
对照组二:LTR & No NSP,没有MLM和NSP,替换成一个Left-to-Right(LTR)模型,甚至为了增强可信性,在对照组二的基础上增加一个随机初始化的BiLSTM。实验数据表明,BERT采用MLM&NSP策略完胜其他。
MLM,Masked LM。对输入的单词序列,随机地掩盖15%的单词,然后对掩盖的单词做预测任务。相比传统标准条件语言模型只能left-to-right或right-to-left单向预测目标函数,MLM可以从任意方向预测被掩盖的单词。不过这种做法会带来两个缺点:
1.预训练阶段随机用符号MASK替换掩盖的单词,而下游任务微调阶段并没有Mask操作,会造成预训练跟微调阶段的不匹配;
2.预训练阶段只对15%被掩盖的单词进行预测,而不是整个句子,模型收敛需要花更多时间。
对于第二点,作者们觉得效果提升明显还是值得;而对于第一点,为了缓和,15%随机掩盖的单词并不是都用符号MASK替换,掩盖单词操作进行了以下改进,同时举例“my dog is hairy”挑中单词“hairy”。
80%用符号MASK替换:my dog is hairy -> my dog is MASK 10%用其他单词替换:my dog is hairy -> my dog is apple 10%不做替换操作:my dog is hairy -> my dog is hairy
NSP,Next Sentence Prediction。许多重要的下游任务譬如QA、NLI需要语言模型理解两个句子之间的关系,而传统的语言模型在训练的过程没有考虑句对关系的学习。NSP,预测下一句模型,增加对句子A和B关系的预测任务,50%的时间里B是A的下一句,分类标签为IsNext,另外50%的时间里B是随机挑选的句子,并不是A的下一句,分类标签为NotNext。
Input = CLS the man went to MASK store SEP he brought a gallon MASK milk SEP Label = IsNext Input = CLS the man went to MASK store SEP penguin MASK are flight ##less birds SEP Label = NotNext
Mask LM
Masked双向语言模型向上图展示这么做:随机选择语料中15%的单词,把它抠掉,也就是用Mask掩码代替原始单词,然后要求模型去正确预测被抠掉的单词。
但是这里有个问题:训练过程大量看到mask标记,但是真正后面用的时候是不会有这个标记的,这会引导模型认为输出是针对mask这个标记的,但是实际使用又见不到这个标记,这自然会有问题。为了避免这个问题,Bert改造了一下,15%的被上天选中要执行mask替身这项光荣任务的单词中,只有80%真正被替换成mask标记,10%被狸猫换太子随机替换成另外一个单词,10%情况这个单词还待在原地不做改动。这就是Masked双向语音模型的具体做法。
Next Sentencen Prediction
“Next Sentence Prediction”,指的是做语言模型预训练的时候,分两种情况选择两个句子,一种是选择语料中真正顺序相连的两个句子;另外一种是第二个句子从语料库中抛色子,随机选择一个拼到第一个句子后面。我们要求模型除了做上述的Masked语言模型任务外,附带再做个句子关系预测,判断第二个句子是不是真的是第一个句子的后续句子。之所以这么做,是考虑到很多NLP任务是句子关系判断任务,单词预测粒度的训练到不了句子关系这个层级,增加这个任务有助于下游句子关系判断任务。所以可以看到,它的预训练是个多任务过程。这也是Bert的一个创新。
Bert预训练输入
BERT的输入是一个线性序列,支持单句文本和句对文本,句首用符号CLS表示,句尾用符号SEP表示,如果是句对,句子之间添加符号SEP。输入特征,由Token向量、Segment向量和Position向量三个共同组成,分别代表单词信息、句子信息、位置信息。把单词对应的三个embedding叠加,就形成了Bert的输入。
Fine-Tuning
对于种类如此繁多而且各具特点的下游NLP任务,Bert如何改造输入输出部分使得大部分NLP任务都可以使用Bert预训练好的模型参数呢?
通常而言,绝大部分NLP问题可以归入上图所示的四类任务中:序列标注,这是最典型的NLP任务,比如中文分词,词性标注,命名实体识别,语义角色标注等都可以归入这一类问题,它的特点是句子中每个单词要求模型根据上下文都要给出一个分类类别。 分类任务,比如我们常见的文本分类,情感计算等都可以归入这一类。它的特点是不管文章有多长,总体给出一个分类类别即可。 句子关系判断,比如Entailment,QA,语义改写,自然语言推理等任务都是这个模式,它的特点是给定两个句子,模型判断出两个句子是否具备某种语义关系; 生成式任务,比如机器翻译,文本摘要,写诗造句,看图说话等都属于这一类。它的特点是输入文本内容后,需要自主生成另外一段文字。
- 句对关系判断,第一个起始符号CLS经过Transformer编码器后,增加简单的Softmax层,即可用于分类;
- 单句分类任务,具体实现同上一样;
- 问答类任务,譬如SQuAD v1.1,问答系统输入文本序列的question和包含answer的段落,并在序列中标记answer,让BERT模型学习标记answer开始和结束的向量来训练模型;
- 序列标准任务,譬如命名实体标注NER,识别系统输入标记好实体类别(人、组织、位置、其他无名实体)的文本序列进行微调训练,识别实体类别时,将序列的每个Token向量送到预测NER标签的分类层进行识别。
从这里可以看出,上面列出的NLP四大任务里面,除了生成类任务外,Bert其它都覆盖到了,而且改造起来很简单直观。
尽管Bert论文没有提,但可想到,其实对于机器翻译或者文本摘要,聊天机器人这种生成式任务,同样可以稍作改造即可引入Bert的预训练成果。只需要附着在S2S结构上,encoder部分是个深度Transformer结构,decoder部分也是个深度Transformer结构。根据任务选择不同的预训练数据初始化encoder和decoder即可。这是相当直观的一种改造方法。当然,也可以更简单一点,比如直接在单个Transformer结构上加装隐层产生输出也是可以的。
不论如何,从这里可以看出,NLP四大类任务都可以比较方便地改造成Bert能够接受的方式。这其实是Bert的非常大的优点,这意味着它几乎可以做任何NLP的下游任务,具备普适性,这是很强的。
Bert总结
首先是两阶段模型,第一阶段双向语言模型预训练,这里注意要用双向而不是单向,第二阶段采用具体任务Fine-tuning或者做特征集成;
第二是特征抽取要用Transformer作为特征提取器而不是RNN或者CNN;
第三,双向语言模型可以采取CBOW的方法去做。
Bert最大的亮点在于效果好及普适性强,几乎所有NLP任务都可以套用Bert这种两阶段解决思路,而且效果应该会有明显提升。
总结
到这里我们可以再梳理下几个模型之间的演进关系。从上图可见,Bert其实和ELMO及GPT存在千丝万缕的关系,比如如果我们把GPT预训练阶段换成双向语言模型,那么就得到了Bert;而如果我们把ELMO的特征抽取器换成Transformer,那么我们也会得到Bert。
所以可以看出:Bert最关键两点,一点是特征抽取器采用Transformer;第二点是预训练的时候采用双向语言模型。
对于Transformer来说,怎么才能在这个结构上做双向语言模型任务呢?乍一看上去好像不太好搞。其实有一种很直观的思路,ELMO的网络结构图,只需要把两个LSTM替换成两个Transformer,一个负责正向,一个负责反向特征提取,其实应该就可以。当然这是自己的改造,Bert没这么做。那么Bert是怎么做的呢?前面提过Word2Vec CBOW方法,它的核心思想是:在做语言模型任务的时候,我把要预测的单词抠掉,然后根据它的上文Context-Before和下文Context-after去预测单词。其实Bert怎么做的?Bert就是这么做的。从这里可以看到方法间的继承关系。
Bert更像一个最近几年NLP重要技术的集大成者,Bert本身的效果好和普适性强才是最大的亮点。
Ref
- https://zhuanlan.zhihu.com/p/91839581 一文看懂 Attention
- https://zhuanlan.zhihu.com/p/48508221 详解Transformer (Attention Is All You Need)
- https://zhuanlan.zhihu.com/p/43493999 NLP中的Attention原理
- https://zhuanlan.zhihu.com/p/46652512 BERT模型原理详解
- https://cloud.tencent.com/developer/article/1427537 NLP新秀:BERT的优雅解读
- https://zhuanlan.zhihu.com/p/48612853 词向量之BERT
- https://www.bilibili.com/video/av56235038?from=search&seid=9558641265797595207 李宏毅ELMO, BERT, GPT讲解
- https://zhuanlan.zhihu.com/p/49271699 从Word Embedding到Bert模型—自然语言处理中的预训练技术发展史
- https://cloud.tencent.com/developer/article/1780054 Embedding 背景 发展 生成方法 在推荐中的应用
- https://terrifyzhao.github.io/2019/01/11/Transformer模型详解.html Transformer详解