本文上两篇系列
4.6 Bidirectional Encoder Representations from Transformers(BERT)
如果要用一句时下正流行的话来形容BERT的出现,这句话大概再恰当不过:
一切过往, 皆为序章。
2018年的10月11日,这似乎是个绝对平凡的日子(说句题外话,无独有偶,OpenAI在其博客中放出GPT工作的时间恰好不多不少是4个整月前,即2018年6月11日),然而Google AI的Jacob Devlin和他的合作者们悄悄地在arxiv上放上了他们的最新文章,名为《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》,随后先是在Twitter上引发了一波浪潮,同样是Google AI团队的Thang Luong在其Twitter上直言这项工作是一个划时代的工作(原话“A new era of NLP has just begun a few days ago.”),然后在中文社区也迅速发酵,那几天NLP从(搬)业(砖)人员的朋友圈基本被BERT刷屏,即便时至一个多月后的今日,这一现象也基本不见消退的迹象,几乎大家见面闲聊开口必谈BERT,似乎做NLP的大可不必使用华夏光辉灿烂文明之一的“吃过了吗?”,而替换为今日的“BERT跑起来了吗?”,可见一斑。
不得不说,Jacob和他的小伙伴们真是大手笔,和4个月前他的前辈GPT至少一样,乃至野心更大,除了GPT刷过的那些榜之外,BERT还添加了一项任务SQuAD(这大概是当前NLP最为火热的任务之一),也许这里的每一项任务都可能是一个研究小组的活命本钱,甚至还有可能是某个评教授职称的救命稻草。然而,BERT丝毫不放在眼里,直接将它的铁蹄踏入11项NLP任务,将途中目见耳闻所遭遇的一切荡平无余,留给在这之前仍在苦苦挣扎于某个排行榜中的人们无尽的错愕和唏嘘,而BERT似乎不曾心软和迟疑片刻。
很快,大概过了不到一个月,Google AI把他们已经预训练好的BERT模型公布出来,包括英语的base和large模型,另外针对其他语言也放出了中文(仅有的一个非英语的单个模型)和一个102种语言的混合语言预训练模型,这再次触发和引爆了NLP界的集体高潮。
不过,为何BERT能如此引人注目,不妨来一探究竟。私以为,BERT最主要的几个特征分别是
-
利用了真双向的Transformer
-
为了利用双向信息,改进了普通语言模型成为完形填空式的Mask-LM(Mask-Language Model)
-
利用Next Sentence Prediction任务学习句子级别信息
-
进一步完善和扩展了GPT中设计的通用任务框架,使得BERT能够支持包括:句子对分类任务、单句子分类任务、阅读理解任务和序列标注任务
为了更深入理解BERT,我们分别来看看他的这些特征。
首先看看BERT如何使用的双向Transformer,其实很好理解,可以用一句话来回答为什么BERT使用的是双向Transformer,那就是:BERT用了Transformer的Encoder框架。但是,我想这样的答案自然是要扣分的,更“求生欲”一点的答案是:因为Encoder中用了Self-attention机制,而这个机制会将每一个词在整个输入序列中进行加权求和得到新的表征,更通俗的说法是每一个词在经过Self-attention之后,其新的表征将会是整个输入序列中所有词(当然也包括它本身)的加权求和,每一个词都达到了“我中有你,你中有我”的境界,如果经过更多的transformer的block(意味着经过更多Self-attention),那么互相交融的程度将会更高,类似于BERT这样深的结构(Base模型是12层,Large模型是24层),经过所有block后,彼时,遑论“我中有你,你中有我”了,一切早已是“我在哪里”和“我是谁”这样的哲学式命题了,也就是连“你我”的界限都早已混沌的状态了。因此,说BERT是双向的语言模型结构,不仅丝毫不过分,它实质上是一种将序列中每一个词之间无比交融在一块的模型,更可贵的是,交融的姿势还可以多种多样,这可从Large版本BERT的多头机制中的Head个数多达16个,体会出这种交融的程度,可谓丝丝入扣。这里仅给出一例,下图只是两个head学习到的交融模式,如果多达16个head,这样的交融模式还要重复16次,可见一斑。
transformer_multi_head.PNG而相应的在ELMo与GPT中,它们并没有用上这种交融模式,也就是它们本质上还是一个单向的模型,ELMo稍微好一点,将两个单向模型的信息concat起来,GPT则只用了单向模型,这是因为它没有用上Transformer Encoder,只用了Decdoer的天生基因决定的,其实,很多人就把这种left-to-right的Transformer框架叫做Decoder,因为事实上Decoder就是如此(具体做的时候需要提前把未来待生成的词做好mask,细节上通过上三角矩阵来实现),这也是OpenAI把他们的模型叫做"Generative"的原因所在。
然而,使用双向Transformer会有一个问题,正如上面的分析,即便对于Base版BERT来说,经过12个block,每一个block内部都有12个多头注意力机制,到最后一层的输出,序列中每个位置上对应的词向量信息,早已融合了输入序列中所有词的信息,而普通的语言模型中,是通过某个词的上下文语境预测当前词的概率,如果直接把这个套用到Transformer的Encoder中,会发现待预测的输出和序列输入已经糅合在一块了,说白了就是Encoder的输入已经包含了正确的监督信息了,相当于给模型泄题了,如此说来普通语言模型的目标函数无法直接套用。那么,如何解决Self-attention中带来了表征性能卓越的双向机制,却又同时带来了信息泄露的这一问题?
BERT的作者很快联想到了,如果我把原来要预测整个句子的输出,改为只预测这个句子中的某个词,并且把输入中这个词所在位置挖空,这样一来,岂不就不会存在泄露真题的问题了?Jacob是这样想的(实际上是参考了很早之前提出的Cloze问题),这位以单人徒手搭建大工程著称的牛人,行动力超强,立马就把这一方法吸收和实现到BERT中,他们的想法也特别简单。
-
输入序列依然和普通Transformer保持一致,只不过把挖掉的一个词用"[MASK]"替换
-
Transformer的Encoder部分按正常进行
-
输出层在被挖掉的词位置,接一个分类层做词典大小上的分类问题,得到被mask掉的词概率大小
正是因为加了mask,因此BERT才把这种方法叫做Masked-LM,整个过程如下所示
bert_pretrain.png而这就直接把普通语言模型中的生成问题(正如GPT中把它当做一个生成问题一样,虽然其本质上也是一个序列生成问题),变为一个简单的分类问题,并且也直接解决了Encoder中多层Self-attention的双向机制带来的泄密问题(单层Self-attention是真双向,但不会带来泄密问题,只有多层累加的Self-attention才会带来泄密问题),使得语言模型中的真双向机制变为现实。
不过,BERT针对如何做“[MASK]”,做了一些更深入的研究,它做了如下处理
-
选取语料中所有词的15%进行随机mask
-
选中的词在80%的概率下被真实mask
-
选中的词在10%的概率下不做mask,而被随机替换成其他一个词
-
选中的词在10%的概率下不做mask,仍然保留原来真实的词
至于为什么要这么做,BERT也给出了足够感性的解释。对于要做mask,这个原因上面已经分析了,就是为了解决双向机制的泄密问题而设计的;而为什么还要有一部分概率不做真正的mask,而是输入一个实际的词,这样做的好处是尽量让训练和finetune的时候输入保持一致,因为finetune的时候输入中是没有“[MASK]”标记的,,对于保留为原来的真实词,也就是真的有10%的情况下是泄密的(占所有词的比例为15% * 10% = 1.5%),作者说这样能够给模型一定的bias,相当于是额外的奖励,将模型对于词的表征能够拉向词的真实表征(个人理解是:因为输入层是待预测词的真实embedding,在输出层中的该词位置得到的embedding,是经过层层Self-attention后得到的,这部分embedding里肯定多多少少依然保留有部分输入embedding的信息,而这部分的多多少少就是通过输入一定比例的真实词所带来的额外奖励,最终会使得模型的输出向量朝输入层的真实embedding有一个偏移,而如果全用mask的话,模型只需要保证输出层的分类准确,对于输出层的向量表征并不关心,因此可能会导致最终的向量输出效果并不好);最后,BERT对选中的词在10%的概率下不做mask,而是被随机替换成为一个其他词,这样做的目的,BERT也给出了他们的解释:因为模型不知道哪些词是被mask的,哪些词是mask了之后又被替换成了一个其他的词,这会迫使模型尽量在每一个词上都学习到一个全局语境下的表征,因而也能够让BERT获得更好的语境相关的词向量(这正是解决一词多义的最重要特性)。其实这样做的更感性解释是,因为模型不知道哪里有坑,所以随时都要提心吊胆,保持高度的警惕。正如一马平川的大西北高速公路,通常认为都是路线直,路面状况好,但如果掉以轻心,一旦有了突发情况,往往也最容易出事故,鲁棒性不高;而反倒是山间小路,明确告诉了每一位司机路面随时都有坑,并且没法老远就提前知道,所以即便老司机也只能小心翼翼的把稳方向盘慢慢的开,这样做反倒鲁棒性更高。正所谓《左传》中所言“居安思危,思则有备,有备无患,敢以此规”,BERT的这种设计又何尝不是“居安思危”的典范,Jacob给出的原文解释如下:
The Transformer encoder does not know which words it will be asked to predict or which have been replaced by random words, so it is forced to keep a distributional contextual representation of every input token.
此外在引用19中,作者也从直觉上解释了这样做的可行性,
- If we used [MASK] 100% of the time the model wouldn’t necessarily produce good token representations for non-masked words. The non-masked tokens were still used for context, but the model was optimized for predicting masked words.
-
If we used [MASK] 90% of the time and random words 10% of the time, this would teach the model that the observed word is never correct.
-
If we used [MASK] 90% of the time and kept the same word 10% of the time, then the model could just trivially copy the non-contextual embedding.
然而可惜的是,Jacob并没有在论文中做更细致的实验,来证明这些言论的正确性,因此可能会存在其他的更优的比例组合。
除了用上Mask-LM的方法使得双向Transformer下的语言模型成为现实,BERT还利用和借鉴了Skip-thoughts方法中的句子预测问题,来学习句子级别的语义关系,具体做法则是将两个句子组合成一个序列,当然组合方式会按照下面将要介绍的方式,然后让模型预测这两个句子是否是先后近邻的两个句子,也就是会把"Next Sentence Prediction"问题建模成为一个二分类问题。训练的时候,数据中有50%的情况这两个句子是先后关系,而另外50%的情况下,这两个句子是随机从语料中凑到一起的,也就是不具备先后关系,以此来构造训练数据。句子级别的预测思路和之前介绍的Skip-thoughts基本一致,当然更本质的思想来源还是来自于word2vec中的skip-gram模型。
在预训练阶段,因为有两个任务需要训练:Mask-LM和Next Sentence Prediction,因此BERT的预训练过程实质上是一个Multi-task Learning,具体说来,BERT的损失函数由两部分组成,第一部分是来自于Mask-LM的单词级别的分类任务,另一部分是句子级别的分类任务,通过这两个任务的联合学习,可以使得BERT学习到的表征既有token级别的信息,同时也包含了句子级别的语义信息。具体的损失函数如下
其中是BERT中Encoder部分的参数,是Mask-LM任务中在Encoder上所接的输出层中的参数,则是句子预测任务中在Encoder上接上的分类器中的参数。因此,在第一部分的损失函数中,如果被mask的词集合为M,因为它是一个词典大小|V|上的多分类问题,那么具体说来有
相应的在句子预测任务中,也是一个分类问题的损失函数
因此,两个任务联合学习的损失函数是
至于具体的预训练工程实现细节方面,BERT还利用了一系列策略,使得模型更易于训练,比如对于学习率的warm-up策略(和上文提到的ULMFiT以及Transformer中用到的技巧类似),使用的激活函数不再是普通的ReLu,而是GeLu,也是用了dropout等这些比较常见的训练技巧。此外,BERT使用的语料比GPT也要大得多(GPT用的是BooksCorpus,800 million个词,而BERT除了BooksCorpus之外,还加入了Wikipedia数据集,2500 million个词,总共有3.3 billion个词)。
最后,当BERT的预训练完成后,针对如何利用预训练好的模型,迁移到特定任务背景下,BERT在GPT的基础上,将这一个非常重要的工作进行了更深入的设计,因为中间的Encoder对于几乎所有任务而言,都可以直接利用,因此这部分的设计主要分为两个方面:输入层和输出层。在输入层方面,思路和GPT基本类似,如果输入只有一个句子的话,则直接在句子的前后添加句子的起始标记位和句子的结束符号,在BERT中,起始标记都用“[CLS]”来表示,结束标记符用"[SEP]"表示,对于两个句子的输入情况,除了起始标记和结束标记之外,两个句子间通过"[SEP]"来进行区分。除了这些之外,BERT还用了两个表示当前是句子A还是句子B的向量来进行表示,对于句子A来说,每一词都会添加一个同样的表示当前句子为句子A的向量,相应的,如果有句子B的话,句子B中的每一个词也都会添加一个表示当前句子为句子B的向量。当然,和Transformer中一样,为了引入序列中词的位置信息,也用了position embedding,因此,最终的输入层大概是
bert_embedding_layer.PNG除了输入层要尽量做到通用之外,根据不同任务设计不同的输出层也变得尤为重要,BERT主要针对四类任务考虑和设计了一些非常易于移植的输出层,这四类任务分别是:单个序列文本分类任务(SST-2, CoLA)、两个序列文本分类任务(MNLI, QQP, QNLI, STS-B, MRPC, RTE)、阅读理解任务(SQuAD)和序列标注任务(CoNLL-2003 NER),句子或答案选择任务(SWAG等,这里我把SWAG单独划分一个类别,因为具体做法和普通的文本分类任务不完全一致,只不过BERT因为它和其他分类任务都只需要利用"[CLS]"位置的向量,所以才把它一起归入到句子对分类任务中)。
对于单序列文本分类任务和序列对的文本分类任务使用框架基本一致,只要输入层按照上面提到的方法做好表示即可,然后这两个分类任务都是利用BERT的Encoder最后一层的第一个时刻“[CLS]”对应的输出作为分类器的输入,文中的解释是这个时刻可以得到输入序列的全局表征,并且因为监督信号从这个位置反馈给模型,因而实际上在finetune阶段也可以使得这一表征尽量倾向于全局的表征。当然,即便如此,同样也是可以通过一些很简单易行的办法将其他时刻甚至其他层内的表征拿来用的,个人认为并不需要局限在这一个时刻上的表征。另外,finetune阶段,在BERT Encoder基础上,这些分类任务因为只需要接一个全连接层,因此增加的参数只有,其中H是Encoder输出层中隐状态的维度,K是分类类别个数。
对于SQuAD 1.1任务来说,需要在给定的段落中找到正确答案所在的区间,这段区间通过一个起始符与终止符来进行标记,因此只需要预测输入序列中哪个token所在位置是起始符或终止符即可,因此这个过程只需要维护两个向量,分别是起始符的向量和终止符的向量,不过与其说是向量,还不如说是两个对应的全连接层,只不过这两个全连接有些特殊,在这两个全连接层中,输入层的节点个数为BERT Encoder输出的节点个数,也就是H,而这两个全连接层的输出层都只有一个节点,而这两个全连接层的作用就是要把序列输出的每一个向量通过这两个全连接层映射为一个实数值,可以等同于打分,然后再根据这个打分在序列的方向上选取最大值作为起始符和终止符,因此这个过程实际上只增加了的参数量,可谓比分类任务的参数增加还要少(至少和2分类任务的参数一致)。这里有两个需要注意的地方:其一,无论对于起始符和终止符也好,它们的判断和普通的分类任务不一样,不是直接针对每一个token进行一个是否是起始符的分类(如果是这样的话,参数量要增加),这里有些类似于在全体输入序列上进行排序的一个工作,因此针对起始符的判断来讲只需要一个的全连接就可以了。第二个需要注意的是,训练的时候无须担心终止符是否在起始符后面这一点,因为即便模型训练的时候预测是终止符在起始符前面了,这一错误现象会通过损失函数反馈给模型,然而在做inference的时候,就必须要保证终止符一定要在起始符后面这一点了。
BERT也考虑了如何在序列标注任务上进行finetune,对于序列中的每一个token而言,实际上就是一个分类任务,只不过和前面提到的普通分类任务不一样的地方在于,这里的分类是需要针对序列中的每一个词做分类,参数增加在j,这里的K是序列标注中标注的种类个数。
最后对于SWAG(Situations Adversarial Generations)任务来讲,因为需要在给定一个句子后,从四个候选句子中选择一个最有可能是该句子的下一个句子,这里面通常包含了一些常识推理。BERT的做法是将前置句子和四个候选句子分别进行组合成一个句子对,并按照之前讨论过的方法输入到Encoder,这样便可以得到四个pair的句子表征,然后只需要维护一个向量(或者说是一个多到1的全连接层),便可以给每一个候选句子进行打分,从而得到四个候选句子中最有可能是下一个的句子。这个全连接只需要增加的参数量,这一点和SQuAD 1.1任务中使用BERT的情况类似,此外,这种做法和GPT对于Question Selection的做法也比较类似,因此理论上应该也是可以应用到WikiQA这种数据集上的,finetune阶段同样只需要增加的参数量。
bert_finetune_model.PNG最后,我们再次总结下BERT的几个主要特点:
-
Transformer Encoder因为有Self-attention机制,因此BERT自带双向功能
-
因为双向功能以及多层Self-attention机制的影响,使得BERT必须使用Cloze版的语言模型Masked-LM来完成token级别的预训练
-
为了获取比词更高级别的句子级别的语义表征,BERT加入了Next Sentence Prediction来和Masked-LM一起做联合训练
-
为了适配多任务下的迁移学习,BERT设计了更通用的输入层和输出层
然后,我们再来看看BERT的工作都站在了哪些“巨人肩膀”上:
-
针对第一点,双向功能是Transformer Encoder自带的,因此这个“巨人肩膀”是Transformer
-
第二点中Masked-LM的巨人肩膀是语言模型,CBOW以及Cloze问题
-
第三点中Next Sentence Prediction的“巨人肩膀”是Skip-gram,Skip-thoughts和Quick-thoughts等工作
-
第四点中,对输入层和输出层的改造,借鉴了T-DMCA以及GPT的做法
再来看看预训练使用的数据方面,ELMo使用的是1B Word Benchmark数据集,词数为10亿,然而句子间的顺序是随机打乱的,无法建模句子级别的依赖关系,所以GPT和BERT都弃用这个数据集;GPT使用的是BooksCorpus,词数为8亿;BERT除了使用BooksCorpus之外,还加入了25亿级别的Wikipedia数据集,因此整体使用的数据集为33亿个词。可见BERT使用的数据集是最大的。
再来看看模型方面,ELMo使用的是一个Bi-LSTM结构,输入层和输出层使用CNN来进行建模。而GPT使用了12个block,隐藏层节点数为768,多头为12,这也是BERT的base版本(因为base版本就是为了跟GPT做对比的),参数量按照BERT base版本估计为110M。而BERT再一次把模型变得更大了,使用24个block,并且隐藏层节点数与多头都有相应扩大,按照Jacob在论文中自己的说法,言下之意似乎想要表明他们设计的这个模型应该是有史以来的最大的模型之一。
在计算资源比拼方面,GPT模型在8块GPU上预训练了一个月时间(参数规模和GPT差不多的BERT base版本,在16块TPU上只花了4天时间,为此BERT在论文中提到这个训练时间的时候,还不失时机的给它们家的TPU放了个链接,打了一波广告),对于参数规模为GPT三倍之多的BERT large版本,在64块TPU上训练耗时也依然达到4天之久。
bert_gpt_elmo.PNG因此,总结来看,BERT的出现既踩在了一众前辈们的“巨人肩膀”上,通过精心设计和一些训练技巧造出了一个庞大的模型,最后又舍得花钱砸下巨大资源,给众人上演了一场炫目无比的烟花秀。所以,与其说BERT带来了一次翻天覆地的革新,不如说BERT团队糅合能力超群,以及还有一点也非常重要:有钱。
然而,和普通的烟花秀不一样,普通烟花易逝,而BERT说不定可真是将一朵烟花怒放在每一位NLP算法人员心中,它先是让我们看了一场横扫11项任务的表演秀(也是实力秀),宣告世人这场烟花不一般,而更为可贵的是,当众人惊魂甫定之时,很快,BERT又把他们排练烟花秀的一切设备(模型)全都公之于众,甚至把排练得到的效果最好的布景(预训练参数)和点火器(finetune超参数)也全都奉献了出来,似乎愿景也足够伟大:让每个人都可以上演一场自己的烟花秀,将预训练好的模型在复杂任务上简单进行一次finetune,便立马能够得到一个非常高的baseline。
沉寂了五年之久的NLP界,似乎迎来了一个新的时代。
4.7 如何爬上梯子的第二级
预训练语言模型的优势在于
-
近乎无限量的优质数据
-
无需人工标注
-
一次学习多次复用
-
学习到的表征可在多个任务中进行快速迁移
问题是如何利用这些预训练好的模型,Google们已经给我们提供巨人的肩膀了,那“梯子”在哪呢?我们怎么爬上去这些“巨人肩膀”?也就是如何使用这些预训练好的模型。一般来说,可以有三种方式来使用。它们分别是:
- 将预训练模型当做一个特征提取器,直接将预训练模型的输出层去掉,然后使用去掉输出层之后的最后一层输出作为特征输入到我们自己精心设计好的Task-specific模型中去,在训练过程中,作为特征提取器的部分(比如BERT Encoder)的参数是不变的。另外,特征提取器并不一定只能用最后一层的输出向量,也可以使用中间的某些层,甚至也可以借鉴ELMo的做法,在各层之间利用一个softmax来学习各自的权值,或者也可以尝试一些更为复杂的各层组合方式;
- 将预训练模型整体接入Task-specific模型,继而重新在新的数据集上整体重新训练,当然训练技巧可以有很多种,比如ULMFiT的三角学习率和逐层解冻或者是Transformer的warmup策略(上文都有提到),这些训练技巧非常重要,需要好好把控,否则很容易学崩了,甚至让原有预训练语言模型的优势都被新的finetune抹去了,因此需要实验设计一个比较好的finetune策略。此外,和特征提取器的接入方式类似,预训练模型的接入不一定只能接最后层的输出,可以尝试更复杂的接入方式,比如DenseNet;
- 和上面两种极端情况相反,或者说综合了上面两种方式的方案,即保留预训练模型的一部分,另外一部分则和Task-specific模型一起finetune,在某些情况下,这可能是个比较合理的选择,比如预训练模型比较深(NLP模型通常来讲比CV模型都要浅很多),以及训练数据不算太多的情况,这个时候一方面要保证预训练模型在大规模语料上曾经学习到的表征,另一方面因为又要做新数据下的迁移,但是数据量比较少,重新finetune整个模型可能不太合适,容易导致模型的鲁棒性不高,那么似乎选择最后的一些层进行选择性的finetune会是比较好的方案。
上面这三种方案,既包括了BERT所言的feature-based使用方法, 也包括了BERT的finetune方法。另外GPT和BERT也给我们提供了很好的输入层与输出层通用包,再加上一些训练技巧,一般的NLP任务下应该是足够应付了。
然而,GPT和BERT即便刷新了十多项NLP任务,但似乎各自默契的都不曾尝试过在生成问题中大展身手,比如二者的主要征战沙场GLUE(General Language Understanding Evaluation)连名字中都直指这个“胶水”排行榜主要就是语言理解问题,除了GLUE外(GPT和BERT都去掉了GLUE的WNLI数据集,因此他们刷的GLUE是8个数据集),GPT额外刷过的数据集有4个:SNLI, SciTail, RACE和Stroy Cloze,其中SNLI和SciTail都是自然语言推断问题,本质上是一个句子级别的分类问题,RACE和Stroy Cloze是question answering或阅读理解式的问题,本质上可以认为分别是一个token级别的分类以及句子级别的打分排序问题;而BERT额外刷过的三个数据集中SQuAD本质上是一个token级别的分类问题,NER也是如此,SWAG则是从候选句子中选择正确的推断,是一个句子级别的打分排序问题。GPT刷了12项数据集(打破其中9项纪录),BERT刷了11项数据集(打破其中11项),然而无一例外的这些数据集全是Natural Language Understanding领域的问题,而对于NLP中另一个庞大领域Natural Language Generation,不约而同的选择了放弃,是未曾注意,还是选择视而不见,背后的原因我无从猜测。不过,无论GPT和BERT是什么原因没有选择在生成问题中去尝试,私以为,想要把GPT和BERT的预训练模型迁移到生成问题中,大约应该也不是一件非常困难的事情,首先GPT本质上就是一个生成模型(从其名字的Generative中就可以看出),而BERT虽然使用的是Encoder,但是把它改造成一个Decoder应该不是什么特别困难的事情,因为本质上Encoder和Decoder基本框架相同,只不过Decoder需要做好两件事情,第一件事情是Decoder中也有Self-attention层,和Encoder中的Self-attention不同的是,Decoder需要做好mask,以避免attention到未来待预测的词上去;第二件事情需要做的是Decoder中有一个交叉attention,以获取来自于Encoder侧的信息。但是,这里还涉及到Encoder-Decoder整体这种seq2seq的框架是否要保留的问题,如果使用GPT使用的T-DMCA这种仅用Decoder做生成问题的,那么改造相对会更易行,如果要保留Encoder-Decoder这种双结构,那么改造可能会相对复杂一些。
乐观一点,如果这种改造非常成功(似乎已经具备有充分的理由来相信这一点),那么NLP似乎会迎来一个阶段性的大一统时代:也就是一个基本模型做到一个闭环。其流程是:从文本到语义表征,再从语义表征重新生成文本。这样看来,至少在NLP领域(CV等其他AI领域,留给各位看官自行脑洞好了),未来大约还是很值得期待的一件事情。
nlp_nlu_nlg.jpeg那么以谷歌为代表的这些工作,会不会像五年以前一样,也即将开启一个新的时代呢?能不能让AI成功爬上第二级梯子呢?让我们拭目以待。
四. 后现代启示录?
回过头来看此文,通篇大体都是反观AI儿子理解他人说的话写的书,无非都是些人类母亲的育儿笔记,徒当以史为鉴可以知兴替罢了。
实事求是的说,人类母亲的教学成果目前看来,至少还不算太赖,寄养在各大公司(以Google, Facebook和OpenAI等为代表)的AI儿子们也开始理解母亲的一些稍微简单些的指令,甚至也能咿咿呀呀的开口说话了,不能不说这十多年的AI成长大家有目共睹。但成果毕竟属于过去,望子成龙的人类母亲对于儿子的今后该如何安排?毕竟儿子以后还要上清北常青藤,毕竟七大姑八大姨的饭桌上,人类母亲还指望着儿子来长脸的不是?不过非要问人类母亲的育儿计划,首先那些有权有势的托儿所所长们(以Google, Facebook和OpenAI等为代表)如果真雄心万丈也一定不愿透露半点风声,其次可能人类母亲自己也弄的不是太清楚,无非是走一步瞧一步罢了。然而没什么能够阻挡人类对于未来的好奇心,追本溯源按图索骥之下,说不定也能窥的一些蛛丝马迹。
因而,无妨抬抬头看看星空,也看看未来,以期AI的今后也能刷刷朋友圈,感叹一下年华易逝,甚而至于能体会得到人类母亲养儿育女的艰难,哪怕只有丝毫,大概人类母亲也就不枉费了一片可能和人类历史一样悠久的望子成龙之心。闲话少叙,让我们开开脑洞,大言不惭的看看今后NLP的一些可能性。只不过我人微言轻,暂且幻想一下皇帝的金锄头吧。
一般来说,语言学流派大约可以分为四个流派:历史比较语言学、结构主义语言学、转换-生成语言学和系统功能语言学这四个大的学派,其中历史比较语言学年代比较久远了,注重搜集多种语言历史资料,通过对比归纳总结等等方法,进而理解语言的演化和发展历程,历史比较语言学的主要贡献在于它把语言学从其他学科里抽离出来成为一门独立的学科。而结构主义语言学(大约兴起于20世纪20年代左右)的开山鼻祖索绪尔(同时他也是现代语言学之父),对于语言的理解,他有一个非常经典的类比,他认为语言就像是一盘象棋,棋盘上的每一个棋子都有特定的游戏规则,棋子只不过是这些特定游戏规则的一个实物标记而已,如果某天弄丢了某一个棋子,我们立马可以拿任何一个小物件来代替这个棋子的功能,只要游戏规则不变,那么棋局依然能够照常进行,棋子替换前后基本不会有任何差别,而索绪尔则认为语言里的基本单位诸如词语习语等等都只不过是棋盘上的棋子(signifiant,能指),重要的不是棋子本身,而是棋子背后对应的一整套规则,也就是语言里的各种结构或约定俗称的指代或用法(signifié,所指)。象棋的整个规则便是我们所使用的一整套语言系统,比如象棋的规则便是我们的汉语,而国际象棋的规则便是英语,两种象棋各自使用的棋子各有不同,规则自然也有所区别。索绪尔之后,结构主义语言学又进一步发展成为三个独立子学派,但它们的共同点都是和索绪尔一脉相承的,也就是都认为语言本质上是一个符号系统,符号只是表面,更重要的是挖掘和研究符号系统内在的规则和联系。结构主义语言学兴起后,标志着现代语言学的诞生,并且在一段时期内不仅显著的影响了后来的语言学流派,还影响了其他的一些人文学科。
索绪尔_符号系统的所指和能指.jpg到了1957年,乔姆斯基出版《句法结构》一书,标志着转换生成语言学的诞生。乔姆斯基的主要观点则认为人类天生有一套普遍语法,我们日常的语言系统只不过是这套普遍语法的形式化体现而已,因此他认为语言中存在着深层结构和表层结构,深层结构与意义和概念相关,而表层结构则与语音和符号相关,这两层结构由一套转换语法连接起来,也就是由深层结构生成表层结构。他为此还特别举了一些例子,为了说明两个句子表层结构类似乃至完全一样,但是深层语义完全不相关的现象,比如
Flying palnes can be dangerous.
这个句子可以因为同一份表层结构对应到两套不同的深层结构,而表达出完全不一样的意义,第一种是flying作为形容词,那么此时句子想表达的意义是在天上正在飞行的飞机很危险;而第二种是把"flying palnes"解构成一种动宾结构,那么这个句子表达的意思就成了开飞机这个动作很危险。乔姆斯基举这个例子,就是为了说明传统的结构主义语言学对于这种句子根本无能为力,因为它们的表层结构是完全一致的,从而得出语言有一种更为本质的深层结构的结论。但是个人理解,与其说是语言学的深层结构,还不如说是人类认知体系或者客观世界的结构化表示,因为深层结构必然对应到人类的认知体系。并且乔姆斯基过于强调了普遍语法的天生性,而忽略了后天语言的塑造和习得。后来乔姆斯基的众多弟子(乔治莱考夫等人)也公开宣判与自己老师乔姆斯基的语言学观点决裂,并各自独立发展出了一些新的语言学理论,比如乔治莱考夫的认知语言学流派。
flying_planes_can_be_dangerous.jpg我们可以发现从历史比较语言学,到结构主义语言学,再到转换生成语言学,这三个流派基本可以认为是一脉相承的,只不过所想要揭示的东西越来越深层,从历史比较语言学着重于研究语言学的一些表现形式和演化形态,到结构主义语言学着重于研究符号系统背后的结构,再到转换生成语言学认为结构主义语言学揭示的只是语言的表层结构,因而自己提出了深层结构的概念。
而到了20世纪八九十年代,以韩礼德为代表的一些语言学家,逐渐注意到无论是结构主义还是转换生成语言学,它们都片面的强调语言学的理论形式,而忽视了语言的社会的功能属性,韩礼德认为这语言的结构和语法等等理论形式,以及社会功能属性是语言的两个方面,这二者都很重要,他想要把二者统一起来,因此发展了他自己的语言学观点,他提出了系统语法和功能语法的概念,前者强调语言的内在结构和形式理论,这点和索绪尔的结构主义一脉相承,但他同时还提出了功能语法,着重语言的社会属性,说明语言是社会交流和沟通的媒介,不过由于在系统语法上的影响力远不如乔姆斯基等流派的影响力,功能语法的建树倒是别的流派所缺乏的,因此把韩礼德的学说就叫做系统功能语言学了。
而上文提到的乔姆斯基的弟子之一乔治莱考夫,认为他的老师对于人类天生具有普遍语法的观点是大错特错,尤其是转换生成语法中认为深层结构由一些有限的普遍语法组成,着重语言规则的形式化,乔治莱考夫严厉的抨击了他老师的观点,与转换生成语法着重形式和规则的探讨相反,认知语言学认为语言不是人类的一个独立系统,人类的一切语言行为都只不过是人类对于客观世界和精神世界亲身体验的外化,也就是说,语言无论把它叫做符号也好,叫做语法结构也好,叫做系统也好,都逃离不了语言是人类认知系统的一部分,语言并不独立存在。人类在长期的自然演化中,形成了一套自己独有的认知系统,而通过这套认知系统,人类可以获得对于身边客观环境以及自己内部精神世界的一套独特体验,这套体验如果映射到外在的语音或文上,便形成了我们人类的语言系统。因此人类的语言与人类其他认知能力没有本质上的区别。不过,认知语言学并没有一味地架空自己的语言学观念,它也适时地道出了语法规则和人类的认知系统有一定的对应性,言下之意便是说语法规则是人类认知系统抽象化的外在体现。可以看出,在这一点上,认知语言学比转换生成语言学又更进了一步,直接从研究人类的认知角度来研究人类的语言学。乔治莱考夫在语言学中引入了隐喻的概念,认为隐喻是人类最重要的认知活动之一,也是语言中最普遍的现象。
隐喻.PNG然而回到当下的自然语言处理,似乎现在谈认知,有点为时尚早。在自然语言处理的发展历程中,除了乔姆斯基的转换生成语言学曾经在上世纪六七十年代大放异彩,一度成为当时自然语言处理的主流方法,除此之外,似乎理论语言学界一直和自然语言处理脱钩比较严重,这一方面揭示了人类语言系统的复杂,另一方面,也预示着现在的自然语言处理缺乏严密的理论支持,说白了,现在的NLP界,尤其以BERT为代表的做法,实际上是简单粗暴的代名词,它们把人类语言习得过程中的轻量、泛化和低功耗,用大数据、大模型和大计算量炮轰的荡然无存,站在眼力所及之处的满目疮痍上,我们依然无法让机器真正明白“喵星人”这个词对于人类而言,其背后究竟意味着一个怎样的复杂情绪和体验(更有趣的是,即便对于每个人类个体而言,其背后的体验也是千差万别的,可见语言笼阔了符号性、认知性、社会性和个体性之后的复杂程度),然而在身后留下一片废墟和尸骨的BERT,正准备重建一个伟大新帝国的BERT,它究竟是要选择开心呢还是难过?对于人类母亲而言,这大概会是个问题。
不过无论如何,帝国的建立总是艰难的。让人类母亲足可欣慰的是,即便步履维艰,托儿所里的AI儿子们依然一步一步坚定的踩上了简陋的梯子,站在巨人的肩膀上回首,我们发现,和人类母亲的语言习得方式依靠某一个机缘巧合的基因突变不同,AI踏出了一条只属于它自己的路,未来的巨人肩膀和梯子在哪里,我们是否需要选用新的巨人肩膀和梯子,这都是成长的必经之痛,一切都留给未来去证实或证伪吧。
本文上两篇系列
Reference
1. https://www.frontiersin.org/articles/10.3389/fncel.2017.00212/full
2. https://waitbutwhy.com/2017/04/neuralink.html
4. https://thegradient.pub/nlp-imagenet/
5. https://arxiv.org/abs/1805.00932
6. https://distill.pub/2017/feature-visualization/
7. http://ruder.io/a-review-of-the-recent-history-of-nlp/
8. https://drive.google.com/file/d/1tAC5T1HLDPHPXY4BoIq5sM3iokQH3p7Z/view
9. https://arxiv.org/abs/1206.5538
10. https://blog.csdn.net/a635661820/article/details/44130285
11. https://arxiv.org/ftp/arxiv/papers/1611/1611.05962.pdf
12. https://www.zybuluo.com/Dounm/note/591752#41-cbow%E6%A8%A1%E5%9E%8B
13. https://arxiv.org/pdf/1301.3781.pdf
15. https://medium.com/huggingface/universal-word-sentence-embeddings-ce48ddc8fc3a
16. https://tfhub.dev/google/universal-sentence-encoder/2
17. https://www.mihaileric.com/posts/deep-contextualized-word-representations-elmo/
18. http://jalammar.github.io/illustrated-transformer/
19. https://towardsdatascience.com/bert-explained-state-of-the-art-language-model-for-nlp-f8b21a9b6270
网友评论