BERT从零详细解读:如何做预训练 – MLM+NSP

  • Post author:
  • Post category:其他


MLM: mask language model

NSP: 去判断两个句子之间的关系

BERT在预训练时使用的是大量的无标注的语料(比如随手可见的一些文本,它是没有标注的)。所以它在预训练任务设计的时候,一定是要考虑无监督来做,因为是没有标签的。

对于无监督的目标函数来讲,有两组目标函数比较受到重视,

第一种是

AR模型

,auto regressive,自回归模型。只能考虑单侧信息,典型的就是GPT。

另一种是

AE模型

,auto encoding,自编码模型。从损坏的输入数据中预测重建原始数据,可以使用上下文的信息。BERT使用的就是AE。

这么理解这个概念其实比较枯燥,我不做数学公式的推导,我举个最简单的例子。


MLM

【我爱吃饭】

AR: P(我爱吃饭)=P(我)P(爱|我)P(吃|我爱)P(饭|我爱吃);

AE:

AE模型是对句子做一个mask, mask也就是面具的意思,简单来说就是用面具去掩盖掉句子中某些,或者某几个单词。

mask之后:【我爱mask饭】

我们自己知道,虽然它是无监督的,mask之后,我知道它是“吃”这个字。

P(我爱吃饭|我爱mask饭)=P(mask=吃|我爱饭)

仔细体会一下mask这个模型,它打破了文本原有的信息,让它文本重建。原有文本是“我爱吃饭”,我把吃这个字打掉了,让模型不知道。然后在预训练的时候,让它去做文本重建。在做文本重建的时候,这个模型绞尽脑汁地从周围地文本中学习各种信息,来让自己预测出来的词汇,无限地接近原本的词汇。也就是说模型在学习\训练的时候,要做到让自己的mask的词汇出来,无限地接近,或者就是”吃“这个字。

仔细探究一下,mask这个模型,有没有缺点呢?当然是有的。

还讲刚才这个例子

【我爱吃饭】 mask之后:【我爱mask mask】

优化目标: P(我爱吃饭|我爱mask mask) = P(吃|我爱)P(饭|我爱);这个式子中”吃“和”饭“是相互独立的,也就是mask和mask之间是相互独立的。但是我们知道”吃“和”饭“,也就是这两个mask之间是相互有关系的,很大概率情况下,mask之间不是独立的。

BERT在预训练的时候呢,第一个任务就是MLM,就是用到了mask策略。需要注意的是mask的概率问题:

随机mask 15%的单词,也就是100个单词里面挑出15个单词来mask。这15%的单词又不是全部真正的mask,而是10%替换成其它单词,10%原封不动,80%替换成mask。没有看到一个很好地解释讲为什么这么去做。

mask代码实践:

for index in mask_indices:
	#80% of the time, replace with [MASK]
	if random.random() < 0.8:
		masked_token = "[MASK]"
	else
		# 10% of the time, keep original
		if random.random() < 0.5:
			masked_token = tokens[index]
		# 10% of the time, replace with random word
		else:
			masked_token = random.choice(vacab_list)


NSP


NSP一个最重要的点,就是去理解它样本的构造模式。

NSP之间有谈到过,就是在做input embedding的时候,为什么区分有两个特殊符号[CLS]和[SEP]。

NSP样本如下:

  1. 从训练语料库中取出两个连续的段落作为正样本

    两个连续的段落说明两个段落来自同一文档,一个文档就是一个主题,所以是同一个主题下两个连续的段落,顺序没有颠倒。
  2. 从不同的文档中随机创建一对段落作为负样本,区别于这里,它使用的是不同文档中随机创建一对段落,也就是说不同的主题,随便抽一个过来,直接连一下,作为负样本。

    缺点:主题预测和连贯性预测合并为一个单项任务。

    由于主题预测是非常简单的,导致整个任务

    相比于连贯性预测,主题预测非常容易学习。后续的改进,比如ALbert,直接抛弃了主题预测,Albert正样本和负样本都来自同一个文档。