目录

  • [1] 什么是BERT?
  • [2] BERT的结构
  • [3] BERT的预训练
  • [4] BERT的使用

[1] 什么是BERT?

BERT是一种预训练语言模型(pre-trained language model, PLM),其全称是Bidirectional Encoder Representations from Transformers。

接下来我们来解释这句话中涉及的一些点:

Q1:什么是语言模型?什么又是预训练语言模型?

语言模型 是这样一个模型:对于任意的词序列,它能够计算出这个序列是一句话的概率。比如词序列A:“知乎|的|文章|真|水|啊”,这个明显是一句话,一个好的语言模型也会给出很高的概率,再看词序列B:“知乎|的|睡觉|苹果|好快”,这明显不是一句话,如果语言模型训练的好,那么序列B的概率就很小很小。

下面给出较为正式的定义。假设我们要为中文创建一个语言模型, VVV 表示词典, V =V=V={ 猫,狗,机器,学习,语言,模型,…}, wi∈ Vw_i\in VwiV 。语言模型就是这样一个模型:给定词典 VVV ,能够计算出任意单词序列 w1, w2, . . . , wn w_1,w_2,…,w_nw1,w2,,wn 是一句话的概率 p ( w1, w2, . . . , wn)p(w_1,w_2,…,w_n)p(w1,w2,,wn) ,其中, p > = 0p>=0p>=0

——以上摘自 语言模型的概念

预训练:预训练是一种迁移学习的概念。所谓预训练模型,举个例子,假设我们有大量的维基百科数据,那么我们可以用这部分巨大的数据来训练一个泛化能力很强的模型,当我们需要在特定场景使用时,例如做医学命名实体识别,那么,只需要简单的修改一些输出层,再用我们自己的数据进行一个增量训练,对权重进行一个轻微的调整即可。预训练语言模型有很多,典型的如ELMO、GPT、BERT等。

Q2:什么是Transformer?

Bert是基于Transformer实现的,BERT中包含很多Transformer模块,其取得成功的一个关键因素是Transformer的强大作用。

Transformer可以理解为一个神经网络模块,模块内部有其复杂的网络结构,我们可以暂且将其视为黑盒,这并不影响对Bert的理解。总之,这个模块通过自注意力机制实现快速并行,改进了RNN最被人诟病的训练慢的缺点,并且可以增加到非常深的深度,充分发掘DNN模型的特性,提升模型准确率。

PS:BERT一层层深究下去的路径是这样的:【BERT】–【Transformer】–【self-attention】–【attention机制】–【seq2seq】

Q3:什么是双向(Bidirectional)?

因为BERT之前的预训练语言模型如ELMO和GPT都是单向的(ELMO可以说是双向的,但其实是两个方向相反的单向语言模型的拼接),而结合上下文信息对自然语言处理是非常重要的。Bidirectional也是Bert的主要创新点。

BERT的优势:

1.作为一种预训练模型,在特定场景使用时不需要用大量的语料来进行训练,节约时间效率高效,泛化能力较强。
2.Bert是一种端到端(end-to-end)的模型,不需要我们调整网络结构,只需要在最后加上特定于下游任务的输出层。
3.基于Transformer,可以实现快速并行,也可以增加到非常深的深度,充分发掘DNN模型的特性,提升模型准确率。
4.和ELMO,GPT等其他预训练模型相比,BERT是一种双向的模型,结合上下文来进行训练,具有更好的性能。

● 以上主要是对BERT有一个感性认识,下面我们开始具体介绍BERT的原理和结构。


[2] BERT的结构

BERT有两种size,base版一共有110M参数,large版有340M的参数,也就是说,不论是base还是large,BERT的参数量都是上亿的,这个量还是相当大的:

B E R T B A S E: L = 12 , H = 768 , A = 12 , T o t a l P a r a m e t e r s = 110 MBERT_{BASE}:L=12, H=768, A=12, Total Parameters=110MBERTBASE:L=12,H=768,A=12,TotalParameters=110M

B E R T L A R G E: L = 24 , H = 1024 , A = 16 , T o t a l P a r a m e t e r s = 340 MBERT_{LARGE}: L=24, H=1024, A=16, Total Parameters=340MBERTLARGE:L=24,H=1024,A=16,TotalParameters=340M

其中,L:Transformer blocks 层数;H:hidden size;A:the number of self-attention heads
BERT的结构没什么可说的,复杂的东西都在Transformer里了。。(可以看我另一篇讲Transformer的文章Transformer)


[3] BERT的预训练

BERT的预训练阶段包括两个任务,一个是Masked LM ,还有一个是下句预测(Next Sentence Prediction,NSP)。

Task #1:Masked LM

