大规模预训练模型BERT

Dec 29, 2024
1 views
NLP

Self-Supervised Learning,又称为自监督学习,我们知道一般机器学习分为有监督学习,无监督学习和强化学习。 而 Self-Supervised Learning 是无监督学习里面的一种,主要是希望能够学习到一种通用的特征表达用于下游任务 (Downstream Tasks)。 其主要的方式就是通过自己监督自己。作为代表作的 kaiming 的 MoCo 引发一波热议, Yann Lecun也在 AAAI 上讲 Self-Supervised Learning 是未来的大势所趋。所以在这个系列中,我会系统地解读 Self-Supervised Learning 的经典工作。

本文主要介绍 Self-Supervised Learning 在 NLP领域的经典工作:BERT模型的原理及其变体。本文来自台湾大学李宏毅老师PPT:

https://speech.ee.ntu.edu.tw/~hylee/ml/ml2021-course-data/bert_v8.pdfspeech.ee.ntu.edu.tw/~hylee/ml/ml2021-course-data/bert_v8.pdf

1 .芝麻街

在介绍 Self-Supervised Learning 之前就不得不先介绍下 芝麻街 。因为不知道为什么,Self-Supervised Learning的模型都是以芝麻街的人物来命名的。那么这些芝麻街的角色都是什么样的模型呢?在我们实际了解他们做的事情之前,我们先来看看他们的名字:

ELMo代表的是红色怪物,是Embedding from Language Models的缩写,它是最早的 Self-Supervised Learning的模型。后来又有了BERT (Bidirectional Encoder Representations from Transformers),是我们最耳熟能详的的 Self-Supervised Learning model。然后BERT最好的朋友是ERNIE (Enhanced Representation through Knowledge Integration),是紧接着BERT出现的模型。后来又出现了一个叫做大鸟 (Big Bird: Transformers for Longer Sequences) 的模型。

image

图1:芝麻街的角色们

BERT这个模型的最大特点是比较大,它有340 M的参数量 (parameters),这个数值究竟有多大呢?我们可以横向对比几个数据:

最近很火的 视觉 Transformer 模型 ViT 及其变体 DeiT 模型的参数量如下表所示。结合最近比较火的"爽"为单位,我们定义:1爽=6.4亿=6.4×e8=640M。但其实除了BERT之外,还有一些更大的模型,如GPT-2, GPT-3, T5等等。

通过上面这个表相信你能直观感受到主流这些预训练模型的庞大的参数量,最近出的Switch Transformer的参数量甚至达到了1.6T,是GPT-3的近10倍,而GPT-3的参数量又是Turing NLG的10多倍。

2. BERT 与 Self-Supervised Learning

接下来就正式介绍什么是 Self-Supervised Learning

如下图2所示,我们之前在做 Supervised Learning的时候,如何让model输出我们想要的 \(y\) 呢?你得要有label的资料。假设今天要做情感分析,让机器看一段文字,输出对应的情感是正面的还是负面的。那你要有一大堆文章和对应的label,才能够训练model。

那 Self-Supervised 就是在没有label的情况下自己想办法监督自己。还是同样的一堆资料 \(x\) ,我们现在把它分成2部分:\(x^{'}\) 和 \(x^{''}\) 。然后把 \(x^{'}\)输入到模型里面,让它输出 \(y\),然后我们让 \(y\) 与 \(x^{''}\) 越接近越好,这个就是 Self-Supervised Learning。换言之在 Self-Supervised Learning里面输入的一部分作为了监督信号,一部分仍作为输入。

image

图2:Supervised Learning 和 Self-Supervised Learning

Self-Supervised Learning 这个词汇最早是Yann LeCun说的,下图是Yann LeCun于19年4月30日在Facebook上的贴文。为什么不直接叫做Unsupervised Learning呢?因为Unsupervised Learning是一个比较大的family,里面有很多不同的方法,为了突出方法的专门化,所以就叫做 Self-Supervised Learning。

image

图3:Yann LeCun发言

下面的问题是怎么把输入的 \(x\) 分成2部分: \(x\)′ 和 \(x\)″ 呢?我们看下面的BERT模型,它的架构就是Transformer 的 Encoder,里面有很多Self-attention,MLP,Normalization等等。

BERT可以做的事情也就是Transformer 的 Encoder 可以做的事情,就是输入一排向量,输出另外一排向量,输入和输出的维度是一致的。那么不仅仅是一句话可以看做是一个sequence,一段语音也可以看做是一个sequence,甚至一个image也可以看做是一个sequence。所以BERT其实不仅可以用在NLP上,还可以用在CV里面。所以BERT其实输入的是一段文字,如下图4所示。

image

图4:BERT的架构就是Transformer 的 Encoder

接下来要做的事情是把这段输入文字里面的一部分随机盖住。随机盖住有2种,一种是直接用一个Mask把要盖住的token (对中文来说就是一个字)给Mask掉,具体是换成一个特殊的字符。另一种做法是把这个token替换成一个随机的token。

image

