首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

五问BERT:深入理解NLP领域爆红的预训练模型

BERT是NLP领域的一个重要里程碑,它是一个非常强大的语言模型,极大地提高了解决NLP任务的能力。本文由浅入深,通过理论与案例多角度介绍了BERT的理论与使用方法。如果你是一名NLP实践者,可以根据本文的案例指导,学习BERT的使用。

假如你一直关注深度学习相关信息的话,你一定听说过BERT,在过去的一年里,它一直是非常热门的焦点话题。

在2018年底,谷歌人工智能语言的研究人员开源了一种新的自然语言处理技术(NLP),被称为BERT(Transformers的双向编码器表示)。这是一项重大突破,它以其令人难以置信的性能轰动了整个深度学习社区。在这篇博文中,我们将通过回答以下5个问题来了解和学习BERT:

  1. 为什么需要BERT?
  2. BERT背后的核心思想是什么?
  3. BERT的工作原理是什么?
  4. 什么场景下使用BERT,如何进行fine-tune?
  5. 如何使用BERT?BERT文本分类实践指南

本文将首先讲解BERT的理论,接着用实践案例来加深对BERT的理解。

1. 为什么需要BERT?

NLP面临的最大挑战之一是缺乏足够的训练数据。总的来说,有大量的文本数据可用,但是如果我们想要创建特定于某任务的数据集,我们需要将这些数据分成许多不同的字段。即便我们这样做了之后,我们最终只能得到几百或几十万个人类标记的训练样本。可是,为了达到更好的效果,基于深度学习的NLP模型需要更大的数据量,只有对数亿甚至十亿级的带注释的数据进行训练,才能达到很好的效果。

为了帮助弥补这一数据量上的差距,研究人员开发了各种技术来训练通用语言模型,这些模型使用网络上的大量未加注释的文本作为数据源(这称为预训练)。这些通用的预训练模型可以在更小的特定任务数据集上进行微调,例如,当处理诸如问题回答和情绪分析等问题时,与从零开始在更小的任务特定数据集上训练相比,这种方法可以极大地提高准确性。BERT是NLP预训练的新技术之一,它在深度学习社区引起了轰动,因为它在各种NLP任务中呈现出的结果是最准确的,比如问答场景。

BERT另一个优势在于它可以免费下载和使用,我们可以使用BERT模型从文本中提取高质量的语言特性数据,或者我们可以调整这些模型用于某个特定的任务场景中,如情绪分析或问题回答,然后用我们自己的数据生产最优的预测结果。

2. BERT背后的核心思想是什么?

语言模型的真正意义是什么?语言模型试图解决哪些问题?从本质上讲,他们的任务是根据上下文“填空”。例如下面这个例子:

“那位女士去商店买了一双 _____ 鞋子。”

语言模型在完成这个句子时会设置20%的权重使用单词“cart”,80%的权重可能使用单词“pair”。

在BERT出现之前,语言模型需要通过对文本序列从左至右或者再结合从右向左的扫描方式理解文本。这种单向的方法比较适合生成句子——它可以预测下一个单词,将它添加到序列中,然后接着预测下一个单词,直到形成一个完整的句子。

有了BERT之后,便可以创建一个双向训练的语言模型(这也是它的关键技术创新)。与单向语言模型相比,这意味着我们现在可以对语境和语言流有更深刻的理解了。

BERT并不是按顺序预测下一个单词的,而是使用了一种称为Masked LM (MLM)的新技术:它随机mask句子中的单词,然后尝试预测它们。mask意味着模型将从两个方向观察,它使用句子的全部上下文,包括左边和右边的环境,来预测被mask的词。与之前的语言模型不同,它会同时考虑前一个和下一个标记。现有的基于LSTM的从左到右和从右到左的组合模型缺少这个“相同时间的部分”。(更准确地说,BERT是没有方向性的。)

但为什么这种无方向性的方法如此强大呢?

预训练的语言模型可以是上下文无关的,也可以是基于上下文的。基于上下文的表示可以是单向的或双向的。像word2vec这样的上下文无关模型为词汇表中的每个单词生成单个单词embedding 表示(数字向量)。例如,单词“bank”在“bank account”和“bank of the river”中有相同的上下文无关表示。但在句子中,基于上下文的模型就会生成基于句子中其他单词的表示形式。例如,在“I accessed the bank account,”这句话中,单向上下文模型将基于 “I accessed the“来表示“bank”,这时就不会考虑到”account” 了。然而,BERT用它的前一个和下一个上下文来表示“bank”——“ I accessed the … account”——从深度神经网络的最底部开始,使其成为深度双向的。

将BERT的神经网络结构与之前最先进的上下文预训练方法进行比较。箭头表示从一个层到下一个层的信息流。顶部的绿色方框表示每个输入单词的最终上下文表示。

BERT基于Transformer模型架构,而不是LSTMs。本文之后会介绍BERT的模型细节,但总的来说:

Transformer工作时力求执行一个少的、恒定数量的步骤。在每个步骤中,它应用一个标注机制来理解句子中所有单词之间的关系,而不管它们的位置。例如,对于句子“ I arrived at the bank after crossing the river”,需要确定“bank”这个词是指一条河的岸边,而不是一个金融机构,Transformer可以很快根据“river”这个词进行标注,只用一步就实现了目的。

以上我们已经介绍了BERT的关键理念,下面我们深入了解一下细节。

3.BERT的工作原理是什么?

BERT依附于“Transformer”(一种标注机制,用来学习文本中单词之间的上下文关系)。一个基本的Transformer包括一个编码器,用来读取文本输入,一个解码器,用来产生关于任务的预测。由于BERT的目标是生成语言表示模型,所以它只需要编码器部分。BERT的编码器的输入是一个tokens序列,它首先被转换成向量,然后在神经网络中进行处理。但是在开始处理之前,BERT需要对输入进行处理并添加一些额外的元数据:

  1. Token embeddings:在第一个句子的开头将[CLS]token添加到输入单词token中,并在每个句子的末尾插入[SEP]token。
  2. Segment embeddings:将表示句子A或句子B的标记添加到每个token中。这可以在不同的句子间区分编码器。
  3. Positional embeddings:将positional embedding添加到每个token中,以标示其在句子中的位置。

BERT的输入示意图:input embeddings是Token embeddings、Segment embeddings和Positional embeddings的总和。

实际上,Transformer堆叠了一个将序列映射到序列的层,因此输出也是一个向量序列,在相同索引处的输入和输出标记之间具有1:1的对应关系。正如我们之前说过的,BERT不会试图预测句子中的下一个单词。它的训练主要采用以下两种策略:

1. Masked LM (MLM)

其指导思想是“简单”:使用( MASK) token随机mask 15%的单词输入,之后运行基于编码器的BERT标注,然后基于所提供的上下文中的其他non-masked词序列预测被mask的单词含义。然而,这种原始的mask方法有一个问题——模型只在[ MASK]token出现在输入中时才尝试预测,而我们希望模型不管输入中出现了什么tokens都能够尝试预测正确的tokens 。为了解决这个问题,我们选择mask15%的tokens:

  • 实际上80%的tokens被替换为token [MASK].。
  • 10%的token被替换为随机token。
  • 10%的token保持不变。

训练BERT损失函数时,只考虑mask token的预测,而忽略非mask token的预测。这会导致模型的收敛速度比从左到右或从右到左的模型慢得多。

2. 下一句话预测(NSP)

为了理解两个句子之间的关系,BERT训练过程中还使用了下一句预测。具有这种理解能力的预训练模型可以处理问答相关的任务。在训练过程中,该模型得到输入的句子对,并学习预测第二个句子是否是原文本中的下一个句子。

正如我们前面看到的,BERT用一个特殊的(SEP)token来分隔句子。在训练过程中,模型一次输入两个句子:

  • 有50%的可能性,第二句话在第一句之后。
  • 有50%的可能性,它是一个来自完整语料库的随机句子。

之后BERT就要预测第二个句子是否是随机的,并假设这个随机的句子与第一个句子是断开的:

预测第二句与第一句是否是连接的,基本上完整的输入序列经过 Transformer模型,再用一个简单的分类器层将(CLS)token的输出转换为2×1的向量,并使用softmax分配IsNext-Label。

该模型结合了 Masked LM神经网络和下一句预测进行训练。这是为了最小化两种策略的组合损失函数——所谓的“合作共赢”。

模型架构

根据模型架构的规模,BERT有四种预训练的版本:

BERT-Base: 12-layer, 768-hidden-nodes, 12-attention-heads, 110M parameters

BERT-Large: 24-layer, 1024-hidden-nodes, 16-attention-heads, 340M parameters

