BERT系列模型总结

文章目录

  • 参考
  • Transformer
  • Transformer-XL
  • BERT
  • XLNET
  • RoBERTa
  • ALBert

参考

NLP预训练模型:从transformer到albert
XLNet:运行机制及和Bert的异同比较
从语言模型到Seq2Seq:Transformer如戏,全靠Mask
Transformer和Bert相关知识解答
Transformer-XL: Attentive Language Models Beyonds a Fixed-Length Context

Transformer

transformer是这些模型的基础,具体的原理+代码的介绍可以看我的博客:Transformer 代码+原理。

在上一篇博客中详细介绍了transformer的原理和pytorch版本的代码,这里将一些问题拎出来,再强调一下。

  1. 为什么在embedding后要乘以embedding size的开方?
    可能因为embedding matrix的初始化方式是xavier init,导致其方差为1/embedding size,所以乘以其平方使得最终的方差是1,从而帮助embedding matrix的收敛。
  2. 为什么要加入positional encoding?
    因为纯attention是天然对称的,在自然语言处理中,token在文本中的位置是一个比较重要的特征,因此需要加入positional encoding。至于为什么要用这种encode方式是因为这种方式一定程度上可以表达token之间的相对位置,以及具有远程衰减的特性。
  3. 为什么算attention的时候要除以 d k \sqrt d_k d k
    因为 Q K QK QK有时候乘积会很大,如果直接对其softmax会将有的结果推入到很小的区域,这样会造成很难收敛,所以除以 d k \sqrt d_k d k一定程度上可以帮助收敛。为什么要除以 d k \sqrt d_k d k,是因为如果我们将 K , Q K,Q KQ视作均值为0,方差为1的随机变量,那么计算后的结果均值为0,方差为 d k d_k dk,除以 d k \sqrt d_k d k能使得最后的方差为1。
  4. 在计算注意力分数的时候如何做mask操作?
    有两个mask操作,一个是padding mask(因为句子长度不够,所以用padding填充,但是这一部分其实是不用算的),另一个是在decoder部分为了防止信息被“偷看”,而使用的mask self-attention。
  • padding mask:解决方式就是在填充的位置赋予一个很小的负值/负无穷(-np.inf)的值,经过softmax后的得分为0,即没有注意力分配到这个上面。
  • mask self-attention: 产生一个对角线为0的上三角矩阵。这样就掩盖掉后面的信息了。
  1. 为什么要使用multi-head attention?
    论文上的说法是使用multi-head可以得到不同的子空间,从而得到不同的信息。但是也有很多paper对此说法保持怀疑态度,并做了很多研究,这里参考了为什么Transformer 需要进行 Multi-head Attention?的解答。
    有大量的paper表明,transformer或者bert等的特定层是有特定的功能的,比如说,底层更偏向于关注语法,顶层更偏向于关注语义。这样看来,在同一层transformer关注的点是相同的,说明每个head关注点是相同的,但是也有研究表明,在每层总有一两个head关注点独一无二。这可能是因为初始化的不同所导致的(因为如果相同的初始化,经过同样的计算,其参数会是相同的)。
    众多研究表明Multi-Head其实不是必须的,去掉一些头效果依然有不错的效果(而且效果下降可能是因为参数量下降),这是因为在头足够的情况下,这些头已经能够有关注位置信息、关注语法信息、关注罕见词的能力了,再多一些头,无非是一种enhance或noise而已。

  2. 为什么使用LN对其进行归一化?
    关于BN和LN我之前写过一篇博客来介绍:聊聊深度学习中的BN和LN。
    在这篇博客中我有介绍过说BN更适合做CV,因为BN是对mini-batch中的某一个维度进行归一化,但这不适合NLP任务,LN是对一个样本中的所有维度进行归一,比较适合NLP任务。
    选择什么样的归一化方式,取决于关注数据的哪部分信息,如果某个维度的差异度很重要,需要被拟合,那么就别在那个维度归一化。

  3. 为什么multi-head attention后面要加一个ffn?
    我之前的博客文本分类算法中就提到过,增加全连接层,能够学习特征之间的关联与权重(也就是对特征进行加权以及合并),能够提升整个block的非线性变换的能力。

Transformer-XL

对于长文本,经典的transformer会按照固定的长度(512字符),直接将文本划分为若干个segment。很有可能一句话就会被分到两个segment之中,导致上下文碎片化。
而且在模型训练时,如下图(a),两个segment之间并不产生联系,他们被单独训练,而且这显然不符合现实。
在模型评估之时,如下图(b),每次只计算最后一个位置的token,计算完后,整个序列向后移动一个位置。
BERT系列模型总结_第1张图片
Transformer-XL通过引入Segment-Level recurrence mechanism来建模更长序列,它通过融合前后两个Segment的信息来到这个目的。
BERT系列模型总结_第2张图片

  • 在模型训练阶段,会缓存上一个segment的输出序列,在计算下一个segment输出时,会使用上一个segment的缓存信息,帮助模型看到更远的地方,同时也避免了上下文碎片化的问题。
  • 在评估阶段,因为缓存了之前的输出序列,因此不用重新计算,加快了推理速度。

