以下内容主要参考了文末列出的参考文献,在此表示感谢!
2018年被认为是NLP技术的new era的开始。在这一年,提出了多种有创新性的技术,而且最后的集大成者Bert在NLP的多项任务中屠榜,造成的震撼不比当初神经网络初次在ImageNet登场时小。
我们首先来看一下BERT的光辉战绩。
NLP一般可分为四大类任务:
- 序列标注:分词/POS Tag/NER/语义标注
- 分类任务:文本分类/情感计算
- 句子关系判断:Entailment/QA/自然语言推理
- 生成式任务:机器翻译/文本摘要....
绝大部分NLP问题可以归入上图所示的四类任务中:一类是序列标注,这是最典型的NLP任务,比如中文分词,词性标注,命名实体识别,语义角色标注等都可以归入这一类问题,它的特点是句子中每个单词要求模型根据上下文都要给出一个分类类别。第二类是分类任务,比如我们常见的文本分类,情感计算等都可以归入这一类。它的特点是不管文章有多长,总体给出一个分类类别即可。第三类任务是句子关系判断,比如Entailment,QA,语义改写,自然语言推理等任务都是这个模式,它的特点是给定两个句子,模型判断出两个句子是否具备某种语义关系;第四类是生成式任务,比如机器翻译,文本摘要,写诗造句,看图说话等都属于这一类。它的特点是输入文本内容后,需要自主生成另外一段文字。
在2018年Google的论文中推出了两种BERT,一个是BERT Base,一种是BERT LARGE。两者结构相似,主要是层数不同。BERT基本具有与OpenAI GPT完全相同的参数:L = 12,H = 768,A = 12其中L是堆叠的编码器的数量,H是隐藏大小,A是MultiHead attention中的头数。 BERT large基本上更大且计算密集度更高:L = 24,H = 1024,A = 16。从论文的结果中可以看出:BERT Base 超过 OpenAI GPT,并且BERT Large超过BERT base。
接下来看一下BERT在各项任务上的成绩:
GLUE
一般语言理解评估(GLUE)基准(Wang et al,2018)是各种自然语言理解任务的集合。有8个GLUE分类任务用于评估绩效。BERT Base不仅所有任务上超过了OpenAI GPT 达到了SOTA,而且平均将SOTA提高了5%。 对于所有任务,BERT Large 超过 BERT Base。
SQUAD
Standford问题回答数据集(SQuAD)是100k问答对的集合(Rajpurkar等,2016)。 给定一个问题和维基百科中包含答案的段落,任务是预测段落中的答案文本范围。在SQUAD中,BERT大大提升了性能。 获得最高分的模型是BERT LARGE模型集成,使用TriviaQA扩充数据集。
命名实体识别
为了评估Tag标记任务的性能,在CoNLL 2003命名实体识别(NER)数据集上微调BERT。 该数据集由200k个训练单词组成,这些单词已注释为人员、组织、位置、杂项或其他(非命名实体)。在NER中,BERT Large在一定程度上实现了SOTA,但没有大幅提高。
SWAG
具有对抗性生成的情境(SWAG)数据集包含113k个句子对完成示例,其评估基于常识的推断(Zellers等,2018)。在这项任务中,BERT以惊人的27.1%大幅改进SOTA,这是超人的表现!
消融研究(Ablation Study)
作者还进行了许多有用的消融研究。 最重要的一点是:
- BERT和OpenAI GPT之间的大多数性能差异可以通过双向性来解释。
在BERT中,最主要的创新包括Bi-directional,双向性以及next sentence prediction,但是从消融研究来看,起到最重要作用的是双向性。
接下来我们来讨论一下2018年的重要工作,ELMo,GPT以及BERT。
前面介绍了BERT的效果,另一方面是Bert具备广泛的通用性,就是说绝大部分NLP任务都可以采用BERT的两阶段模式直接去提升效果。我们先看一下BERT的两阶段模式。
预训练过程就是做图像或者视频领域的一种比较常规的做法,而且这种做法很有效,能明显促进应用的效果。
那么图像领域怎么做预训练呢,上图展示了这个过程,我们设计好网络结构以后,对于图像来说一般是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用新数据集合清洗掉高层无关的特征抽取器。
一般用ImageNet来做网络的预训练,主要有两点,一方面ImageNet是图像领域里有超多事先标注好训练数据的数据集合,分量足是个很大的优势,量越大训练出的参数越靠谱;另外一方面因为ImageNet有1000类,类别多,算是通用的图像数据,跟领域没太大关系。
那我们思考一下,在NLP领域的预训练模型是啥?
NLP领域也有类似的预训练模型,就是词向量机制。
譬如这样经过查表:
然后一句话可以表示为
结合下图:
如上图所示,有个NLP的下游任务,QA,就是问答问题。所谓问答问题,指的是给定一个问题X,给定另外一个句子Y,要判断句子Y是否是问题X的正确答案。使用问答问题假设设计的网络结构如上图所示,来演示如何使用训练好的Word Embedding。句子中每个单词以Onehot形式作为输入,然后乘以学好的Word Embedding矩阵Q,就直接取出单词对应的Word Embedding了。那个Word Embedding矩阵Q其实就是网络Onehot层到embedding层映射的网络参数矩阵。所以你看到了,使用Word Embedding等价于什么?等价于把Onehot层到embedding层的网络用预训练好的参数矩阵Q初始化了。这跟前面讲的图像领域的低层预训练过程其实是一样的,区别无非Word Embedding只能初始化第一层网络参数,再高层的参数就无能为力了。下游NLP任务在使用Word Embedding的时候也类似图像有两种做法,一种是Frozen,就是Word Embedding那层网络参数固定不动;另外一种是Fine-Tuning,就是Word Embedding这层参数使用新的训练集合训练也需要跟着训练过程更新掉。
我们上星期讨论word2vec的时候,把它夸成了一朵花儿。word2vec的好处是它可以捕捉语义信息,而且在CBOW中,它同时使用了上下文信息。使用word2vec对很多NLP任务都有帮助。但是word2vec也有自己的问题。最主要的问题是什么呢?
多义词是自然语言中经常出现的现象,也是语言灵活性和高效性的一种体现。多义词对Word Embedding来说有什么负面影响?如上图所示,比如多义词Bank,有两个常用含义,但是Word Embedding在对bank这个单词进行编码的时候,是区分不开这两个含义的,因为它们尽管上下文环境中出现的单词不同,但是在用语言模型训练的时候,不论什么上下文的句子经过word2vec,都是预测相同的单词bank,而同一个单词占的是同一行的参数空间,这导致两种不同的上下文信息都会编码到相同的word embedding空间里去。所以word embedding无法区分多义词的不同语义,这就是它的一个比较严重的问题。
ELMO
ELmo模型是AllenNLP在2018年8月发布的一个上下文无关模型,在BERT没出来时,也小有名气。ELMO是“Embedding from Language Models”的简称,其实这个名字并没有反应它的本质思想,提出ELMO的论文题目:“Deep contextualized word representation”更能体现其精髓,而精髓在哪里?在deep contextualized这个短语,一个是deep,一个是context,其中context更关键。在此之前的Word Embedding本质上是个静态的方式,所谓静态指的是训练好之后每个单词的表达就固定住了,以后使用的时候,不论新句子上下文单词是什么,这个单词的Word Embedding不会跟着上下文场景的变化而改变,所以对于比如Bank这个词,它事先学好的Word Embedding中混合了几种语义 ,在应用中来了个新句子,即使从上下文中(比如句子包含money等词)明显可以看出它代表的是“银行”的含义,但是对应的Word Embedding内容也不会变,它还是混合了多种语义。这是为何说它是静态的,这也是问题所在。ELMO的本质思想是:我事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分,不过这没关系。在我实际使用Word Embedding的时候,单词已经具备了特定的上下文了,这个时候我可以根据上下文单词的语义去调整单词的Word Embedding表示,这样经过调整后的Word Embedding更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。所以ELMO本身是个根据当前上下文对Word Embedding动态调整的思路。
上图展示的是ELMO的预训练过程,它的网络结构采用了双层双向LSTM,目前语言模型训练的任务目标是根据单词
通过把正向和反向的隐藏层状态连接起来:
ELMO采用了典型的两阶段过程,第一个阶段是利用语言模型进行预训练;第二个阶段是在做下游任务时,从预训练网络中提取对应单词的网络各层的Word Embedding作为新特征补充到下游任务中。
上面介绍的是ELMO的第一阶段:预训练阶段。那么预训练好网络结构后,如何给下游任务使用呢?上图展示了下游任务的使用过程,比如我们的下游任务仍然是QA问题,此时对于问句X,我们可以先将句子X作为预训练好的ELMO网络的输入,这样句子X中每个单词在ELMO网络中都能获得对应的三个Embedding。这种三层的结构也类似于图像处理中的从底层到高层语义逐渐复杂。之后给予这三个Embedding中的每一个Embedding一个权重a,这个权重可以学习得来,根据各自权重累加求和,将三个Embedding整合成一个。然后将整合后的这个Embedding作为X句在自己任务的那个网络结构中对应单词的输入,以此作为补充的新特征给下游任务使用。对于上图所示下游任务QA中的回答句子Y来说也是如此处理。因为ELMO给下游提供的是每个单词的特征形式,所以这一类预训练的方法被称为“Feature-based Pre-Training”。
使用双向LSTM,一开始的训练目标可以表示为:
具体来说是通过双方向预测单词,前向过程中,用1~k-1的词去预测第k个词,后向过程中,用k+1~N的词去预测第k个词。在计算损失函数的时候,也同时考虑了双向的损失,这样,使得词的特征提取具有了双向性。
ELMO的前辈,TagLM:
ELMo使用双层BiLSTM来训练语言模型,创新是线性组合不同层的word vectors, 作为最终的word representation。核心公式:
其中,
- 第一层是普通的word embedding 可以用wrod2vec或者glove来得到,或者使用character level得到token embedding》 这部分是general embedding,上下文无关。文中使用的character level的CNN+Highway.
- 后面连接两个biLSTM 去encode 输入(同时也有残差连接), 每一层LSTM得到的输出(隐状态) 作为每个词的上下文相关的word vectors.
- 这样每个词就会有(L+1)个词向量,L为biLSTM的层数.
- 词向量的线性组合,针对不同的任务,不同层的向量做不同的权重加和。 其中权重s是一个维度为L的vector,参与训练。
因此ELMo的基本输入单元为句子,每个词没有固定的词向量,是根据词的上下文环境来动态产生当前词的词向量,常见的场景可以较好解决一词多义的问题,这一点跟word2vec与glove等通用词向量模型是不同的。
另外比较有趣的一点是,ELMO和BERT是动画片《芝麻街》中的两个人物。
ELMO可以较好地解决多义词的问题。
在各项任务中也有不错的表现:
那么ELMO的缺点是什么呢?
首先,“不完全双向”。是指模型的前向和后向LSTM两个模型是分别训练的,从图中也可以看出,对于一个序列,前向遍历一遍获得左边的LSTM,后向遍历一遍获得右边的LSTM,最后得到的隐层向量直接拼接得到结果向量(前向的hidden state1 + 后向的hidden state2 = 总的hidden state,+是concat),并且在最后的Loss function中也是前向和后向的loss function直接相加,并非完全同时的双向计算。
另外,“自己看见自己”。是指要预测的下一个词在给定的序列中已经出现的情况。传统语言模型的数学原理决定了它的单向性。从公式
可以看出,传统语言模型的目标是获得在给定序列从头到尾条件概率相乘后概率最大的下一词,而双向模型会导致预测的下一词已经在给定序列中出现了的问题,这就是“自己看见自己”。如下图所示(图片从下往上看),最下行是训练数据A B C D,经过两个bi-lstm操作,需要预测某个词位置的内容。比如第二行第二列A|CD这个结果是第一层bi-lstm在B位置输出内容,包括正向A和反向CD,直接拼接成A|CD。比如第三行第二例ABCD这个结果是前向BCD和反向AB|D拼接结果,而当前位置需要预测的是B,已经在ABCD中出现了,这就会有问题。因而对于Bi-LSTM,只要层数增加,就是会存在“自己看见自己”的问题。
另一方面,在ELMO提出的时候,TRANSFORMER已经提出了,而TF是比LSTM更适合用于提取远距离词间关系特征的架构。
GPT
GPT是2018年OpenAI的论文《Improving Language Understandingby Generative Pre-Training》中提出的模型,是“Generative Pre-Training”的简称,从名字看其含义是指的生成式的预训练。GPT也采用两阶段过程,第一个阶段是利用语言模型进行预训练,第二阶段通过Fine-tuning的模式解决下游任务。下图展示了GPT的预训练过程,其实和ELMO是类似的,主要不同在于两点:首先,特征抽取器不是用的RNN,而是用的Transformer,上面提到过它的特征抽取能力要强于RNN,这个选择很明显是很明智的。
大家可以快速回顾一下什么是Transformer?multi-head, self-attention。补上位置编码的公式:
所以,结合一个例子来看:
其次,GPT的预训练虽然仍然是以语言模型作为目标任务,但是采用的是单向的语言模型,所谓“单向”的含义是指:语言模型训练的任务目标是根据
GPT中也提出了fine-tuning的方法,这里就非常类似图像处理中的预处理了。但是使用预训练好的参数,需要对任务的模型进行了改造。把任务的网络结构改造成和GPT的网络结构是一样的。然后,在做下游任务的时候,利用第一步预训练好的参数初始化GPT的网络结构,这样通过预训练学到的语言学知识就被引入到你手头的任务里来了,这是个非常好的事情。再次,你可以用手头的任务去训练这个网络,对网络参数进行Fine-tuning,使得这个网络更适合解决手头的问题。
GPT论文给了一个改造施工图如上,其实也很简单:对于分类问题,不用怎么动,加上一个起始和终结符号即可;对于句子关系判断问题,比如Entailment,两个句子中间再加个分隔符即可;对文本相似性判断问题,把两个句子顺序颠倒下做出两个输入即可,这是为了告诉模型句子顺序不重要;对于多项选择问题,则多路输入,每一路把文章和答案选项拼接作为输入即可。从上图可看出,这种改造还是很方便的,不同任务只需要在输入部分施工即可。
GPT的效果也是很好的(在BERT出来之前):
以上只是部分截图,GPT在12个任务中有9个达到最好。
GPT的有效因子分析:
那么GPT有什么缺点呢?
事后看起来,主要就是没有采用双向模型。
BERT
BERT是谷歌在2018年的论文《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》中提出来的模型。Bert采用和GPT完全相同的两阶段模型,首先是语言模型预训练;其次是使用Fine-Tuning模式解决下游任务。和GPT的最主要不同在于在预训练阶段采用了类似ELMO的双向语言模型,当然另外一点是语言模型的数据规模要比GPT大。
将三个模型并在一起看很有意思:
从上图可见,Bert其实和ELMO及GPT存在千丝万缕的关系,比如如果我们把GPT预训练阶段换成双向语言模型,那么就得到了Bert;而如果我们把ELMO的特征抽取器换成Transformer,那么我们也会得到Bert。所以你可以看出:Bert最关键两点,一点是特征抽取器采用Transformer;第二点是预训练的时候采用双向语言模型。
以下是BERT论文的思路:
假如给一个句子 “能实现语言表征[mask]的模型”,遮盖住其中“目标”一词。从前往后预测[mask],也就是用“能/实现/语言/表征”,来预测[mask];或者,从后往前预测[mask],也就是用“模型/的”,来预测[mask],称之为单向预测 unidirectional。单向预测,不能完整地理解整个语句的语义。于是研究者们尝试双向预测。把从前往后,与从后往前的两个预测,拼接在一起 [mask1/mask2],这就是双向预测 bi-directional。
BERT 的作者认为,bi-directional 仍然不能完整地理解整个语句的语义,更好的办法是用上下文全向来预测[mask],也就是用 “能/实现/语言/表征/../的/模型”,来预测[mask]。BERT 作者把上下文全向的预测方法,称之为 deep bi-directional。如何来实现上下文全向预测呢?BERT 的作者建议使用 Transformer 模型。
这个模型的核心是聚焦机制,对于一个语句,可以同时启用多个聚焦点,而不必局限于从前往后的,或者从后往前的,序列串行处理。不仅要正确地选择模型的结构,而且还要正确地训练模型的参数,这样才能保障模型能够准确地理解语句的语义。BERT 用了两个步骤,试图去正确地训练模型的参数。第一个步骤是把一篇文章中,15% 的词汇遮盖,让模型根据上下文全向地预测被遮盖的词。假如有 1 万篇文章,每篇文章平均有 100 个词汇,随机遮盖 15% 的词汇,模型的任务是正确地预测这 15 万个被遮盖的词汇。通过全向预测被遮盖住的词汇,来初步训练 Transformer 模型的参数。
然后,用第二个步骤继续训练模型的参数。譬如从上述 1 万篇文章中,挑选 20 万对语句,总共 40 万条语句。挑选语句对的时候,其中 210 万对语句,是连续的两条上下文语句,另外 210 万对语句,不是连续的语句。然后让 Transformer 模型来识别这 20 万对语句,哪些是连续的,哪些不连续。
BERT的关键创新如下:
1: Masked LM
从直觉上看,研究团队有理由相信,深度双向模型比left-to-right 模型或left-to-right and right-to-left模型的浅层连接更强大。遗憾的是,标准条件语言模型只能从左到右或从右到左进行训练,因为双向条件作用将允许每个单词在多层上下文中间接地“see itself”。
为了训练一个深度双向表示(deep bidirectional representation),研究团队采用了一种简单的方法,即随机屏蔽(masking)部分输入token,然后只预测那些被屏蔽的token。论文将这个过程称为“masked LM”(MLM),尽管在文献中它经常被称为Cloze任务(Taylor, 1953)。
在这个例子中,与masked token对应的最终隐藏向量被输入到词汇表上的输出softmax中,就像在标准LM中一样。在团队所有实验中,随机地屏蔽了每个序列中15%的WordPiece token。
虽然这确实能让团队获得双向预训练模型,但这种方法有两个缺点。首先,预训练和fine-tuning之间不匹配,因为在fine-tuning期间从未看到[MASK]token。为了解决这个问题,团队并不总是用实际的[MASK]token替换被“masked”的词汇。相反,训练数据生成器随机选择15%的token。例如在这个句子“my dog is hairy”中,它选择的token是“hairy”。然后,执行以下过程:
数据生成器将执行以下操作,而不是始终用[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. 这样做的目的是将表示偏向于实际观察到的单词。
Transformer encoder不知道它将被要求预测哪些单词或哪些单词已被随机单词替换,因此它被迫保持每个输入token的分布式上下文表示。此外,因为随机替换只发生在所有token的1.5%(即15%的10%),这似乎不会损害模型的语言理解能力。
使用MLM的第二个缺点是每个batch只预测了15%的token,这表明模型可能需要更多的预训练步骤才能收敛。团队证明MLM的收敛速度略慢于 left-to-right的模型(预测每个token),但MLM模型在实验上获得的提升远远超过增加的训练成本。
(FFNN feedforward neural network)
2:下一句预测
许多重要的下游任务,如问答(QA)和自然语言推理(NLI)都是基于理解两个句子之间的关系,这并没有通过语言建模直接获得。
在为了训练一个理解句子的模型关系,预先训练一个二进制化的下一句测任务,这一任务可以从任何单语语料库中生成。具体地说,当选择句子A和B作为预训练样本时,B有50%的可能是A的下一个句子,也有50%的可能是来自语料库的随机句子。例如:
Input = [CLS] the man went to [MASK] store [SEP]
he bought a gallon [MASK] milk [SEP]
Label = IsNext
Input = [CLS] the man [MASK] to the store [SEP]
penguin [MASK] are flight ##less birds [SEP]
Label = NotNext
团队完全随机地选择了NotNext语句,最终的预训练模型在此任务上实现了97%-98%的准确率。对BERT模型的观点
- high-performance的原因其实还是归结于两点,除了模型的改进,更重要的是用了超大的数据集(BooksCorpus 800M + English Wikipedia 2.5G单词)和超大的算力(对应于超大模型)在相关的任务上做预训练,实现了在目标任务上表现的单调增长
- 这个模型的双向和Elmo不一样,大部分人对他这个双向在novelty上的contribution 的大小有误解,我觉得这个细节可能是他比Elmo显著提升的原因。Elmo是拼一个左到右和一个右到左,他这个是训练中直接开一个窗口,用了个有顺序的cbow。
- 可复现性差:有钱才能为所欲为(Reddit对跑一次BERT的价格讨论)
For TPU pods: 4 TPUs * ~$2/h (preemptible) * 24 h/day * 4 days = $768 (base model)
16 TPUs = ~$3k (large model) For TPU:
16 tpus * $8/hr * 24 h/day * 4 days = 12k
64 tpus * $8/hr * 24 h/day * 4 days = 50k
XLNet概述
XLNet是一个语言模型。和ELMO,GPT,BERT一脉相承,同时借鉴了Transformer-XL,故称XLNet(XL含义源于衣服尺码,意思是模型横向更宽);并提出一些新方法改善了Bert存在的问题,目前取得了全面超越Bert的成果。和Bert一样,XLNet也分为Pre-train和Fine-turn两阶段;并且参数规模比Bert更大。
参考:
- https://zhuanlan.zhihu.com/p/49271699
- https://zhuanlan.zhihu.com/p/54743941
- https://jalammar.github.io/illustrated-bert/
- https://medium.com/dissecting-bert/dissecting-bert-part-1-d3c3d495cdb3
- https://medium.com/dissecting-bert/dissecting-bert-part2-335ff2ed9c73
- https://blog.htliu.cn/2019/01/01/elmo-1/
- https://blog.csdn.net/xiayto/article/details/84730009