深度学习建模训练总结(七):梳理NLP发展里程碑——BERT

bert是近两年非常火的模型,最近简单看了一下,确实有几个很惊艳的地方,这次就来看看bert到底是一个什么运作原理。

在正式讨论bert之前,首先需要讨论transformer到底还有什么缺点,之前也提到,transformer完全可以看成是完美版本的RNN,它一方面能够实现动态输入输出,同时也避免了长期依赖的问题,但是,在实际应用的时候,人们还是发现了几点不足,作为特征提取器,直接使用transformer和LSTM、RNN一样,只能单向处理信息,即使使用双向transformer,也不是真正意义上的双向分析,除此之外,transformer参数多,要训练这样的模型需要大量的标注数据。

bert的出现,就解决了上述的问题,一方面,它提出了双向特征提取的思路,另一方面,它结合了无监督训练的思想,使得我们可以通过无监督学习对模型进行预训练,再基于现有的标注数据进行fine-tuning,除此之外,最让我惊艳的还是语言模型这一点,通过预训练语言模型,然后针对具体的任务调整模型结构,进行fine-tuning,通过一个统一的框架完成各式各样的任务,总有种大道至简的感觉。

首先来看看语言模型是什么,一句话概括就是一个判断一句话是不是一句话的模型,比如"今天天气很好"就是合理的一句话,"今天打天气"就不是,所以语言模型就是一种关于语法、词义、用语习惯等等语言知识的分析模型,更进一步的,我甚至觉得这是一个掌握了一门语言的非智能体,而bert所做的,首先就是先预训练出这样一个懂得语言的非智能体,然后通过具体的下游任务,赋予它智能。

怎么训练出一个掌握语言的非智能体,bert提出,需要从单词层面和句子层面进行学习,单词层面就是做完形填空,给一个句子,挖空一个词,如果模型能根据上下文猜出这个词是什么,则认为模型已经具备对单词的基本认识。对于句子,bert给模型两个句子,让模型分析这两个句子是否具有连贯性,从而让模型从句子层面进行学习。

深度学习建模训练总结(七):梳理NLP发展里程碑——BERT
以上是bert的结构,为了兼顾词分析和句子分析,模型输入需要两个句子,中间通过特殊token[SEP]分隔开,而输入的第一个token[CLS]则是用于下游任务,这里之后再说。

首先我们来看看模型如何进行词分析,一开始也提到,transformer、RNN、LSTM都无法进行双向分析,而bert我认为是结合了word2vec的思想,通过上下文预测单词,从而实现通过上下文表示词向量的效果。先对输入的某个单词进行mask,然后在输出阶段预测mask的单词是什么,比如说,“the dog is lovely”,模型会随机mask成"the dog is [MASK]",在输出阶段,模型就进行预测,给出在词库中,mask属于某个词的概率。

如果直接使用上述的思路,需要注意到一个问题,[MASK]这个标签只会出现在预训练阶段,而且是每轮输入都会有,但是却不会出现在fine-tuninng阶段,这样模型在fine-tuning时就可能不知所措,为了解决这个问题,研究团队就十分聪明地提出,一方面80%的数据维持原来的mask,另外10%的词用其他词随机替换,还有10%保持不变,这时候,整个问题就变成了两部分,第一,句子挖空一个词,模型是否能猜出这个词是什么,第二,指定句子的某一个词,模型判断这个词使用是否正确,如果不正确,用哪个词更好。通过这样的改变,一方面模型依然能学习到单词层面的语义,另一方面也避免了引入[MASK]标签出现的问题。

以上的过程就是bert的mask language model,针对句子相关性分析,主要就是输入两个句子,输出一个概率判断两个句子是否有上下文关联,注意一开始提到的[CLS]token,它的作用就是在输出层给出这个概率。

我们可以看看一个具体的输入输出:

深度学习建模训练总结(七):梳理NLP发展里程碑——BERT
通过上图就可以清晰地看出输入输出到底是什么情况,其实输出就是要同时完成masked language model和next sentence prediction两个任务,一方面在mask的位置分析出模型认为正确的单词,另一方面在NSP(next sentence prediction)计算出句子相关联的概率。

除了预训练的过程,我们还需要注意单词的embedding:

深度学习建模训练总结(七):梳理NLP发展里程碑——BERT
embedding包括三个部分,第一是对单词正常的embedding,bert使用的是WordPiece embedding,第二是segment embedding,其实就是区分两个句子的embedding,具体的向量由模型学习,然后不同的句子分配不同的向量,第三个是position embedding,也是由模型学习得到,三者相加,得到最终的embedding向量。

关于embedding向量,我依然偏向于认为,这里其实是为不同句子、不同位置的不同单词分配一个新向量,模型通过大量数据进行训练学习,学习出单词在不同句子、不同位置中语义的细微变化,从而比起以往普通的embedding向量更能准确表达出单词的意思。

到目前为止,我们已经完成了输入的embedding以及预训练,得到了一个训练好的语言模型,接下来就是怎么应用到下游任务的问题。

一般来说,NLP领域号称有四大类任务:序列标注、分类任务、句子关系判断、生成式任务,这是从任务内容的角度进行划分,但是我更偏向于从输入输出的角度进行划分,也就是n-to-1、n-to-n、n-to-m,序列标注就是n-to-n,分类任务、句子关系判断就是n-to-1,生成式任务就是n-to-m。