预训练模型(三)-----Bert


1.什么是Bert?

Bert用我自己的话就是:使用了transformer中encoder的两阶段两任务两版本的语言模型

没错,就是有好多2,每个2有什么意思呢?

先大体说一下,两阶段是指预训练和微调阶段,两任务是指Mask Language和NSP任务,两个版本是指Google发布的Base版本和Large版本。

Base版本:L(Layers)=12,H(Hidden)=768,A(attention head)=12,Total Parameters=110M
Large版本:L(Layers)=24,H(Hidden)=1024,A(attention head)=16,Total Parameters=340M

 

2.细看Bert

第一步,先来计算Bert的输入部分参数:

通过上图我们可以看到BERT的输入部分由3部分组成:

Token Embedding:单词的Embedding
Segment Embedding:句子的Embedding,BERT的训练数据都是由两个句子构成的,那么每个句子有个句子整体的embedding项对应给每个单词
Position Embedding:位置Embedding,NLP中单词顺序是很重要的特征,需要在这里对位置信息进行编码,用来表示每个单词在输入序列中的位置信息

 

仔细看上图,input和Token Embedding中出现了##ing的符号,那么问题来了,##这个表示的是什么意思呢?

其实在Bert中,英文单词是以Wordpiece Embeding形式进行表示,而中文则是单字切分,##表示的词就是那些Wordpiece。可能有人又会问,Wordpiece是怎得来的呢?

首先在英文中,把每个单词分割成单个字母,然后统计相邻字母组合的次数,提取次数较多的部分就为一个Wordpiece,类似于我们英语语法中前缀和后缀,例如-ing在语法中表示动词的进行时态,而在我们的词表中则表示##ing。

使用wordpiece embeding表示有什么好处呢?

1.减少了输入空间的表示

2.减少了oov问题

 

解释完输入问题后,我们来细看一下整个模型参数流动变化

(一)

BERT的词表大小vocab_size=32000,那么每个词可以表示成32000维的one-hot向量

BERT的max_length=512,也就是可以接收的最大输入句子的长度,那么每个单词的位置信息可以表示成512维的one-hot向量

BERT的训练数据接收的是句子对,那么就可以用2维的one-hot向量表示两个不同的句子

 

 

上图是神经网络语言模型NNLM的结构,BERT的Hidden=768,也就是上图中的N=768,上面说的单词,位置,和句子的one-hot向量首先均需要通过神经网络语言模型输入到隐藏层的映射,在这一步,就以一个单词为例,可以发现这里的参数计算其实与输入序列的长度是没有关系地。

单词one-hot向量(1,32000),需要的参数矩阵为(32000,768),

位置one-hot向量(1,512),需要的参数矩阵为(512,768),

句子one-hot向量(1,2),需要的参数矩阵为(2,768),

经过这样的转换就能得到Token Embedding,Segment Embedding,Position Embedding了,然后再将这3个Embedding相加就是BERT的输入了。

因此输入部分涉及的参数为:32000768+512768+2768

然后合并后的输入Embedding便会进入Transformer的Encoder中,Encoder又分为Multi-Heads Attention和Feed Forward。

(二)

 

 在Multi-Heads Attention输入Embedding需要经过三个W矩阵转换生成Q,K,V,怎么确定三个W矩阵的维度呢?

 因为BERT输入的句子最大长度为512,句子长度小于512用0补齐,大于512截断。故输入的序列Embedding矩阵的维度为(512,768),dmodel=768 也就是一个单词的Embedding维度

 W的维度为(dmodel,dmodel/heads)=(768,768/12)=(768,64),Base BERT是12个Heads

 

然后通过self-attention计算Z,这样我们就得到了12个Z,然后将12个Z拼接成一个大的矩阵12个(512,64)拼接成(512,768)的大

接着需要对这个(512,768)的大矩阵用Wo进行线性变换,维度不变依旧是(512,768),因为参数矩阵Wo的维度也是(768,768)

 

 那么在MultiHead Atten中参数为:3*768*64*12+768*768

(三)Add&Norm中是没有参数的,只是添加一个残差块和归一化

(四)Feed Forward

 

 

在这一步需要将输入映射到高维空间然后再变回原本的向量维度,W1就是负责映射到高维空间,W2负责恢复成原来的维度。Feed Forward的输入为(512,768)的矩阵,BERT的映射到高维度的变换使用的是4H,也就是4倍输入维度,因此W1的维度就是(768,4x768),W2的维度就是(4x768,768),故Feed Forward部分参数量为:7684768+4768768

然后又进行Add&Norm,接着进入下一个Encoder,堆叠12层。

因此将这三步所有参数汇总为:
(32000+512+2)768+12(37686412+768768+76847682)=109905408=110M

 

3.双向

Masked双向语言模型向上图展示这么做:随机选择语料中15%的单词,把它抠掉,也就是用[Mask]掩码代替原始单词,然后要求模型去正确预测被抠掉的单词。但是这里有个问题:训练过程大量看到[mask]标记,这会引导模型认为输出是针对[mask]这个标记的,但是真正后面在进行预测任务的时候是不会有这个标记的,这自然会有类似语义鸿沟的问题。为了避免这个问题,Bert改造了一下,15%的被选中要执行[mask]替换的单词中,只有80%真正被替换成[mask]标记,10%被随机替换成另外一个单词,10%情况这个单词不做改动。这就是Masked双向语言模型的具体做法。