图5:把这段输入文字里面的一部分随机盖住

接下来把这个盖住的token对应位置输出的向量做一个Linear Transformation,再做softmax输出一个分布,这个分布是每一个字的概率,如下图6所示。

那接下来要怎么训练BERT呢?因为这时候BERT并不知道被 Mask 住的字是 "湾" ,但是我们知道啊,所以损失就是让这个输出和被盖住的 "湾" 越接近越好,如下图7所示。

image

图6:把这个盖住的token对应位置输出的向量做一个Linear Transformation

image

图7:让这个输出和被Mask 住的 token 越接近越好

其实BERT在训练的时候可以不止是选取一个token,我们可以选取一排的token都盖住,这就是 SpanBERT 的做法,至于要盖住多长的token呢?SpanBERT定了一个概率的分布,如图8所示。有0.22的概率只盖住一个token等等。

image

图8:SpanBERT定了一个概率的分布

除此之外,SpanBERT还提出了一种叫做Span Boundary Objective (SBO) 的训练方法,意思是说:

image

图9:Span Boundary Objective (SBO)

盖住一串token以后,用这段被盖住的token的左右2个Embedding去预测被盖住的token是什么。SBO把盖住的部分的左右两边的Embedding吃进来,同时还输入一个数字,比如说3,就代表我们要还原被盖住的这些token里面的第3个token。

其实BERT在训练的时候不只是要做Masking Input,还要做的一件事情是Next Sentence Prediction。那我们会先爬取很多的sentence,然后随机地找两个sentence,给他俩中间加上一个分隔符号 [SEP],代表前后是2个不同的句子;然后在最前面加上1个分类符号 [CLS],这个东西相当于是 ViT 里面的 class token。把这些拼在一起以后输入到BERT里面,当然输出也会是一个Sequence,但是我们只看 [CLS] 的输出,并把这个输出通过一个 Linear Transformation,得到二元的分类 "Yes/No"。在这里它代表的意义是:"Sentence 1 和Sentence 2 是不是相接的?"如下图10所示。

image

图10:Next Sentence Prediction

但是后续有文章,比如Robustly optimized BERT approach (RoBERTa) 就说明Next Sentence Prediction是对训练没有什么特别的帮助的,一种可能的原因是判断前后两个句子是不是相接的这个任务太简单了,要知道两个句子是不是应该接在一起也许不是一个特别难的任务。所以BERT可能没有借助Next Sentence Prediction这个任务学到太多的有用的东西。后续的 ALBERT中又提出了sentence order prediction的办法,被认为是有作用的,就是判断连续的两个sentence的先后顺序。

下面的问题是:BERT到底应该怎么用呢?

我们现在训练的BERT模型其实只会做2件事情:

  • Masked token prediction:预测盖住的词是什么。
  • Next sentence prediction:预测两个句子是不是前后接起来的。
    那么接下来神奇的地方是:BERT可以被用在很多下游任务 (Downstream Tasks) 上,这些下游任务可能和Masked token prediction,Next sentence prediction毫无关联,但是BERT可以作为预训练模型被用在它们上面。

GLUE里面的9个任务如上图12所示。我们要把预训练模型在这9个任务上面分别微调,所以其实会得到9个模型,分别做在9个任务上,看看这9个任务上正确率的平均是多少,这个平均数值就代表了Self-Supervised-Learning模型的好坏。下图11代表的是近年来的模型的GLUE得分,黑色的线代表的是人类的得分,最新的XLNet就在这个任务上超越了人类水平,说明GLUE 这个任务已经被玩坏了,所以后续有了更难的自然语言任务:SuperGLUE让机器来解。ng的这个过程,我们叫做Fine-tune。

image

图11:BERT的训练过程

那接下来训练好BERT以后,就要测试一下它的能力如何。通常你需要把它在多个任务上进行测试,刚才我们说BERT相当于是胚胎干细胞,那你需要让它分化成多种专门的细胞,在每个任务上进行测试。这个任务集最知名的标杆就叫做:

General Language Understanding Evaluation (GLUE)

GLUE Benchmarkgluebenchmark.com/

image

image

图12:GLUE:预训练模型评测任务集最知名的标杆

GLUE里面的9个任务如上图10所示。我们要把预训练模型在这9个任务上面分别微调,所以其实会得到9个模型,分别做在9个任务上,看看这9个任务上正确率的平均是多少,这个平均数值就代表了Self-Supervised-Learning模型的好坏。下图13代表的是近年来的模型的GLUE得分,黑色的线代表的是人类的得分,最新的XLNet就在这个任务上超越了人类水平,说明GLUE 这个任务已经被玩坏了,所以后续有了更难的自然语言任务:SuperGLUE让机器来解。

image

图13:近年来的模型的GLUE得分

接下来的问题是:如何使用BERT呢?

Case1:情感分析:输入一个句子,输出对应的情感类别。