在这里插入图片描述
BERT系列模型总结_第3张图片
这里采用了相对位置编码,Vanilla Transformer中的绝对位置编码不适合transformer-XL,因为这会造成位置信息的混乱——我们不知道某个相同的位置编码到底是来自于哪个segment。
BERT系列模型总结_第4张图片
并且transformer-XL改变了self-attention的计算方式,首先我们来看看Vanilla Transformer的计算过程:
在这里插入图片描述
BERT系列模型总结_第5张图片
通过上述分析,transformer-xl从n-1层到第n层的完整计算为:
BERT系列模型总结_第6张图片

BERT

bert是在transformer encoder的基础之上进行改进的,因此在整个流程上与transformer encoder没有大的差别,只是在embedding,multi-head attention,loss上有所差别。关于bert的细节,网络上有很多相关资料,这里就列出一些关于bert的问题。

  1. bert和transformer在embedding上的差异?
  • transformer的输入是 token embedding 和 position embedding(绝对位置编码);bert的输入是 token embedding ,position embedding(学习得到)和segment embedding。
  • bert在token序列之前加了一个特定的token“[cls]”,这个token对应的向量后续会用在分类任务上;如果是句子对的任务,那么两个句子间使用特定的token“[seq]”来分割
  • transformer 在embedding之后跟了一个dropout,但是bert在embedding之后先跟了一个layer normalization,再跟了一个dropout。
  1. 为什么Bert的三个Embedding可以进行相加?
    训练的时候就是三个embedding相加进行训练的,所以评估阶段使用三个embedding相加会获得比较好的表示。从梯度的角度来看: ( f + h + g ) ′ = f ′ + h ′ + g ′ (f+h+g)'=f'+h'+g' (f+h+g)=f+h+g
  2. 为什么bert的mask选择的是15%,还可以有其他的比例么?
    15%的概率是通过实验得到的最好的概率,xlnet也是在这个概率附近,说明在这个概率下,既能有充分的mask样本可以学习,又不至于让segment的信息损失太多,以至于影响mask样本上下文信息的表达。然而因为在下游任务中不会出现token“”,所以预训练和fine-tune出现了不一致,为了减弱不一致性给模型带来的影响,被mask的token有80%的概率用“”表示,有10%的概率随机替换成某一个token,有10%的概率保留原来的token,这3个百分比也是多次实验得到的最佳组合,在这3个百分比的情况下,下游任务的fine-tune可以达到最佳的实验结果。
  3. bert和transformer在loss上的差异?
  • transformer的loss是在decoder阶段计算的。bert预训练的loss由2部分构成,分别是NSP和MLM任务,这两个任务的loss相加得到总loss。 bert fine-tune任务的loss会根据任务性质来设计。
  • bert在encoder之后,在计算NSP和MLM的loss之前,分别对NSP和MLM的输入加了一个Dense操作,这部分参数只对预训练有用,对fine-tune没用。而transformer在decoder之后就直接计算loss了,中间没有Dense操作。
  1. 为什么transformer和bert的训练都是采用的是warm up的学习率?
    因为一开始训练的时候loss很大,直接采用大学习率,是一个毁灭性的打击,因此先采用小学习率,使得loss逐步平稳再采用大学习率加快训练步伐,之后为了收敛逐步减小学习率。

XLNET

XLNet: Generalized Autoregressive Pretraining for Language
XLNet:Generalized Autoregressive Pretraining for Language Understanding
XLNet:运行机制及和Bert的异同比较

在介绍XLNET之前,需要介绍一下AR和AE。

  • AR(Autoregressive Model, 自回归模型):通过估计一串文本序列的生成概率分布进行建模。一般而言,AR模型通过要么从前到后计算文本序列概率,要么从后向前计算文本序列概率,但不论哪种方式的建模,都是单向的。即在预测一个单词的时候无法同时看到该单词位置两边的信息。
  • AE(Autoencoding Model,自编码模型):通过从破坏的输入文本序列中重建原始数据进行建模。例如BERT通过预测【mask】位置的词重建原始序列。它的优点在于在预测单词的时候能够同时捕获该单词位置前后双向的信息;它的缺点是预训练过程中采用了mask单词的策略,然而微调阶段并没有,因此导致了预训练阶段和微调阶段的的GAP,另外在训练过程中,对不同mask单词的预测是相互独立的。

