Bert论文笔记
首先放上论文的地址:
BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
背景介绍
NLP中有一个比较重要的任务就是如何将人类的句子和词翻译机器能够理解的表示,从而让机器进行处理。也就是说如何让机器理解人类语言
最简单最粗暴的表示方式是one-hot编码,即就像鸢尾花数据集中的one-hot表示,但是如果在词级别上用one-hot进行表示,那么表示词的向量将会非常大而且会过于稀疏,不利于机器处理,并且很难表示词的语义特征。
后来出现了Word Embedding ,这一方式解决了传统机器学习方法中的特征稀疏问题,它通过把一个词映射到一个低维稠密的语义空间,从而使得相似的词可以共享上下文的信息,从而提升泛化能力。而且通过无监督的训练(比如word2vec和glove方法),从而能够将这些语义知识迁移到数据较少的具体任务上。但是word embeding也有缺点,Word Embedding学到的是一个词的所有语义,比如bank可以是”银行”也可以是”水边。如果一定要用一个固定的向量来编码其语义,那么我们只能把这两个词的语义都编码进去,但是实际一个句子中只有一个语义是合理的,这显然是有问题的。也就是说word Embeding没有考虑到词在具体语境以及中的含义。
为了解决句子的在不同语境中的语义不同的问题,可以通过RNN来编码上下文的语义,但是最原始的RNN由于梯度消失和梯度爆炸等问题很难训练,后来引入了LSTM和GRU等模型来解决这个问题。最早的RNN只能用于分类、回归和序列标注等任务,通过引入两个RNN构成的Seq2Seq模型可以解决序列的变换问题。于是到了2017年,Google提出了Transformer模型,引入了Self-Attention。Self-Attention的初衷是为了用Attention替代LSTM,从而可以更好的并行(因为LSTM的时序依赖特效很难并行,而transformer一次可以处理多个词),从而可以处理更大规模的语料。
于是在transformer的基础上发展出了ELMo(Deep contextualized word representations)与OpenAI GPT(Improving Language Understanding by Generative Pre-Training)这两预训练模型。ELMo会根据不同的任务,把上面得到的双向的LSTM的不同层的隐状态组合起来。使得输出的词表示与上下文相关的。可以用于下游(downstream)的特定任务。和ELMo不同,GPT得到的语言模型的参数不是固定的,它会根据特定的任务进行调整(通常是微调),这样得到的句子表示能更好的适配特定任务。它的思想其实也很简单,使用Transformer来学习一个语言模型,对句子进行无监督的Embedding,然后根据具体任务对Transformer的参数进行微调。从这一点来看,GPT的思想很接近本文要介绍的bert了。
一切过往, 皆为序章:尽管以及有能够考虑到上下文语义信息的模型,但是ELMo和GPT最大的问题就是其语言模型是单向的——我们是根据之前的历史来预测当前词。但是我们不能利用后面的信息。bert之所以state-of-the-art的一点就是其虽然也是基于transformer,但是BERT能够同时利用前后两个方向的信息。主要是源于其预训练的方法的创新。
注意:即使ELMo训练了双向的两个RNN,但是一个RNN只能看一个方向,因此也是无法”同时”利用前后两个方向的信息的。
摘要
We introduce a new language representation model called BERT, which stands for Bidirectional Encoder Representations from Transformers.
简介:bert首先是一个基于transform的双向编码表示。
BERT is designed to pretrain deep bidirectional representations from unlabeled text by jointly conditioning on both left and right context in all layers.
As a result, the pre-trained BERT model can be finetuned with just one additional output layer to create state-of-the-art models for a wide range of tasks, such as question answering and language inference, without substantial taskspecific architecture modifications.
其次摘要讲了下bert的上游和下游任务中的处理方式:
bert的预训练的策略是输入未标注的文本,bert的所有层中都要同时考虑未标记的文本的上下文,从而得到文本的深度编码表示(即机器能够理解并处理的语义表示)。
因此,预训练好的bert模型可以通过finetuned ,只用在后面接上一个输出层(应用于下游任务),就可以达到意料之外的效果,因此可以应用到广泛的学习任务中
BERT is conceptually simple and empirically powerful. It obtains new state-of-the-art results on eleven natural language processing tasks, including pushing the GLUE score to 80.5% (7.7% point absolute improvement), MultiNLI accuracy to 86.7% (4.6% absolute improvement), SQuAD v1.1 question answering Test F1 to 93.2 (1.5 point absolute improvement) and SQuAD v2.0 Test F1 to 83.1 (5.1 point absolute improvement).
摘要最后讲了下bert的效果:bert在概念上简单,在经验上有力。在11个NLP任务中取得了非常非常不错的成果(效果见下)
以及具体这11个任务:
MNLI:Multi-Genre Natural Language Inference,给定句子对,判断两个句子是蕴含、矛盾还是中立,三分类
QQP:Quora Question Pairs,给定句子对,判断语义是否相似,二分类
QNLI:Question Natural Language Inference,给定句子对,判断后者是否是前者的回答,二分类
SST-2:Standford Sentiment Treebank,给定单句,判断情感,二分类
CoLA:Corpus of Linguistic Acceptability,给定单句,判断是否是一句话?二分类
STS-B:Semantic Textual Similarity,给定句子对,判断相似度程度,五分类
MRPC:Microsoft Research Paraphrase Corpus,给定句子对,判断语义是否一致,二分类
RTE:Recognizing Texual Entailment,给定句子对,判断蕴含性,二分类
SQuAD1.1:Stanford Question Answering Dataset,经典阅读理解任务,给定(question,paragraph)对,预测answer在paragraph中的起止位置(span prediction)
CoNLL-2003 NER:命名实体识别任务,输入单句,为每个词进行标注
SWAG:Situations With Adversarial Generations,原任务是给定一句话,和4个选项,从选项里面找出它的下一句,这里转化为输入pair的分类问题,四分类
bert的介绍
We introduce BERT and its detailed implementation in this section. There are two steps in our framework: pre-training and fine-tuning.
During pre-training, the model is trained on unlabeled data over different pre-training tasks. For finetuning, the BERT model is first initialized with the pre-trained parameters, and all of the parameters are fine-tuned using labeled data from the downstream tasks. Each downstream task has separate fine-tuned models, even though they are initialized with the same pre-trained parameters. The question-answering example in Figure 1 will serve as a running example for this section.
使用bert框架需要两步,即:上游的预训练以及下游的微调步骤
首先gooles对提供海量数据对bert进行了预训练(预训练时是基于未标注的数据),这样我们初始化bert模型的时候就不是随机初始化而是利用了gooles预先训练好的参数和权重,我们需要对bert模型进行微调,以应用到下游的NL任务中(此时我们微调时利用的数据是有标签的数据)
然后论文附了一张图来表示bert在预训练和微调时的区别:
预训练时:输入的经过mask处理过后的连续的两句子用一个特殊符号[sep]来进行隔离,[cls]是在没有输入文本示例前加入的特殊符号,某种意义上相当于一个考虑了全文信息的特征表达。bert经过预训练完后能够得到一组权重参数。
微调:微调时的网路结构除了输出层要适应下游的文本的处理任务,其他结构和预训练的时的架构完全一致,微调期间所有参数都要进行微调
A distinctive feature of BERT is its unified architecture across different tasks.
Bert在应用到不同任务中时需要进行微调,但是应用到具体任务中时bert的架构都不改变,预训练和微调的方式只有细微的不同。
当前背景介绍
We argue that current techniques restrict the power of the pre-trained representations, especially for the fine-tuning approaches.
The major limitation is that standard language models are unidirectional, and this limits the choice of architectures that can be used during pre-training. For example, in OpenAI GPT, the authors use a left-toright architecture, where every token can only attend to previous tokens in the self-attention layers of the Transformer (Vaswani et al., 2017).
作者总结了之前的一些模型的不足;
作者认为当前的处理技术限制了预训练的表示能力,特别是在预训练方式上;
以往的语义处理模型大多是单向的,只考虑上文或者只考虑下文。
比如在早期的GPT模型中,作者只考虑前文,这使得文章中每个taken都只参考Transformer的自注意层之前的token.也就是说以往的语义模型单向(即使是训练了双向RNN的ELMo,但是一个RNN只能关注文本的一个方向,无法像摘要中提到的那样,每一个层都利用文本前后的信息)
In this paper, we improve the fine-tuning based approaches by proposing BERT: Bidirectional Encoder Representations from Transformers.
BERT alleviates the previously mentioned unidirectionality constraint by using a “masked language model” (MLM) pre-training objective, inspired by the Cloze task (Taylor, 1953). The masked language model randomly masks some of the tokens from the input, and the objective is to predict the original vocabulary id of the masked arXiv:1810.04805v2 [cs.CL] 24 May 2019 word based only on its context. Unlike left-toright language model pre-training, the MLM objective enables the representation to fuse the left and the right context, which allows us to pretrain a deep bidirectional Transformer. In addition to the masked language model, we also use a “next sentence prediction” task that jointly pretrains text-pair representations. The contributions of our paper are as follows:
于是为了改进前人的语义模型,使得模型能够同时利用上下文的信息,Bert改进了预训练手段。比较创新的一点就是MLM的引入
bert的预训练
预训练的创新点在于如何让bert能够同时关注句子前后两个方向的信息
具体的实现方式可以如此描述:
bert 的模型根据完形填空这个任务的启发,通过使用一种叫MLM的的方式对文本进行预训练。
预训练任务1 Masked LM
(这里看论文没有理解,于是看了下源码的自述文件)
在原始的预处理代码中,我们随机选择要屏蔽的单词级别的token(也就是像完形填空一样随机地盖住一些词)。例如:
Input Text(原文本): the man jumped up , put his basket on phil ##am ##mon ' s head` `Original Masked Input(经过遮盖后的输出): [MASK] man [MASK] up , put his [MASK] on phil [MASK] ##mon ' s head
bert在预训练的时候会随机的Mask掉15%的词,然后让BERT来预测这些Mask的词,通过调整模型的参数使得模型预测正确的概率尽可能大,这等价于交叉熵的损失函数。这样的Transformer在编码一个词的时候会(必须)参考上下文的信息。
但是这有一个问题:在Mask 时会出现特殊的Token [MASK],但是在后面的fine-tuning时却不会出现,这会出现Mismatch的问题。
(原文本): the man jumped up , put his basket on phil ##am ##mon ‘ s head
(经过遮盖后的输出): [MASK] man [MASK] up , put his [MASK] on phil [MASK] ##mon ‘ s head
该如何理解这句话?还是以上述文本作为例子,(the)在这一轮预训练的时候以15%的概率被mask掉了,可能下一轮(the)这个词比较倒霉,还被mask掉,可能这个词一直运气都比较背,每轮都被mask,这样bert就只知道(the)位置上有个空缺,但是不知道这个词到底是什么,为了避免这种事情的发生,bert在mask词的时候采用了一种非常不错的机制:
如果某个Token在被选中的15%个Token里,则按照下面的方式随机的执行:
- 80%的概率替换成[MASK],比如my dog is hairy → my dog is [MASK](这样一个词真正被mask的可能降低到15%*80%,在这种概率下,实际情况中基本上不会出现某个词一直被mask无法被学习到的情况)
- 10%的概率替换成随机的一个词,比如my dog is hairy → my dog is apple(相当于加入噪音,提高泛化性)
- 10%的概率替换成它本身,比如my dog is hairy → my dog is hairy
这样做的好处是,BERT并不知道[MASK]替换的是哪一个词,而且任何一个词都有可能是被替换掉的,比如它看到的apple可能是被替换的词。这样强迫模型在编码当前时刻的时候不能太依赖于当前的词,而要考虑它的上下文,甚至更加上下文进行”纠错”。比如上面的例子模型在编码apple是根据上下文my dog is应该把apple(部分)编码成hairy的语义而不是apple的语义。
预训练任务2 Next Sentence Prediction (NSP)
许多重要的下游任务,如问答(QA)和自然语言推理(NLI)都是建立在理解两个句子之间关系的基础上的,而语言建模并不能直接捕捉到这一点。
也就是说为了适应某些下游任务的需要,因此BERT还增加了一个新的任务——预测两个句子是否有关联关系,这是一种Multi-Task Learing。
bert的预训练时选择一些句子对A与B,其中50%的数据B是A的下一条句子,剩余50%的数据B是语料库中随机选择的,学习其中的相关性,添加这样的预训练的目的是目前很多NLP的任务比如QA和NLI都需要理解两个句子之间的关系,从而能让预训练的模型更好的适应这样的任务。
50%: [CLS] the man went to [MASK] store [SEP] he bought a gallon [MASK] milk [SEP]
另外50%:[CLS] the man [MASK] to the store [SEP] penguin [MASK] are flight ##less birds [SEP]
预训练流程
The pre-training procedure largely follows the existing literature on language model pre-training. For the pre-training corpus we use the BooksCorpus (800M words) (Zhu et al., 2015) and English Wikipedia (2,500M words). For Wikipedia we extract only the text passages and ignore lists, tables, and headers. It is critical to use a document-level corpus rather than a shuffled sentence-level corpus such as the Billion Word Benchmark (Chelba et al., 2013) in order to extract long contiguous sequences.
使用BooksCorpus(800万字)(Zhu et al., 2015)和English Wikipedia(2500万字)。对于Wikipedia,我们只提取文本段落,而忽略列表、表格和标题。使用文档级语料库而不是打乱的句子级语料库(如Billion Word Benchmark (Chelba et al., 2013))来提取长连续序列是至关重要的。
用BooksCorpus(800M词)+ 英文Wikipedia(2500M词)作为语料,作者强调使用document-level的语料比随机shuffle句子的语料(比如Billion Word Benchmark)要好,因为这样才能抽取出连续的长句子,才好用于BERT的数据构造。
bert模型是非常大的,所以train来非常消耗资源。
都是训练了4天,BASE版本的BERT,用16块TPU,LARGE版本的BERT,用64块TPU,只能说谷歌爸爸真的非常有钱。
bert的微调
For each task, we simply plug in the task specific inputs and outputs into BERT and fine tune all the parameters end-to-end.
对于每个任务,我们只需将特定于任务的输入和输出插入BERT,并端到端调整所有参数。
普通人很难训练一个标准的bert模型,据说8个GPU一起训练需要训练一年左右。但是谷歌已经预训练好了bert模型并且提供了开源使用(代码地址),所以如果我们要使用bert模型,只需对其进行微调,应用到具体任务中即可。
论文中提及的应对四种不同任务的微调方式
论文中探讨了4用bert处理的任务,论述了bert的使用。
对于普通的分类任务,输入是一个序列,如图中右上所示,所有的Token都是属于同一个Segment(Id=0),我们用第一个特殊Token [CLS]的最后一层输出接上softmax进行分类,用分类的数据来进行Fine-Tuning(即需要用[SEP]分隔两个句子输入到模型中,然后同样仅须将[CLS]的输出送到分类器进行分类)。
对于相似度计算等输入为两个序列的任务(句子对关系任务)),过程如图左上所示。两个序列的Token对应不同的Segment(Id=0/1)。我们也是用第一个特殊Token [CLS]的最后一层输出接上softmax进行分类,然后用分类数据进行Fine-Tuning。
对于序列标注( SQuAD v2.0),比如命名实体识别,输入是一个句子(Token序列),除了[CLS]和[SEP]的每个时刻都会有输出的Tag,比如B-PER表示人名的开始,本章的序列标注部分已经介绍过怎么把NER变成序列标注的问题了,这里不再赘述。然后用输出的Tag来进行Fine-Tuning,过程如图右下所示。即对每个位置的输出进行分类即可,如果将每个位置的输出作为特征输入到CRF将取得更好的效果。;
第四类是问答类问题,比如SQuAD v1.1数据集,输入是一个问题和一段很长的包含答案的文字(Paragraph),输出在这段文字里找到问题的答案。即将问题与答案拼接输入到BERT模型中,然后将答案位置的输出向量进行二分类并在句子方向上进行softmax(只需预测开始和结束位置即可)
应用bert前应做的数据预处理
由于bert是基于transformer的编码器,所以数据预处理要符合tramformer的需求
这里引用了自然语言处理——BERT情感分类实战(一)之预处理
其中预处理有如下六步
step1分词:将文本的词按照单词级别进行划分,注意这里分词的规则:
step2对文本划分句子以及标注:为句首添加[CLS]标志,然后用[SEP]划分每个字句
step3用[PAD]填充句子:因为输入的每个句子要保证序列长度要一致,所以如果我定义喂入bert的序列长度为20,则如果现在句子长度(加上cls和sep后的长度)没有20,则要用[pad]进行填充,保证输入进入的序列长度是20,这从引用者的代码便可以看出
padded_tokens = tokens + ['[PAD]' for _ in range(20 - len(tokens))] print(padded_tokens) #效果如下
step4注意力mask编码:即有单词的为1,用[PAD]填充的为0,让bert知道句子的什么地方有语义什么地方没有
step5分段segment编码:用于区分不同的句子,我们这里只有一个句子,故全设为0
step6把token转化为id:文本分词后创建一个包含对应字 id的字典,这一步则是要查到每个单词对应的序号,转化为id
最后我们要把得到了mask编码、segment编码、taken_id一起喂入bert
模型架构
BERT’s model architecture is a multi-layer bidirectional Transformer encoder based on the original implementation described in Vaswani et al. (2017) and released in the tensor2tensor library.1 Because the use of Transformers has become common and our implementation is almost identical to the original,
we will omit an exhaustive background description of the model architecture and refer readers to Vaswani et al. (2017) as well as excellent guides such as “The Annotated Transformer.”
bert的模型架构是参考了Vaswani et al. (2017)的多层双向的 Transformer 编码器,因为bert对此实现基本上和原参考一致,而且transformer在NLP任务中使用比较广泛,所以这篇文章就不再细讲Vaswani et al. (2017)中编码器的实现(bert中的’T’ 的实现)
In this work, we denote the number of layers (i.e., Transformer blocks) as L, the hidden size as H, and the number of self-attention heads as A. 3 We primarily report results on two model sizes: BERTBASE (L=12, H=768, A=12, Total Parameters=110M) and BERTLARGE (L=24, H=1024, A=16, Total Parameters=340M).
在介绍bert模型的架构时作者做出如下定义:
- 作者直接定义一个Transformer blocks作为网络中的一层(L),(模型将将堆叠作者定义的L)。
- 隐藏层神经元规模用H表示(表示编码的维度)
- A表示self-attention heads的数量;
作者就L,H,A的不同进行了效果比对,发现参数越多规模越大效果越好
结果分析
在GLUE评测平台上的结果如下图所示,我们可以发现BERT比之前最好的OpenAI GPT还提高了很多
在SQuAD数据集上,BERT之前最好的结果F1=89.3%,而7个BERT的ensembling能达到93.2%的F1得分
bert模型的创新点在于Mask LM和预测句子关系的Multi-Task Learning。为了知道每个改动的贡献,文章做了如下的对照(Ablation)实验。
如下图所示,这里测试用的是小参数的一个BERT参考模型;
No NSP是没有预测句子关系(只有Mask LM)的BERT模型;
-LTR & No NSP基本等同于OpenAI GPT,它是基于Transoformer的从左到右的普通语言模型;
而最后一行+BiLSTM是指在Fine-Tuning OpenAI GPT的时候多加一个双向LSTM层(通常的Fine-Tuning都是只有一个线性层)。
总结
bert的优点就不用说了11个NLP任务中屠榜
但是作者在论文中也提到了bert的缺点:
- [MASK]标记在实际预测中不会出现,训练时用过多[MASK]影响模型表现
- 每个batch只有15%的token被预测,所以BERT收敛得比left-to-right模型要慢(它们会预测每个token)(虽然预训练慢但是应用起来准确率高,对有钱有设备的谷歌来说都是小问题)
- BERT的预训练任务MLM使得能够借助上下文对序列进行编码,但同时也使得其预训练过程与中的数据与微调的数据不匹配,难以适应生成式任务
- BERT没有考虑预测[MASK]之间的相关性,是对语言模型联合概率的有偏估计
- 由于最大输入长度的限制(见上数据预处理),适合句子和段落级别的任务,不适用于文档级别的任务(如长文本分类)
参考资料
汉语自然语言处理-从零解读碾压循环神经网络的transformer模型(一)-注意力机制-位置编码-attention is all you need
汉语自然语言处理-BERT的解读语言模型预训练-实践应用-transformer模型(二)-语料预处理-情感分析分类-数据增强-解决过拟合问题