https://space.bilibili.com/181990557

MENU

BERT and it's family

November 6, 2020 • Read: 240 • Deep Learning阅读设置

大名鼎鼎的芝麻街

预训练语言模型的缩写大多是芝麻街的人物。这显然是起名艺术大师们的有意为之。他们甚至都可以抛弃用首字母缩写的原则去硬凑出芝麻街人名

上图所示的模型(除了Big Bird,因为没有这个模型)他们之间都有一些共同点,就是能通过一个句子的上下文来给一个词进行Embedding,而能达到这种目的的网络架构有很多,例如LSTM,Self-attention layers,Tree-based model(注重文法,一般表现不佳,只有在文法结构非常严谨的情况下表现好)等等

Smaller Model

预训练语言模型比来比去,变得越来越巨大,越来越臃肿,越来越玩不起。然而,让模型变小,就好比我们去到非洲,可以让人民币在一线城市的购买力变得更多。这就是穷人用的 BERT。在Distill BERTTiny BERTMobile BERTQ8BERTALBERT

授人以鱼不如授人以渔,究竟有哪些方法可以使Model变小呢?可以参考李宏毅老师模型压缩的视频讲解,以及All The Ways You Can Compress BERT这篇文章。常见的方法有以下几种

  • Network Pruning 剪枝
  • Knowledge Distillation 知识蒸馏
  • Parameter Quantization 参数量化
  • Architecture Design 结构设计

Network Architecture Improvements

除了模型压缩以外,近年来比较火的尝试还有模型架构的设计。比方说Transfomer-XL通过理解跨片段的内容,可以处理非常长的序列;ReformerLongformer可以使得自注意力的复杂度变小,从$O(N^2)$到$O(NlogN)$甚至更低

How to Fine-tune

这一部分李宏毅老师讲了非常多,但我个人感觉有很多都是比较简单的内容。比方说输入一个句子如何进行分类,这个大家其实做的都比较多了,常见的做法就是利用[CLS]的输出后面跟一个线性分类层,又或者是将所有token的输出求一个Average,再送入线性分类层。上述两种方法的目的都是要做分类,但是它们的本质区别在于究竟用什么东西来代表一个句子的Embedding比较好,关于这个可以看一下Sentence-BERT的论文,或者是Sentence-BERT详解这篇文章,里面有一些实验证明了用哪些东西代表一个句子的向量比较好

Extraction-based QA

可能大家见的不多的是如何利用预训练模型做Extraction-based QA (Question Answering)任务

比方说现在将一篇文档和一个问题丢入QA Model,模型会输出两个整数$s$和$e$,这两个整数代表这个问题的答案就是文档中第$s$个词到第$e$个词,即$\{d_s,...,d_e\}$

得到这两个整数的方式也很有意思。首先我们生成两个向量(上图中橙色和蓝色),用其中一个(橙色)向量去和document所有位置的输出做一个dot product,之后再经过一个Softmax得到一系列概率值,我们取最大概率值所在的下标(其实就是argmax)就得到了答案的开始位置$s=2$

答案的结束位置$e$得到的方式也差不多,就是用另一个(蓝色)向量去和document所有位置的输出做一个dot product,同样经过Softmax之后得到概率最大值所在的下标。那么最终答案就是$[s,e]$这个区间内的单词

回到主题,预训练语言模型要如何进行微调呢?一种方法是固定预训练的模型,让它作为一个特征提取器,训练的时候,只更新后面接的Task-specific模型的参数;另一种方法是不固定预训练语言模型的参数,所有参数在训练过程中都进行更新。不过就我本人做过的很多实验来看,后者效果是比前者好的,但是问题在于,很多预训练模型特别大,经常11G的显存都不够,所以不得不采用前一种方法

Combination of Features

我们知道BERT有很多Encoder Layer,大家常规的做法都是提取最后一层的输出来做下游任务,但实际上这是最优解吗?其实就有人在NER任务上做过一个实验,将不同层的输出进行各种组合,得到的效果如下

肖涵在 Github 上创建了一个名为 bert-as-service 的开源项目,该项目旨在使用 BERT 为您的文本创建单词嵌入。他尝试了各种方法来组合这些嵌入,并在项目的 FAQ 页面上分享了一些结论和基本原理

肖涵的观点认为:

  1. 第一层是嵌入层,由于它没有上下文信息,因此同一个词在不同语境下的向量是相同的
  2. 随着进入网络的更深层次,单词嵌入从每一层中获得了越来越多的上下文信息
  3. 但是,当您接近最后一层时,词嵌入将开始获取 BERT 特定预训练任务的信息(MLM 和 NSP)
  4. 使用倒数第二层比较合理

Why Pre-train Models?

为什么我们要使用这些预训练的模型?一个很明显的道理是,我们没那么多钱去从头训练一个比较大的模型,所以直接拿别人训练好的来用就行了

当然,EMNLP 2019的一篇文章Visualizing and Understanding the Effectiveness of BERT从学术角度仔细分析了为什么要使用预训练模型,文章表明,预训练模型可以大大加速损失的收敛,而不使用预训练模型,损失比较难下降。可以理解为,预训练模型提供了一种比随机初始化更好的初始化

另一个结论是,预训练模型可以大大增加模型的泛化能力。上图表示给模型不同参数时,模型训练后结束点的损失会抵达一个local minima的位置。这个local minima的位置越陡峭,则泛化能力越差,因为输入稍微变化,它的损失就会有很大的变动;反之,这个local minima越平缓,则泛化能力越强

ELMo