BERT的成功有一部分取决于它充分利用了上下文的信息,但是BERT这种学习模式,不太适合自然语言生成的任务,因此其自然语言生成任务的效果较差。而且上文也说过,因为这种mask的方式会造成fine-tune阶段与训练阶段的脱节,所以bert就出了三种情况的这种操作。

XLNET的想法就是,在AR的想法中尽可能地利用双向的语言的信息。在fine-tune的阶段,不像Bert那种带Mask符号的Denoising-autoencoder的模式,而是采用自回归LM的模式。

就是说,看上去输入句子X仍然是自左向右的输入,看到Ti单词的上文Context_before,来预测Ti这个单词。但是又希望在Context_before里,不仅仅看到上文单词,也能看到Ti单词后面的下文Context_after里的下文单词,这样的话,Bert里面预训练阶段引入的Mask符号就不需要了,于是在预训练阶段,看上去是个标准的从左向右过程,Fine-tuning当然也是这个过程,于是两个环节就统一起来。

那么XLNET是怎么做的呢?它的思想是对token进行全排列,比如说有token [x1,x2,x3]某个排列后就有[x1,x3,x2],那么我们在读到x2的时候自然就有x1,x3的信息(上下文信息)。假设token的长度是n,那么进行全排列的话就有n!种可能,这样计算量也太大了,因此xlnet采用采样的方式逼近目标函数的期望