随机选择语料中的15%的单词,意思是说从输入的token中随机选择15%的token,而不是从总的BERT词表中随机选择15%。然后每次对于这部分被选出来的15%的token进行预测,因为每次只能预测输入token的15%,而且是随机地,要想将整个句子都预测出来的话需要多迭代几次,这也是为什么BERT预训练需要消耗大量时间的原因。

为什么要80%,10%,10%?

 

BERT是一个多任务学习模型,上面MLM只是BERT预训练中的一个任务,另一个是NSP(Next Sentence Prediction)。之前也说过BERT的训练数据是句子对的形式。

NSP,指的是做语言模型预训练的时候,分两种情况选择两个句子,一种是选择语料中真正顺序相连的两个句子;

另外一种是第二个句子从语料库中抛色子,随机选择一个拼到第一个句子后面。

我们要求模型除了做上述的Masked语言模型任务外,附带再做个句子关系预测,判断第二个句子是不是真的是第一个句子的后续句子。

之所以这么做,是考虑到很多NLP任务是句子关系判断任务,单词预测粒度的训练到不了句子关系这个层级,增加这个任务有助于下游句子关系判断任务。

对NSP具体实现步骤做一个说明:

 

 

1.首先在最开始的输入阶段,我们从语料中提取出两个句子A和B,50%的概率B是A在语料中的下一句,50%的概率B是随机取地句子
2.将这两个句子A和B打包成一个序列,[CLS] A [SEP] B [SEP]
3.然后就是生成句子标识,将序列中[CLS] A [SEP]的区域中每个单词token都对应一个相同的句子Embedding EA,EA怎么算,在上面将计算参数的时候应该已经说过了,B [SEP]对应一个相同的句子Embedding EB
4.最后将[CLS]的输出当做是包含序列A 和 序列B句子语义关系信息的输出输入到分类器中
这样就完成了句子对之间关系分析的任务

那么CLS的位置是否可以随意放置?

首先,CLS在预训练中没有意义,在微调中有意义,用了提取label。其次,CLS可以放在句子各个位置,因为有self-attention机制。

 

4.微调

关于是否微调有两种使用方式:

(一)不微调,只利用提取出的特征:BERT 预训练的输出其实是输入中每个Token经过训练后的Embedding值,就是每个Token的词向量,然后当我们真正在使用的时候就可以根据自己的实际业务将这些Token词向量提取出来,接下来就像使用Word2vec的词向量一样进行后续任务

(二)微调:Fine-Tune的结构也应该按照BERT的预训练结构来设计,然后就可以运用到各种NLP下游task中了

句子关系类任务:Entailment,QA,语义改写,自然语言推理等任务都是这个模式,它的特点是给定两个句子,模型判断出两个句子是否具备某种语义关系
使用BERT预训练的模型参数处理这类问题,只需要给输入加上一个起始[CLS]和终结符号[SEP],句子之间加个分隔符[SEP]即可。对于输出来说,把第一个起始符号[CLS]对应的Transformer最后一层位置上面串接一个softmax分类层即可

分类任务:文本分类,情感分析等任务,特点是不管文章有多长,总体给出一个分类类别即可
对于分类问题,咱们一般都把一句话或者一篇文档当做一个整体,那么就是输入单句,只需要给输入文本增加起始[CLS]和终结符号[SEP],输出部分与句子关系类任务一样,是要把把第一个起始符号[CLS]对应的Transformer最后一层位置上面串接一个softmax分类层即可

序列标注:这是最典型的NLP任务,比如中文分词,词性标注,命名实体识别,语义角色标注等都可以归入这一类问题,它的特点是句子中每个单词要求模型根据上下文都要给出一个分类类别。
对于序列标注问题,输入部分和单句分类是一样的,只需要输出部分Transformer最后一层的每个单词对应位置都进行softmax分类

生成式任务:比如机器翻译,文本摘要,写诗造句,看图说话等都属于这一类。它的特点是输入文本内容后,需要自主生成另外一段文字。
关于生成式任务,原生BERT无法完成,因为BERT是一个双向的模型,而对于像生成任务式这种,是属于单向模型的。

 

5.BERT优缺点

优点:

1.效果好,相比于GPT,GPT是单向,而BERT是双向,相比与ELMO,ELMO采用的是LSTM,而BERT采用的是效果更好的特征提取器Transformer
2.几乎支持所有的NLP下游任务

BERT缺点:
1.虽然说Transformer中引入了位置编码来解决位置信息是一个还不错的解决方案,但还是存在些许不足之处,可能需要更加强大的超越Transformer的特征提取器

2.BERT参数量巨大,计算资源消耗大

3.一些可有可无的小问题,[mask]的时候每次是随机选取15%,比如”New York is a city”,假设我们Mask住”New”和”York”两个词,那么给定”is a city”的条件下”New”和”York”并不独立,因为”New York”是一个实体,看到”New”则后面出现”York”的概率要比看到”Old”后面出现”York”概率要大得多。不过BERT预训练数据量特别大,影响就基本上可以忽略了

4.Mask带来的语义鸿沟问题,造成输入与输出分布不一致


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM