欢迎关注个人微信公众号,大大大碗面,不定期分享AI论文解读和开发技术,互联网小白,轻喷~
作为一个学渣,自己前段时间才开始接触NLP领域,才疏学浅,但是因为平时有做笔记的习惯,所以将我收集整理的知识进行整理,方便自己和大家理解。其中可能有不对的地方,欢迎大家指出,互相学习。
前言
Bert最近很火,应该是最近最火爆的AI进展,网上的评价很高。在11项NLP tasks中取得了state-of-the-art 的结果,包括NER、问答等领域的任务。本文不仅针对这篇论文进行介绍,而且对它的前世今生进行梳理,其中引用了多篇知乎博文,我在参考文献中一一列出。
在谷歌宣布他们的预训练模型BERT之前,大家所知道的BERT,就是美国的儿童节目“芝麻街”里的人物BERT,就是下面这位),今天我们所讨论的可不是“芝麻街”里的BERT,而是谷歌的预训练语言模型BERT(Bidirectional Encoder Representations from Transformers)。
有意思的是,在BERT的实验部分,作者将BERT模型与OpenAI GPT和EMLo等都进行了对比,而其中EMLo竟然也是“芝麻街”中的人物。
下面就让我们来研究探讨一下这篇文章吧!
这一部分基本上引用自张俊林大佬的文章《从Word Embedding到Bert模型—自然语言处理中的预训练技术发展史》
图像领域的预训练
首先,BERT模型是一个预训练模型。自从深度学习火起来后,预训练过程就是做图像或者视频领域的一种比较常规的做法,有比较长的历史了,而且这种做法很有效,能明显促进应用的效果。
上图展示了在CV领域中,是如何进行预训练。当我们设计好网络结构以后,对于图像来说一般是CNN的多层叠加网络结构,可以先用某个训练集合比如训练集合A或者训练集合B对这个网络进行预先训练,在A任务上或者B任务上学会网络参数,然后存起来以备后用。当我们面临第三个任务C时,网络结构采取相同的网络结构,在比较浅的几层CNN结构,网络参数初始化的时候可以加载A任务或者B任务学习好的参数,其它CNN高层参数仍然随机初始化。之后我们用C任务的训练数据来训练网络,此时有两种做法,一种是浅层加载的参数在训练C任务过程中不动,这种方法被称为“Frozen”;另外一种是底层网络参数尽管被初始化了,在C任务训练过程中仍然随着训练的进程不断改变,这种一般叫“Fine-Tuning”,顾名思义,就是更好地把参数进行调整使得更适应当前的C任务。一般图像或者视频领域要做预训练一般都这么做。
这么做有几个好处,首先,如果手头任务C的训练集合数据量较少的话,现阶段的好用的CNN比如Resnet/Densenet/Inception等网络结构层数很深,几百万上千万参数量算起步价,上亿参数的也很常见,训练数据少很难很好地训练这么复杂的网络,但是如果其中大量参数通过大的训练集合比如ImageNet预先训练好直接拿来初始化大部分网络结构参数,然后再用C任务手头比较可怜的数据量上Fine-tuning过程去调整参数让它们更适合解决C任务,那事情就好办多了。这样原先训练不了的任务就能解决了,即使手头任务训练数据也不少,加个预训练过程也能极大加快任务训练的收敛速度。
图像领域预训练的可行性
对于层级结构的CNN来说,不同层级的神经元学习到了不同类型的图像特征,由底向上特征形成层级结构,如上图所示,如果我们手头是个人脸识别任务,训练好网络后,把每层神经元学习到的特征可视化肉眼看一看每层学到了啥特征,你会看到最底层的神经元学到的是线段等特征,图示的第二个隐层学到的是人脸五官的轮廓,第三层学到的是人脸的轮廓,通过三步形成了特征的层级结构,越是底层的特征越是所有不论什么领域的图像都会具备的比如边角线弧线等底层基础特征,越往上抽取出的特征越与手头任务相关。正因为此,所以预训练好的网络参数,尤其是底层的网络参数抽取出特征跟具体任务越无关,越具备任务的通用性,所以这是为何一般用底层预训练好的参数初始化新任务网络参数的原因。而高层特征跟任务关联较大,实际可以不用使用,或者采用Fine-tuning用新数据集合清洗掉高层无关的特征抽取器。
NLP领域的预训练
既然预训练在CV领域如此好用,那么它在NLP甚至其他领域是否也能发挥其作用?众多的学者进行了研究,提出了各自的模型。word embedding其实就是NLP里的早期预训练技术,但是这项技术总体而言不太成功,一般加到下游任务里,有1到2个点的性能提升,提升的幅度并没有像在图像领域那样喜人。
Word Embedding
在说Word Embedding之前,先更粗略地说下语言模型,因为一般NLP里面做预训练一般的选择是用语言模型任务来做。
语言模型
什么是语言模型?简单地说,语言模型就是用来计算一个句子的概率的模型,也就是判断一句话是否是人话的概率?为了能够量化地衡量哪个句子更像一句人话,可以设计如上图所示函数,核心函数P的思想是根据句子里面前面的一系列前导单词预测后面跟哪个单词的概率大小。句子里面每个单词都有个根据上文预测自己的过程,把所有这些单词的产生概率乘起来,数值越大代表这越像一句人话。(同时可以加入n-gram)
随着神经网络的兴起,越来越多的深度学习模型被运用其中,来进行语言模型这个任务——给你很多语料做这个事情,训练好一个神经网络,训练好之后,以后输入一句话的前面几个单词,要求这个网络输出后面紧跟的单词应该是哪个,你会怎么做?
神经网络语言模型(NNLM)
NNLM是Bengio 在2003年发表在JMLR上的论文。NNLM的学习任务是:输入某个句中单词 W t W_t Wt=“Bert” 前面句子的t-1个单词,要求网络正确预测单词Bert,即最大化:
P ( W t = ′ B e r t ′ ∣ W 1 , W 2 , … W ( t − 1 ) ; θ ) P(W_t= 'Bert' |W_1,W_2,…W_(t-1);θ) P(Wt=′Bert′∣W1,W2,…W(t−1);θ)
前面任意单词 W i W_i Wi 用Onehot编码(比如:0001000)作为原始单词输入,之后乘以矩阵Q后获得向量 C ( W i ) C(W_i ) C(Wi),每个单词的 C ( W i ) C(W_i) C(Wi)拼接,上接隐层,然后接softmax去预测后面应该后续接哪个单词。这个 C ( W i ) C(W_i) C(Wi)是什么?这其实就是单词对应的Word Embedding值,那个矩阵Q包含V行,V代表词典大小,每一行内容代表对应单词的Word embedding值。只不过Q的内容也是网络参数,需要学习获得,训练刚开始用随机值初始化矩阵Q,当这个网络训练好之后,矩阵Q的内容被正确赋值,每一行代表一个单词对应的Word embedding值。所以你看,通过这个网络学习语言模型任务,这个网络不仅自己能够根据上文预测后接单词是什么,同时获得一个副产品,就是那个矩阵Q,这就是单词的Word Embedding是被如何学会的。
2013年最火的用语言模型做Word Embedding的工具是Word2Vec,后来又出了Glove。
Word2Vec
Word2Vec有两种训练方法,一种叫CBOW,核心思想是从一个句子里面把一个词抠掉,用这个词的上文和下文去预测被抠掉的这个词;第二种叫做Skip-gram,和CBOW正好反过来,输入某个单词,要求网络预测它的上下文单词。而你回头看看,NNLM是怎么训练的?是输入一个单词的上文,去预测这个单词。这是有显著差异的。为什么Word2Vec这么处理?原因很简单,因为Word2Vec和NNLM不一样,NNLM的主要任务是要学习一个解决语言模型任务的网络结构,语言模型就是要看到上文预测下文,而word embedding只是无心插柳的一个副产品。但是Word2Vec目标不一样,它单纯就是要word embedding的,这是主产品,所以它完全可以随性地这么去训练网络。
如何使用Word Embedding
我们的主题是预训练,那么问题是Word Embedding这种做法能算是预训练吗?这其实就是标准的预训练过程。要理解这一点要看看学会Word Embedding后下游任务是怎么用它的。
NLP任务在使用Word Embedding的时候也类似图像有两种做法,一种是Frozen,就是Word Embedding那层网络参数固定不动;另外一种是Fine-Tuning,就是Word Embedding这层参数使用新的训练集合训练也需要跟着训练过程更新掉。
- 缺点
在图中我们也已经进行的标注,这是2018年之前NLP领域广泛采用的预训练方式,之前的文章中已经提到了,Word Embedding其实对于很多下游的NLP有帮助,但是帮助的程度没有想象中的那么大而已。所以,究竟是什么原因导致这样的问题呢?
多义词问题。比如多义词Bank,有两个常用含义,但是Word Embedding在对bank这个单词进行编码的时候,是区分不开这两个含义的,因为它们尽管上下文环境中出现的单词不同,但是在用语言模型训练的时候,不论什么上下文的句子经过word2vec,都是预测相同的单词bank,而同一个单词占的是同一行的参数空间,这导致两种不同的上下文信息都会编码到相同的word embedding空间里去。所以word embedding无法区分多义词的不同语义,这就是它的一个比较严重的问题。
从Word Embedding到ELMo
可以说,EMLo是基于特征融合的Word Embedding 方式。
ELMo是“Embedding from Language Models”的简称,但是论文的题目“Deep contextualized word representation”更能体现其精髓。在EMLo之前,Word Embedding本质上是个静态的方式,所谓静态指的是训练好之后每个单词的表达就固定住了,以后使用的时候,不论新句子上下文单词是什么,这个单词的Word Embedding不会跟着上下文场景的变化而改变,所以对于比如Bank这个词,它事先学好的Word Embedding中混合了几种语义 ,在应用中来了个新句子,即使从上下文中(比如句子包含money等词)明显可以看出它代表的是“银行”的含义,但是对应的Word Embedding内容也不会变,它还是混合了多种语义。
而ELMo的本质思想是:我事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分,不过这没关系。在我实际使用Word Embedding的时候,单词已经具备了特定的上下文了,这个时候我可以根据上下文单词的语义去调整单词的Word Embedding表示,这样经过调整后的Word Embedding更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。所以ELMo本身是个根据当前上下文对Word Embedding动态调整的思路。
ELMo采用了典型的两阶段过程,第一个阶段是利用语言模型进行预训练;第二个阶段是在做下游任务时,从预训练网络中提取对应单词的网络各层的Word Embedding作为新特征补充到下游任务中。上图展示的是其预训练过程,它的网络结构采用了双层双向LSTM,目前语言模型训练的任务目标是根据单词 W i W_i Wi的上下文去正确预测单词 W i W_i Wi, W i W_i Wi之前的单词序列Context-before称为上文,之后的单词序列Context-after称为下文。图中左端的前向双层LSTM代表正方向编码器,输入的是从左到右顺序的除了预测单词外 W i W_i Wi的上文Context-before;右端的逆向双层LSTM代表反方向编码器,输入的是从右到左的逆序的句子下文Context-after;每个编码器的深度都是两层LSTM叠加。这个网络结构其实在NLP中是很常用的。使用这个网络结构利用大量语料做语言模型任务就能预先训练好这个网络,如果训练好这个网络后,输入一个新句子 S n e w S_{new} Snew,句子中每个单词都能得到对应的三个Embedding:最底层是单词的Word Embedding,往上走是第一层双向LSTM中对应单词位置的Embedding,这层编码单词的句法信息更多一些;再往上走是第二层LSTM中对应单词位置的Embedding,这层编码单词的语义信息更多一些。也就是说,ELMo的预训练过程不仅仅学会单词的Word Embedding,还学会了一个双层双向的LSTM网络结构,而这两者后面都有用。
- 如何使用
在完成预训练之后,下游任务的句子中每个单词在EMlo网络中都能获得对应的三个Embedding,之后给予这三个Embedding中的每一个Embedding一个权重a,这个权重可以学习得来,根据各自权重累加求和,将三个Embedding整合成一个。然后将整合后的这个Embedding作为X句在自己任务的那个网络结构中对应单词的输入,以此作为补充的新特征给下游任务使用。
- EMlo的缺点
第一点,在做序列编码任务时,使用的是LSTM。(有待提高)
第二点,ELMo采取双向拼接这种融合特征的能力可能比Bert一体化的融合特征方式弱。(只是猜测)
从Word Embedding到GPT
相对的,GPT可以看成是基于“Fine-tuning”的Word Embedding。
GPT是“Generative Pre-Training”的简称,从名字看其含义是指的生成式的预训练。GPT也采用两阶段过程,第一个阶段是利用语言模型进行预训练,第二阶段通过Fine-tuning的模式解决下游任务。上图展示了GPT的预训练过程,其实和ELMo是类似的,主要不同在于两点:首先,特征抽取器不是用的RNN,而是用的Transformer,它的特征抽取能力要强于RNN,这个选择很明显是很明智的;其次,GPT的预训练虽然仍然是以语言模型作为目标任务,但是采用的是单向的语言模型,所谓“单向”的含义是指:GPT只采用Context-before这个单词的上文来进行预测,而抛开了下文。这个选择现在看不是个太好的选择,原因很简单,它没有把单词的下文融合进来,这限制了其在更多应用场景的效果,比如阅读理解这种任务,在做任务的时候是可以允许同时看到上文和下文一起做决策的。如果预训练时候不把单词的下文嵌入到Word Embedding中,将丢掉了很多信息。
- 如何使用
首先,对于不同的下游任务来说,本来你可以任意设计自己的网络结构,现在不行了,你要向GPT的网络结构看齐,把任务的网络结构改造成和GPT的网络结构是一样的。然后,在做下游任务的时候,利用第一步预训练好的参数初始化GPT的网络结构,这样通过预训练学到的语言学知识就被引入到你手头的任务里来了,这是个非常好的事情。再次,你可以用手头的任务去训练这个网络,对网络参数进行Fine-tuning,使得这个网络更适合解决手头的问题。
- 改造下游任务
在GPT的论文中,作者给出了何如改造下游任务的方式。
BERT
BERT和GPT一样,都是属于基于“Fine-Tuning”模式来解决下游任务,只是将GPT的单项模型,变成了双向;也正是如此,在GPT模型中,采用的是Transformer Decoder,每个时刻的Attention计算只能依赖于该时刻前的所有时刻的输出;在BERT模型中,采用的是Transformer Encoder,每个时刻Attention计算可以得到全部时刻的输出。
“双向”的区别
“双向” in ELMo
在上文中我们提到EMLo也是一种“双向”的模型,但是实际上从模型中我们发现,EMLo实现“双向”的方式是通过特征拼接实现的,即,使用两个单项的RNNs构成的语言模型的拼接。如下图所示:
公式表达如下:
前向RNNs(当前词的概率只依赖前面出现的词)的语言模型计算是:
后向RNNs(当前词的概率只依赖后面出现的词)的语言模型计算是:
“双向” in BERT
BERT提出了Masked Language Model,也就是随机去掉句子中的部分token,然后模型来预测被去掉的token是什么。这样实际上已经不是传统的神经网络语言模型(类似于生成模型)了,而是单纯作为分类问题,根据这个时刻的hidden state来预测这个时刻的token应该是什么,而不是预测下一个时刻的词的概率分布了。
具体操作是:随机mask语料中15%的token,然后将masked token 位置输出的final hidden vectors送入softmax,来预测masked token。但是这样会导致一些问题。这样虽然可以放心的双向encoding了,但是这样在encoding时把这些盖住的标记也给encoding进去了,而这些mask标记在下游任务中是不存在的。所以作者提出了一些策略来解决这个问题:
- 有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
注:在训练过程中只有15%的token被预测,正常的语言模型实际上是预测每个token的,因此Masked LM相比正常LM会收敛地慢一些。如下图:
Other Task - Next Sentence Prediction
很多需要解决的NLP tasks依赖于句子间的关系,例如问答任务等,这个关系语言模型是获取不到的,因此将下一句话预测作为了第二个预训练任务。
模型输入
在BERT中模型的输入分为三个部分。
- Token embedding 表示当前词的embedding,词向量。
- Segment Embedding 表示当前词所在句子的index embedding,用于区分不同句子。
- Position Embedding 表示当前词所在位置的index embedding,单词的位置信息。
相比于Transformer模型输入的是Token Embeddings和Position Embeddings,在BERT模型中有多加入了Segment Embeddings为将句子整体的Embedding给对应句子中的每个单词。对于句子对,这样可以区分两个句子,比如,两个句子中都包含单词“我”,那么每个单词分别加上单词所在句子的Embedding就能够区分每个“我”到底是在哪个句子里,另外,paper还提出了使用[SEP]作为分隔符来区分两个句子。
同时,每个句子或者每个句子对(句子序列)前有[CLS]标识符,
这个标识符的Embedding可以代表整个句子或者句子序列的Embedding。
下游任务改造
文本分类任务
既然句子和句子对的上层表示都得到了,那么当然要对文本分类任务进行改造,方式十分简单,对[CLS]的表示进行分类即可。下图为单个句子与句子对的分类任务改造。
序列标注任务
对于每一个token的输出,加一层softmax即可。
抽取式任务
参考资料
再次强调,本文是对网上众多关于BERT解读的汇总。
知乎问题:https://www.zhihu.com/search?type=content&q=BERT
张俊林:https://zhuanlan.zhihu.com/p/49271699
李入魔:https://zhuanlan.zhihu.com/p/46652512
夕小瑶:https://zhuanlan.zhihu.com/p/47488095
习翔宇:https://zhuanlan.zhihu.com/p/46833276
陈猛:https://zhuanlan.zhihu.com/p/51255438
[1] Devlin J , Chang M W , Lee K , et al. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding[J]. 2018.
[2] Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[C]//Advances in Neural Information Processing Systems. 2017: 5998-6008.
[3] Peters M E, Neumann M, Iyyer M, et al. Deep contextualized word representations[J]. arXiv preprint arXiv:1802.05365, 2018.