Masked LM 可以形象地称为完形填空问题,随机掩盖掉每一个句子中15%的词,用其上下文来去判断被盖住的词原本应该是什么。举例来说,有这样一个未标注句子 my dog is hairy ,我们可能随机选择了hairy进行遮掩,就变成 my dog is [mask] ,训练模型去预测 [mask] 位置的词,使预测出 hairy的可能性最大,在这个过程中就将上下文的语义信息学习并体现到模型参数中去了。

这里需要说明,GPT使用统计语言模型,这限制了它只能是单向的,而BERT通过Masked LM能够提取上下文信息。更一般地:

  • AR模型,auto regressive,自回归模型。 自回归模型可以类比为早期的统计语言模型(Statistical Language Model),也就是根据上文预测下一个单词,或者根据下文预测前面的单词,只能考虑单侧信息,典型的如GPT,而ELMo 是将两个方向(从左至右和从右至左)的自回归模型进行了拼接,实现了双向语言模型,但本质上仍然属于自回归模型
  • AE模型,auto encoding,自编码模型。 从损坏的输入数据(相当于加入噪声)中预测重建原始数据,可以使用上下文的信息。BERT使用的就是AE。劣势是在下游的微调阶段不会出现掩码词,因此[MASK] 标记会导致预训练和微调阶段不一致的问题。

所以该方法有一个问题,因为是mask15%的词,其数量已经很高了,这样就会导致某些词在fine-tuning阶段从未见过,为了解决这个问题,作者做了如下的处理:

  • 80%的时间是采用[mask],my dog is hairymy dog is [MASK]
  • 10%的时间是随机取一个词来代替mask的词,my dog is hairy -> my dog is apple
  • 10%的时间保持不变,my dog is hairy -> my dog is hairy

为什么使用这个策略?
(其实我目前还不能很好的理解,先将其他地方看到的说法放在这里)

  • 这是因为transformer要保持对每个输入token分布式的表征,否则Transformer很可能会记住这个[MASK]就是”hairy”(这个地方的理解,强行记住了位置和masked的分布,而没有真正理解上下文),从而导致若训练样本和微调的样本mask不一致的情况下,模型预测出现很大的偏差。
  • 如果仅使用[MASK]或者随机的词,那么模型可能学习到的信息都是错误的单词(认为这个地方的单词就是不正确的);
  • 若仅使用正确的单词,那么模型学到的方法就是直接copy(根据学到的上下文,直接断定),从而学不到完整的上下文信息。

综上三个特点,必须在正确的信息(10%)、未知的信息(80% MASK,使模型具有预测能力)、错误的信息(加入噪声10%,使模型具有纠错能力)都有的情况下,模型才能获取全局全量的信息。

Task #2:Next Sentence Prediction

很多下游任务(QA和natural language inference)都是基于两个句子之间关系的理解,基于此项任务,为了增强模型对句子之间关系的理解能力。训练数据选择两个句子(50%情况下是真正相连的两个句子,50%是随机拼接的两个句子),判断第二个句子是不是真正的第一个句子的下文。
其输入形式是,开头是一个特殊符号[CLS],然后两个句子之间用[SEP]隔断:

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

实际构建预训练任务时,是首选设计好 “下句预测” 任务,生成该任务的标注信息,在此基础上构建 “Masked LM” 任务,生成掩码语言模型的标注信息。考虑到预训练涉及两个句子,BERT 采用如下的输入信息表征方案:

token embedding :将各个词转换成固定维度的向量。在BERT中,每个词会被转换成768维的向量表示。在实际代码实现中,输入文本在送入token embeddings 层之前要先进行tokenization处理。此外,两个特殊的token会被插入到tokenization的结果的开头 ([CLS])和结尾 ([SEP])
segment embedding 用于区分一个token属于句子对中的哪个句子。Segment Embeddings 层只有两种向量表示。前一个向量是把0赋给第一个句子中的各个token, 后一个向量是把1赋给第二个句子中的各个token。如果输入仅仅只有一个句子,那么它的segment embedding就是全0 。
position embedding:Transformers无法编码输入的序列的顺序性,所以要在各个位置上学习一个向量表示来将序列顺序的信息编码进来。加入position embeddings会让BERT理解下面下面这种情况,“ I think, therefore I am ”,第一个 “I” 和第二个 “I”应该有着不同的向量表示。

这3种embedding都是768维的,最后要将其按元素相加,得到每一个token最终的768维的向量表示。

[4] BERT的使用

BERT最终输出的就是句子中每个token的768维的向量,第一个位置是[CLS],它的向量表示蕴含了这个句子整体的信息,用于做文本分类等句子级任务;对于序列标注等token级任务,就需要使用到每一个token的向量表示。只要将768维向量通过一个线性层映射到最终的分类空间中即可。