BERT (Bidirectional Encoder Representation Transformers) 是在2018年由Google AI 团队发布的,虽然在此前刚发布不久的ELMo模型已经相比过去带来了很大的改善,但是BERT在各主流任务上取得的成绩仍然是一个质的飞跃。可以说,此后2-3年陆续出现了很多模型都是围绕着BERT展开进一步的改善和优化,NLP迎来了雨后春笋般的发展。
那么BERT究竟有什么样的特别之处,以至于有这么多后来者争相效仿和赶超呢?从模型设计的理念中我们可以了解到BERT模型有几个关键词:预训练、深度、双向。在当时已有的效果最好的模型中,一种是采用feature-based方法的ELMo模型,它是通过从左向右(LTR)和从右向左(RTL)两个模型的输出拼接获得词的表示;另一种是采用预训练加fine-tune的OpenAI GPT,它是通过从左向右的单向模型来训练。单向模型的主要缺点在于不能获得足够好的词表示,在句子级任务以及分词级任务的效果都是不够好的,同时模型训练的速度也会受到限制。
BERT论文主要贡献在于验证了双向预训练模型比将两个单向预训练模型拼接起来的效果更好,另外BERT也展示了一个通用的大规模预训练模型在很多句子级和分词级任务中能够取得非常好的成绩,从而避免采用许多复杂繁重的特定任务框架。目前BERT在11个NLP下游任务中均达到了最优的效果。
BERT最核心的部分是采用了Transformer架构,所以有的学者认为BERT不是一个新的模型,而是在Transformer基础上发展过来的一套流程。具体而言,BERT是一个多层Transformer的Encoder,输入的Embedding通过一层层的Encoder进行编码转换,再连接到不同的下游任务。从卷积神经网络的发展可以知道,残差的引入使得采用深层更窄的网络比浅层更宽的网络更有利于提升性能,对于图像任务,深层的网络意味着能学到不同层次的特征,从最基本的的线、面到复杂的图像。延伸到NLP领域,增加网络的层数可以理解为词、句子、文章、语义等不同级别进行特征提取。
BERT采用了双向并行输入的方式,即将句子整个输入到模型中,而不是将单词一个接着一个地输入,这样可以充分利用GPU的性能,大大提升模型的运行效率。与此同时由于并行输入会带来单词在文本中的位置信息的丢失,因此BERT模型额外需要增加了一个位置编码输入,确保位置信息不被丢失。
针对不同的下游任务,BERT在输入层引入了两个特殊符号,一个是[CLS],放在整个输入文本序列的最前面;另一个是[SEP],对于输入为句子对的任务,将[SEP]放在两个句子的中间以及第二个句子的结尾。[CLS]对应最终输出的hidden state用作分类任务的表示,如果是单句子分类,表示输入句子的类别;如果是句子对分类,表示两个句子是相关的/不相关的、相似意思/相反意思。
BERT的输入向量由三部分组成,分别是token embedding, segment embedding和position Embedding。在分词接入token embedding之前,先进行tokenization处理。这里采用WordPiece嵌入方式,将每个单词进一步拆分成更通用的sub-word,可以避免词汇表过大及减缓OOV的问题,token embedding层再将各分词转换成固定维度的向量。segment embedding是针对句子对任务设置的,只有0和1两个值,用于区分两个句子,句子A编码为0,句子B编码为1,如果是单句子任务,所有编码都为0。position embedding表示每个单词在句子中的位置,同一个单词在不同位置中它的embedding是不同的。3个embedding的size都是(batch_size, seq_length, hidden_size),最后将3个embeddings按元素值相加,即表示BERT编码层的输入。
BERT采用两个无监督任务进行参数预训练。第一个是MLM(Masked Language Model),简单来讲就是对句子随机挖去几个空,让模型去预测这几个空原先的单词。在BERT的预训练中采取的策略是随机抽取句子中15%的单词进行处理,其中这15%的单词又按照80%-10%-10%的比例采取不同的处理方式:80%的单词用[MASK]来替换,表示需要预测的部分,10%用随机的词来替换,10%维持原单词不变。这种训练流程可以让模型学习到单词在上下文中的分布表示,此外用1.5%的随机替换(15%中的10%)也不会损害模型的理解能力。
另外一个预训练任务是NSP(Next Sentence Prediction)。对于句子对A和B,选取B的时候50%的概率是真实的A的下一句(标签IsNext),50%的概率是从语料中随机选取的句子(标签NotNext)。通过预训练NSP任务,让模型理解到两个句子之间的关系,从而能够应用于QA和NLI等下游任务中。
两个任务进行联合训练( In addition to the masked language model, we also use a “next sentence prediction” task that jointly pretrains text-pair representations. )
BERT模型通过对Masked LM任务和Next Sentence Prediction任务进行联合训练,使模型输出的每个字/词的向量表示都能尽可能全面、准确地刻画输入文本(单句或语句对)的整体信息,为后续的微调任务提供更好的模型参数初始值。
由于Transformer采用的是自注意力机制,使得BERT模型可以应用于各种各样的下游任务。对于句子对任务,常规的做法是在计算交叉注意力之前,先单独对句子对进行编码。而BERT是将这两步合二为一,即使用自注意力机制来对句子进行编码。
对于输入是两个句子(A和B)的任务,A和B可以是意思相同的两种表述,可以是蕴涵的假设-前提对,可以是问答系统中的问题-回答对,也可以是文本分类或序列标注的文本对。而在输出侧,token表示用于分词级的分类任务,例如序列标注或问答系统;而[CLS]用于分类任务,如蕴涵分析或情感分析等。
以下分项列出BERT预训练过程使用的所有数据和调试参数:
在fine-tune阶段,大部分模型参数与预训练阶段是一样的,只有batch_size, lr, epochs需要调整,推荐参数如下:
特定任务的模型是通过在BERT基础上增加一个额外的输出层,BERT典型的四个下游任务分别是:
业务应用:基于业务上的文本数据对bert进行finetune,可以得到含有每个token embedding表示的bert网络,然后将bert网络推导serving端,可以对线上的实时文本推理得到每个实时文本的语义embedding,然后文本的语义embedding进行相似物料召回。
与BERT最有可比性的模型是GPT,BERT的很多设计理念是从GPT参考过来的,目的是就是为了更好比较两个模型。二者主要区别在于:
[1].BERT 论文详细阅读笔记
[2].Bert预训练
[3].BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
[4].关于BERT预训练模型,你想知道的都在这~
[5].图解BERT模型