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

阅读数:573 2019 年 12 月 13 日 09:07

五问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:深入理解NLP领域爆红的预训练模型

将 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:深入理解NLP领域爆红的预训练模型

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 就要预测第二个句子是否是随机的,并假设这个随机的句子与第一个句子是断开的:

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

预测第二句与第一句是否是连接的,基本上完整的输入序列经过 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 模型通过学习两个额外的向量来训练我们的应用程序,这两个向量分别表示答案的开头和结尾。

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

就像句子对任务一样,问题变成了输入序列中的第一个句子,段落变成了第二个句子。不过,在这里 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 库克隆到你自己的电脑上。在你的终端上,输入

复制代码
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 的目录,并输入以下命令:

复制代码
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:深入理解NLP领域爆红的预训练模型

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

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

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

4. 对新数据进行预测

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

复制代码
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

评论

发布