21

BERT and it's family

 3 years ago
source link: https://www.wmathor.com/index.php/archives/1508/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

T5k3wyYOgjKtLFf.png#shadow

大名鼎鼎的芝麻街

Mr1jbW9q6yhOJXV.png#shadow

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

上图所示的模型(除了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

cFrqYOU9ZTegRhf.png#shadow

除了模型压缩以外,近年来比较火的尝试还有模型架构的设计。比方说 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)任务

CAT5v2iREBlLhzU.png#shadow

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

aRm3bUZpVKLPtvz.png#shadow

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

xdRTPOMbmpQfgJh.png#shadow

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

5HljdCGmSxaKrZD.png#shadow

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

Combination of Features

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

v2EBb2f.png!mobile

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

肖涵的观点认为:

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

Why Pre-train Models?

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

cOynJK5gtDdkZBH.png#shadow

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

MXrKTm6SWtIR4Eu.png#shadow

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

ELMo

oRSwkaPJZHEG3lA.png#shadow

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

zcEFerpnhMHlaIC.png#shadow

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

ycaYpuCZT3ADiM6.png#shadow

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

Whole Word Masking (WWM)

Kl9HoxAI8O21VWn.png#shadow

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

SpanBERT

8LDBJPWVnTq694F.png#shadow

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

IVtQavh8pJTb5WU.png#shadow

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

XLNet

26nMuBoNdDC9xap.png#shadow

5xamubK3oTpihRg.png#shadow

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

MASS / BART

KMboCnsf5y2pxDc.png#shadow

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

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

isILA86TawzMH94.png#shadow

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

UniLM

AGQonZCjKeXJux1.png#shadow

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

ELECTRA

5V8hjaTDJtflKHC.png#shadow

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

7KskSc9j5BXCQqH.png#shadow

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

XiYFMzvKTBAUjgf.png#shadow

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK