一些资源:
官方论文:Pre-training of Deep Bidirectional Transformers for Language Understanding
[1810.04805] BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
官方代码和预训练模型:research/bert: TensorFlow code and pre-trained models for BERT
简介:
BERT 全称为 Bidirectional Encoder Representation from Transformer,是 Google 以无监督的方式利用大量无标注文本训练的语言模型。
BERT=Encoder of Transformer:
如果已经 完全了解了 Transformer 的结构,那么bert 理解就很简单了,transformer 是一个 Encoder-Decoder 结构的特征提取器,bert 实际上就是 使用了 Transformer 的 Encoder 部分,在两个任务:1)Mask Language Model 2)Next Sentence Prediction(NSP) 上进行训练,保留模型的参数,于是得到的预训练语言模型。甚至更通俗理解,其就是 word2vec的替代品,只是用更加复杂的网络和更多一点的任务训练得到的词编码器。
基本思想:
1. Bert之前的几年,人们通过DNN对语言模型进行“预训练”,得到词向量,然后在一些下游NLP任务(问题回答,自然语言推断,情感分析等)上进行了微调,取得了很好的效果。
2. 对于下游任务,通常并不是直接使用预训练的语言模型,而是使用语言模型的副产物--词向量。实际上,预训练语言模型通常是希望得到“每个单词的最佳上下文表示”。如果每个单词只能看到自己“左侧的上下文”,显然会缺少许多语境信息。因此需要训练从右到左的模型。这样,每个单词都有两个表示形式:从左到右和从右到左,然后就可以将它们串联在一起以完成下游任务了。
目标函数:
bert 是双向的 transformer 的 encoder ,ELMO也是一个双向的 LSTM 编码器,ELMO 的训练目标是: 和 单独训练两个表示然后 进行拼接;而bert 是以 为目标函数训练语言模型。
模型介绍:
Bert 和GPT一样均是采用的transformer的结构,相对于GPT来说,其是双向结构的,而GPT是单向的,如下图所示:
先看下bert的内部结构,官网最开始提供了两个版本,L表示的是transformer的层数,H表示输出的维度,A表示mutil-head attention的个数:
预训练模型:
首先了解一下什么是预训练模型,举个例子,假设我们有大量的维基百科数据,那么我们可以用这部分巨大的数据来训练一个泛化能力很强的模型,当我们需要在特定场景使用时,例如做文本相似度计算,那么,只需要简单的修改一些输出层,再用我们自己的数据进行一个增量训练,对权重进行一个轻微的调整。
预训练的好处在于在特定场景使用时不需要用大量的语料来进行训练,节约时间效率高效,bert就是这样的一个泛化能力较强的预训练模型。
Bert 的预训练过程:
BERT的预训练阶段包括两个任务,一个是Masked Language Model,还有一个是Next Sentence Prediction。两个任务的图解:
任务一:Masked Language Model:
MLM可以理解为完形填空,作者会随机mask每一个句子中15%的词,用其上下文来做预测,例如:my dog is hairy → my dog is [MASK]
如果机器能够根据上下文正确预测出被 mask 掉的词是什么,那有理由认为机器已经完全理解了这句话的意思。具体的:
随机把一句话中 15% 的 token(字或词)替换成以下内容:
1)这些 token 有 80% 的几率被替换成 [MASK],例如 my dog is hairy→my dog is [MASK]
2)有 10% 的几率被替换成任意一个其它的 token,例如 my dog is hairy→my dog is apple
3)有 10% 的几率原封不动,例如 my dog is hairy→my dog is hairy
之后让模型预测和还原被遮盖掉或替换掉的部分,计算损失的时候,只计算在被随机遮盖或替换的部分,其余部分不做损失,其余部分无论输出什么东西,都无所谓。
那么为啥要以一定的概率使用随机词呢?这是因为transformer要保持对每个输入token分布式的表征,否则Transformer很可能会记住这个[MASK]就是"hairy"。至于使用随机词带来的负面影响,文章中解释说,所有其他的token(即非"hairy"的token)共享15%*10% = 1.5%的概率,其影响是可以忽略不计的。Transformer全局的可视,又增加了信息的获取,但是不让模型获取全量信息。
这样的好处是:Bert 并不知道 [MASK] 替换的是哪一个词,而且任何一个词都有可能是被替换掉的,比如它看到的 apple 可能是被替换的词。这样强迫模型在编码当前时刻词的时候不能太依赖当前的词,而要考虑它的上下文,甚至根据上下文进行 "纠错"。
具体实现分为三步:
1. 在encoder的输出上添加一个分类层。
2. 用嵌入矩阵乘以输出向量,将其转换为词汇的维度。
3. 用softmax计算词汇表中每个单词的概率。
任务二:Next Sentence Prediction:
选择一些句子对A与B,其中50%的数据B是A的下一条句子,剩余50%的数据B是语料库中随机选择的,学习其中的相关性,添加这样的预训练的目的是目前很多NLP的任务比如问答和文本蕴含都需要理解两个句子之间的关系,从而能让预训练的模型更好的适应这样的任务。
首先拿到属于上下文的一对句子,也就是两个句子,之后我们要在这两个句子中加一些特殊的 token:[CLS]上一句话[SEP]下一句话[SEP] 。也就是在句子开头加一个 [CLS],在两句话之间和句末加 [SEP],具体地如下图所示:
上图中 Token Embeddings 是一般的词向量,pytorch 中的 nn.Embedding()
Segment Embedding 是用于区分上下句的词向量,A句全为1,B句全为0
Position Embedding 和 Transformer 中的不一样,不是三角函数,而是学习出来的,可以参与训练;
实现步骤:
1. 整个输入序列输入给 Transformer 模型用一个简单的分类层将[CLS]标记的输出输入到分类器进行二分类.
2. 用 softmax 计算 IsNextSequence 的概率.
Bert 的预训练过程就是把两个任务合并起来同时训练,然后将所有的 loss 相加,目标就是要最小化两种策略的组合损失函数。
bert 的使用:
bert 预训练模型可以直接使用,使用的方式大致分为 Feature-based 和 fine-tune ,关于这个的理解可以看这篇博客:feature-based 和 fine-tune - 简书
下面是一些使用实践:
1)bert embedding:
官方提供了一个可以直接使用bert预训练好的字向量的包:
!pip install transformers
!pip install sentencepiece
参考:
一文读懂BERT(原理篇)_程序猿废柴的博客-CSDN博客_bert