假设给定一串序列 x = [ x 1 , x 2 , x 3 , . . . , x n ] x=[x_1,x_2,x_3,...,x_n] x=[x1,x2,x3,...,xn],它将有n!个不同的排列组合 Z = [ z 1 , z 2 , . . . , z n ! ] Z=[z_1,z_2,...,z_{n!}] Z=[z1,z2,...,zn!],令 z ∈ Z z∈Z zZ表示某一排列方式, z t z_t zt表示为z这个排列的第t个位置, z < t zz<t表示t这个位置前边的t−1个位置编号, x z x_z xz表示将序列x按照索引z进行排序。 x z < t x_{zxz<t表示将原始x按照z进行排列后, x z x_z xz前t−1个位置的token。
BERT系列模型总结_第7张图片
我们解决了目标函数的问题,但是这并不代表问题解决,我们还有两个问题:

  • 在评估时,我们也不可能将顺序打乱,所以我们得在transformer的mask上做文章,简单的说,就是将排列后的位于 x z t x_{z_t} xzt之后的位置mask掉,确保 x z t x_{z_t} xzt得不到之后的信息即可。
  • 位置问题:因为位置被打乱了,在输出层没法获知我们到底是预测哪个真正位置的结果。而且如果对一个长序列而言,有些打乱的结果前半部分是相同的,那么这两个打乱结果在某些节点上根本就没有分别。

因此引入了“双流自注意力”(two stream self attention)机制来帮助建模:

  • content-stream提供了内容方面的表达content representation h θ ( x z ≤ t ) h_θ(x_{z≤t}) hθ(xzt),简记为 h z t h_{z_t} hzt,它等同经典的transformer的状态向量,这个向量中既包含了位置信息 z t z_t zt,又包含了内容信息 x z t x_{z_t} xzt
  • query-stream提供了查询方面的表达query representation g θ ( x z < t , z t ) g_θ(x_{zgθ(xz<t,zt),简记为 g z t g_{z_t} gzt,它仅仅包含了 x z < t x_{zxz<t的内容信息和 z t z_t zt的位置信息,并不包含 x z t x_{z_t} xzt的内容。

BERT系列模型总结_第8张图片
由此可见,计算方式是:
在这里插入图片描述
另外还有一些需要注意的点:

  • xlnet使用的是transformer-xl而非transformer,因此,其计算公式就变为:

BERT系列模型总结_第9张图片

  • Partial Prediction: 因为采用排列的方式训练,会导致xlnet收敛慢,因此,在xlnet的训练过程中,每次只预测序列最后面的token。这样在切分点之前的token就无须计算query representation,这样就大大节省内存,增加模型的训练速度。
  • Relative Segment Encoding: 采用的是相对编码,即给定序列中的两个位置i和j,判断这两个位置是否在同一个segment中,如果在则 s i j = s + s_{ij}=s_+ sij=s+,否则 s i j = s − s_{ij}=s_- sij=s。因此在预测第i个位置token时,需要计算i位置向量与位置j做attention获取分数:
    在这里插入图片描述

xlnet打败了bert,但是在xlnet发布之后不久,bert的改进版roberta使用了160G的数据进行预训练,又打败了xlnet。

RoBERTa

RoBERTa: A Robustly Optimized BERT Pretraining Approach
BERT系列模型总结_第10张图片
Dynamic Masking:
BERT中有个Masking Language Model(MLM) 预训练任务,在准备训练数据的时候,需要Mask掉一些token,训练过程中让模型去预测这些token,这里将数据Mask后,训练数据将不再变化,将使用这些数据一直训练直到结束,这种Mask方式被称为Static Masking。

如果在训练过程中,期望每轮的训练数据中,Mask的位置也相应地发生变化,这就是Dynamic Masking,RoBERTa使用的就是Dynamic Masking。

在RoBERTa中,它具体是这么实现的,将原始的训练数据复制多份,然后进行Masking。这样相同的数据被随机Masking的位置也就发生了变化,相当于实现了Dynamic Masking的目的。例如原始数据共计复制了10份数据,共计需要训练40轮,则每种mask的方式在训练中会被使用4次。

Full-Sentences without NSP:
通过实验发现,去掉NSP任务能够提升一些down-stream任务的指标。FULL-SENTENCES表示从一篇文章或者多篇文章中连续抽取句子,填充到模型输入序列中。也就是说,一个输入序列有可能是跨越多个文章边界的。具体来讲,它会从一篇文章中连续地抽取句子填充输入序列,但是如果到了文章结尾,那么将会从下一个文章中继续抽取句子填充到该序列中,不同文章中的内容还是按照SEP分隔符进行分割。

Larger Batch Size:
RoBERTa通过增加训练过程中Batch Size的大小,来观看模型的在预训练任务和down-stream任务上的表现。发现增加Batch Size有利于降低保留的训练数据的Perplexity,提高down-stream的指标。

Byte-Level BPE:
Byte-Pair Encodeing(BPE)是一种表示单词,生成词表的方式。BERT中的BPE算法是基于字符的BPE算法,由它构造的”单词”往往位于字符和单词之间,常见的形式就是单词中的片段作为一个独立的”单词”,特别是对于那些比较长的单词。比如单词woderful有可能会被拆分成两个子单词”wonder”和”ful”。

不同于BERT,RoBERTa使用了基于Byte的BPE,词表中共计包含50K左右的单词,这种方式的不需要担心未登录词的出现,因为它会从Byte的层面去分解单词。

More Data and More Training Steps

ALBert

ALBERT

我们知道bert模型很大,参数很多,占用的内存很大,在分布式计算中通信开销也大。因此Albert就是为了进行参数的削减但是又不对性能产生影响。

Albert有这些操作:

  • 第一个技术是对嵌入参数化进行因式分解。大的词汇嵌入矩阵分解为两个小的矩阵,将隐藏层的大小与嵌入层的分离开。这种分离使得隐藏层的增加更加容易,同时不显著增加词汇嵌入的参数量。(不再将 one-hot 向量直接映射到大小为 H 的隐藏空间,先映射到一个低维词嵌入空间 E,然后再映射到隐藏空间。通过这种分解,研究者可以将词嵌入参数从 O(V × H) 降低到 O(V × E + E × H),这在 H 远远大于 E 的时候,参数量减少得非常明显。)

之所以可以这样做是因为每次反向传播时都只会更新一个 Token 相关参数,其他参数都不会变。而且在第一次投影的过程中,词与词之间是不会进行交互的,只有在后面的 Attention 过程中才会做交互,我们称为 Sparsely updated。如果词不做交互的话,完全没有必要用一个很高维度的向量去表示,所以就引入一个小的隐藏层。

  • 第二种技术是跨层参数共享。这一技术可以避免参数量随着网络深度的增加而增加。

ALBERT 之所以这样做是因为,考虑到每层其实学习到内容非常相似,所以尝试了将其进行参数共享:BERT系列模型总结_第11张图片

  • 训练任务方面:提出了Sentence-order prediction (SOP)来取代NSP。具体来说,其正例与NSP相同,但负例是通过选择一篇文档中的两个连续的句子并将它们的顺序交换构造的。这样两个句子就会有相同的话题,模型学习到的就更多是句子间的连贯性。用于句子级别的预测(SOP)。SOP 主要聚焦于句间连贯,用于解决原版 BERT 中下一句预测(NSP)损失低效的问题。

作者推测,NSP效果不佳的原因是其难度较小。将主题预测和连贯性预测结合在了一起,但主题预测比连贯性预测简单得多,并且它与LM损失学到的内容是有重合的。SOP的正例选取方式与BERT一致(来自同一文档的两个连续段),而负例不同于BERT中的sample,同样是来自同一文档的两个连续段,但交换两段的顺序,从而避免了主题预测,只关注建模句子之间的连贯性。

关于位置编码的更多内容可以阅读让研究人员绞尽脑汁的Transformer位置编码

你可能感兴趣的:(NLP,自然语言处理,bert,transformer)