BERT是怎么解Sentiment Analysis的问题呢?给它一个句子,在这个句子前面放上 class token,这步和 ViT 是一模一样的。同样地,我们只取输出的Sequence里面的class token对应的那个vector,并将它做Linear Transformation+Softmax,得到类别class,就代表这个句子的预测的情感,如下图14所示。

值得注意的是,对于这种下游任务你需要有labelled data,也就是说BERT其实没办法凭空解Sentiment Analysis的问题,也是需要一部分有监督数据的。我们此时的情感分析模型包括BERT部分和Linear Transformation部分,只是BERT部分的初始化来自Self-Supervised Learning,而Linear Transformation部分采样的是随机初始化。这两部分的参数都用Gradient Descent来更新。

image

图14:使用BERT做情感分析

下图15其实是个对比,就是BERT部分不用预训练模型的初始化 (scratch) 和用了预训练模型的初始化 (fine-tune) 的不同结果,不同颜色的线代表GLUE中的不同任务。 不用预训练模型的初始化会导致收敛很慢而且loss较高,说明预训练模型的初始化的作用。

image

图15:预训练模型的初始化结果对比

Case2:词性标注:输入一个句子,输出每个词对应的词性。

与 Case1 不一样的是,此时输出的每个token都会做Linear Transformation+Softmax,得到类别class,就代表这个token的词性,如下图16所示。

image

图16:用BERT做词性标注

Case3:Natural Language Inference (NLI):输入两个句子,输出一个类别。

比如下图17中所举的例子,其中一个句子是:A person on a horsejumps over a broken down airplane。另一个假设是:hypothesis: A person is at a diner。很明显是矛盾的,所以此时模型要输出contradiction。这个应用场景其实也是很多,比如输入一篇文章和一个留言,来让模型看看留言到底是赞成文章,还是反对文章的。把2个句子输入以后,在最前面加上1个分类符号 [CLS],把这些拼在一起以后输入到BERT里面,当然输出也会是一个Sequence,但是我们只看 [CLS] 的输出,并把这个输出通过一个 Linear Transformation,得到分类 "赞成/反对/中性"。当然此时我们也同样需要少量的监督信息。

image

image

图17:Natural Language Inference (NLI)

Case4:Extraction based Question Answering (QA):输入一篇文章和问题,输出问题答案。

如下图18所示,输入是文章 \(D\) 和问题 \(Q\) ,输出是两个正整数 \(s\) 和 \(e\) 。代表index,我们把文章截取从 \(s\) 到 \(e\) 的片段之后就是问题的答案 \(A=\{d_s,...,d_e\}\)

image

image

图18:Extraction based Question Answering (QA)

针对这种情况训练BERT的方法如下图19所示,输入有一个question和一个document,那么它们之间要用分隔符号 [SEP] 分开。接下来我们从输出的Sequence里面挑出document对应的输出。我们除了BERT部分之外唯一需要训练的是2个vector,分别是图中橙色和蓝色的vector,这两个vector的维度和BERT的embedding dimension的维度是一致的,比如BERT的嵌入维度是768维,那这2个向量也是768维的。

接下来要做的是:把橙色向量和document对应的输出的每个向量做内积以后再做softmax操作,得到的最大值的索引就是document的起始位置,比如第2个0.5最大,那么这里的 \(s\) 就等于2。然后,把蓝色向量和document对应的输出的每个向量做内积以后再做softmax操作,得到的最大值的索引就是document的终止位置,比如第3个0.7最大,那么这里的 \(e\) 就等于3。再强调一遍,橙色向量和蓝色向量是随机初始化的,而BERT部分是使用Self-Supervised Learning 预训练好的。

image

image

图19:训练BERT的方法

Case5:General Sequence 输出一般的句子

下一个问题是如何让预训练的模型输出一般的句子呢?或者,如何把Pre-trained model用在这种seq2seq的model里面?

一种做法是如下图20所示,我们把这个Pre-trained model作为一个Encoder,输入一个sequence以后得到输出,我们再把这个得到的输出送到一个Decoder中,这个Decoder是task-specific的,然后Decoder会输出一串token sequence,这样就结束了。但这样的坏处是:我们有的这个Encoder是经过了预训练的Pre-trained model,但是Decoder不是,它没有经过预训练,不是Pre-trained model。一般来讲我们的Task-specific的data会很少,所以这个Decoder如果很大的话会训不好,我们希望整个模型里面多数的参数都是Pre-trained的。

image

图20:一种seq2seq的model的做法

所以现在我们采取另外一种做法,如下图21所示。我们把输入序列 \(w_1\),\(w_2\) 丢进模型之后呢,给模型一个特别的符号 [SEP],模型看到这个分隔符号之后知道输入已经完成,该输出sequence了。于是在输入 [SEP]之后模型经过一个Task-specific的层之后输出 \(w_3\) ,再输入 \(w_3\) 之后模型经过一个Task-specific的层之后输出 \(w_4\) ,再输入 \(w_4\) 之后模型经过一个Task-specific的层之后输出 \(w_5\) ,以此类推。

image

图21:另一种seq2seq的model的做法

3. BART, MASS 和 ELECTRA 模型