ELM是时目前来说比较知名的双向网络。传统的LSTM只是从左往右过一遍句子,那预测下一个token所依赖的信息就只能取决于它左边的内容,为了能真正利用这个token的上下文,我们可以从右到左再过一遍句子,即BiLSTM。但实际上还不够,因为当模型在encode $w_1,w_2,w_3,w_4$的时候,它没看到句子后面的部分;而在encode $w_5,w_6,w_7$的时候,也没考虑到句子前面的部分,所以ELMo在底层进行编码的时候并不是真正的双向。而在上层,由于两边的embedding进行了concat,此时它才同时看到了双向的信息

BERT

对于Transformer类模型(典型代表就是BERT),自注意力机制使得它能够同时看到上下文,每一个token两两之间都能交互,唯一要做的只是随机地把某个token用[MASK]遮住就可以了

如果你回溯历史,回到Word2vec刚刚掀起NLP革命的时候,你会发现CBOW的训练方式和BERT几乎一样,它们的主要区别在于,BERT能关注的范围长度是可变的,而CBOW的范围是固定的

Whole Word Masking (WWM)

随机地mask掉某个token效果是否真的好呢?对于中文来说,词是由多个字组成的,一个字就是一个token。如果我们随机mask掉某个token,模型可能不需要学到很多语义依赖,就可以很容易地通过前面的字或后面的字来预测这个token。为此我们需要把难度提升一点,盖住的不是某个token,而是某个词(span),模型需要学到更多语义去把遮住的span预测出来,这便是BERT-wwm。同理,我们可以把词的span再延长一些,拓展成短语级别、实体级别(ERNIE)

SpanBERT

还有一种BERT的改进叫SpanBERT。它每次会盖住$n$个token,其中$n$是根据上图所示的概率得到的。实验结果发现,这种基于概率选择盖住多少个token的方式在某些任务上要更好一些

SpanBERT还提出了一种名为Span Boundary Objective (SBO)的训练方法。一般我们训练只是把masked的tokens给训练出来。而SBO希望通过被盖住范围的左右两边的输出,去预测被盖住的范围内有什么样的东西。如上图所示,将$w_3$和$w_8$的输出以及一个索引送入后续的网络中,其中这个索引表示我们希望预测的是span中哪个位置的词

XLNet

关于XLNet更详细的讲解可以看这篇博客。简单来说,XLNet认为BERT类模型训练和测试阶段不统一(训练阶段有[MASK]token,测试阶段没有),因此可能会存在某些问题。如果从Autoregressive的角度去看XLNet,其实就是将输入打乱顺序作为输入,然后从左往右预测下一个token。如果以AutoEncoder (BERT)的角度去看XLNet,我们希望根据[MASK]左边或者右边的信息去预测[MASK]位置的词。与BERT不同的地方在于,XLNet的输入没有[MASK]的存在

MASS / BART

BERT类模型缺乏生成句子的能力,所以它不太适合做Seq2Seq的任务,而MASS和BART这两个模型就解决了BERT不擅长生成的问题。我们首先把一个句子输入到Encoder,我们希望Decoder的output就是Encoder的input,但有一点要注意的是,我们必须将Encoder的input做一定程度的破坏,因为如果没有任何破坏,Decoder直接将Encoder的输入copy过来就行了,它可能学不到什么有用的东西

MASS的做法是,把输入的一些部分随机用[MASK]token遮住。输出不一定要还原完整的句子序列,只要能把[MASK]的部分预测正确就可以了

在BART的论文中,它又提出了各式各样的方法,除了给输入序列随机mask以外,还可以直接删除某个token,或者随机排列组合等。关于BART更详细的讲解可以看这篇文章

UniLM

还有一个模型叫UniLM,它既可以是编码器,也可以是解码器,还可以是Seq2Seq。UniLM由很多Transformer堆叠,它同时进行三种训练,包括BERT那样作为编码器的方式、GPT那样作为解码器的方式、MASS/BART那样作为Seq2Seq的方式。它作为Seq2Seq使用时,输入被分为两个片段,输入第一个片段的时候,该片段上的token之间可以互相注意,但第二个片段,都只能看左边token

ELECTRA

预测一个东西需要的训练强度是很大的,ELECTRA想要简化这件事情,转为二分类问题,判断输入的某个词是否被随机替换了

但问题来了,怎样把一些词进行替换,同时保证文法没错,语义也不是那么奇怪的句子呢?因为如果token被替换成了一些奇怪的东西,模型很容易就能发现,ELECTRA就学不到什么厉害的东西了。论文用了另一个比较小的BERT去输出被mask的单词,这里不需要用很好的BERT,因为如果BERT效果太好,直接就输出了和原来一摸一样的单词,这也不是我们期望的。这个架构看上去有点像GAN,但实际上它并不是GAN,因为GAN的Generator在训练的时候,要骗过Discriminator。而这里的small BERT是自己训练自己的,只要把被mask的位置预测出来就好了,至于后面的模型预测的对不对和它没有关系

ELECTRA训练效果很惊人,在相同的预训练量下,GLUE上的分数比BERT要好很多,而且它只需要1/4的运算量就可以达到XLNet的效果

T5

预训练语言模型需要的资源太多,不是普通人随便就可以做的。谷歌有篇论文叫T5,它展现了谷歌庞大的财力和运算资源,这篇文论把各式各样的预训练方法都尝试了一次,然后得到了一些结论,让别人没有研究可做

Reference

Archives Tip
QR Code for this page
Tipping QR Code
Leave a Comment

2 Comments
  1. Oleg Oleg

    Very cool tutorial, but I don't know 中文((

    1. mathor mathor

      @OlegI am sorry to hear that@(狂汗)