BERT-Base在4个TPUs上训练了4天,BERT-Large在16个TPUs上训练了4天!

有关超参数的详细信息以及关于体系架构和结果分解的更多信息,建议你阅读BERT论文原文。

4. 什么场景下使用BERT,如何进行fine-tune?

BERT在自然语言推理、情感分析、问题问答、意译检测和语言可接受性等一般语言理解的各种任务场景中都表现出色。

那么,针对特定的任务场景,我们如何进行fine-tune呢?BERT可以用于各种各样的语言任务中。如果我们想要基于我们自己的数据集对原始模型进行微调,只需在核心模型上添加一个独立层即可。

例如,假设我们正在创建一个问答应用程序。本质上,问题问答只是一个预测任务——在接收一个问题作为输入时,应用程序的目标是从一些语料库中识别正确的答案。因此,给定一个问题和一个上下文段落,该模型预测该段落中最有可能回答该问题的开始和结束标记。这意味着我们可以使用BERT模型通过学习两个额外的向量来训练我们的应用程序,这两个向量分别表示答案的开头和结尾。

就像句子对任务一样,问题变成了输入序列中的第一个句子,段落变成了第二个句子。不过,在这里fine-tune过程中加入了两个新参数:开始向量和结束向量。

在fine-tune训练中,超参数与BERT训练保持一致;本文对需要调整的超参数给出了具体的指导。

注意,如果我们想进行fine-tune,我们需要转换数据输入的格式,以满足预训练核心BERT模型的特殊格式要求。例如:我们需要添加特殊token来标记开始((CLS))、分离/结束的句子([ SEP])以及segment IDs,以达到区分不同句子的目的,最终才能将数据转换成BERT使用的特性。

5. 如何使用BERT?BERT文本分类实践指南

从上文中我们已经了解了BERT的基本概念,下面我们就要来看一个实际的例子了。在本指南中,我将使用Yelp用户评论数据集,你可以从这里下载该数据集。这是一个简单的二进制文本分类任务——目标是将短文本分为好的和差的评论。下面将介绍完整的工作流程:

1. 安装

在python tensorflow环境中进行设置会比较简单:

a. 将BERT Github库克隆到你自己的电脑上。在你的终端上,输入

代码语言:javascript
复制
git clone https://github.com/google-research/bert.git

b. 从官方的BERT Github页面下载预先训练好的BERT模型文件。里面包括权值、超参数和其他必要的文件,这些文件中包含了BERT在预训练中学到的信息。将其保存到你git clone存储库并解压到目录中。下面是英文版的链接:

BERT-Base, Uncased: 12-layers, 768-hidden, 12-attention-heads, 110M parameters

BERT-Large, Uncased: 24-layers, 1024-hidden, 16-attention-heads, 340M parameters

BERT-Base, Cased: 12-layers, 768-hidden, 12-attention-heads , 110M parameters

BERT-Large, Cased: 24-layers, 1024-hidden, 16-attention-heads, 340M parameters

我们需要根据自身情况选择BERT预训练的版本。例如,如果我们用不了谷歌TPU,我们最好选择使用基本模型。至于“ cased”和“ uncased”的选择取决于字母大小写是或否会对我们的任务产生影响。本教程下载使用的是BERT-Base-Cased模型。

2. 准备数据

为了使用BERT,我们需要将数据转换成BERT使用的格式——我们有csv格式的评论文件,BERT对数据的要求比较特殊,它要求数据按如下所示的特定格式保存在tsv文件中(四列,没有标题行):

  • 第0列:行ID
  • 第1列:行标签(需要是int类型,如0、1、2、3等)
  • 第2列:这一列上,所有行的字母都相同——这是我们需要包含的比较多余的一列,不过BERT需要用到它。
  • 第3列:我们要分类的文本示例

在你克隆BERT的目录中创建一个文件夹,用于在其中添加三个独立的文件,分别是train.tsv、dev.tsv、test.tsv (tsv为制表符分隔值)。在train.tsv和dev.tsv中会包含所有4列。而在test.tsv中我们只需要保留2列,即用于标识行的id和要分类的文本。

以下代码演示了我们如何读取Yelp的评论,并对BERT进行恰当的设置,代码详见:http://gist.github.com/samk3211/1d233b29ce5acc93f4a3e8c13db8ccd3

3.使用预先训练好的BERT模型进行训练

在进行下一步之前,我们先再确认以下是否准备妥当:

  • 所有的.tsv文件都应该放在BERT目录下的data文件夹中。
  • 创建一个名为“bert_output”的文件夹,其中将保存调优后的模型。
  • 在BERT目录下存在预训练的BERT模型。
  • 命令中的路径是相对路径“./”

确认完后,就可以进入到你克隆BERT的目录,并输入以下命令:

代码语言:javascript
复制
python run_classifier.py 
--task_name=cola 
--do_train=true 
--do_eval=true 
--do_predict=true 
--data_dir=./data/ 
--vocab_file=./cased_L-12_H-768_A-12/vocab.txt 
--bert_config_file=./cased_L-12_H-768_A-12/bert_config.json 
--init_checkpoint=./cased_L-12_H-768_A-12/bert_model.ckpt 
--max_seq_length=128 
--train_batch_size=32 
--learning_rate=2e-5 
--num_train_epochs=3.0 
--output_dir=./bert_output/ 
--do_lower_case=False

如果我们观察终端上的输出,可以看到带有额外标记的输入文本的转换,这个之前我们在讨论BERT期望的各种输入标记时已经有所了解:

用BERT训练可能会出现内存溢出的错误。这表明你需要更强大的硬件能力——GPU、更多的RAM甚至TPU。不过,我们也可以尝试一些变通方法,然后再考虑提升硬件。例如,我们可以尝试减少training_batch_size;虽然这样做会使训练速度变慢,但天下没有免费的午餐,还是可以忍受的。

训练可能需要很长时间。所以你运行完命令后就可以先将其放在一边了,除非你的机器性能非常强劲。当然,在训练过程中你也不太能用你的电脑做其它的事情了——至少我在训练时就不能很好的用电脑工作。

我们可以在终端上看到进度日志。一旦训练完成,我们就会在bert_output目录中得到一个关于模型效果的报告;test_results.tsv是根据对测试数据集的预测在输出目录中生成的,其中包含类标签的预测概率值。

4. 对新数据进行预测

如果我们想对新的测试数据test.tsv进行预测,在模型训练完成后,我们就可以进入bert_output目录,并关注有最高数字值的model.ckpt文件。这些检查点文件包含训练模型的权重。一旦我们有了最高的检查点编号,我们可以再次运行run_classifier.py,但这次init_checkpoint应该设置为最高的模型检查点,如下所示:

代码语言:javascript
复制
export TRAINED_MODEL_CKPT=./bert_output/model.ckpt-[highest checkpoint number]

python run_classifier.py 
--task_name=cola 
--do_predict=true 
--data_dir=./data 
--vocab_file=./cased_L-12_H-768_A-12/vocab.txt 
--bert_config_file=/cased_L-12_H-768_A-12/bert_config.json 
--init_checkpoint=$TRAINED_MODEL_CKPT
--max_seq_length=128 
--output_dir=./bert_output

这将生成一个名为test_results.tsv的文件,其列的数目等于类标签的数目。

注意,在训练阶段我们已经设置了-do_predict =true。实际上这个参数设置可以省略,测试结果可以使用上面的命令单独生成。)

5. 知识拓展

上面我们使用的是开箱即用的解决方案进行训练。但是,我们也可以通过创建一个单独的新层来进行自定义fine-tune,该层经过训练可以使BERT适应我们的情绪分类任务(或任何其他任务)。不过这篇博文已经很长了,所以我不打算在此文中再扩展介绍自定义层的内容了,不过我可以提供两个参考:

  • 这里有一个用PyTorch实现的教程,也是基于相同的Yelp用户数据集。
  • 谷歌的研究人员创造了一个很棒的colab笔记本,它详细地展示了如何预测IMDB的电影评论是积极的还是消极的的过程,这个案例就是在Tensorflow中预先训练的BERT模型的基础上增加了一个新层。

总结

BERT是一个非常强大的语言模型,它是NLP领域的一个重要里程碑——它极大地提高了我们在NLP中进行迁移学习的能力;它可以为各种各样的NLP任务提供解决方案。在这篇文章中,我尝试给大家写了一个完整的BERT入门指南,希望大家能从中学到一些NLP的妙处。

如果你想了解更多关于BERT的信息,推荐你参考原始论文和相关的开源Github repo。当然在PyTorch中也有一个BERT的实现可供你学习。

原文链接:

BERT Explained: A Complete Guide with Theory and Tutorial

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/HBS5tZGyqzaxtCSvz3pJ
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券