BERT

  • 一、Bert代码速读

这一部分代码来源是google research 在github上发布的官网链接:google-research/bert讲讲代码中容易忽略但是很重要的点,帮助你在较短时间内实践Bert,所要掌握的必要代码。解析Google research官方发布的Bert源码(给出连接)的主要结构,重点讲run_classifier.py,run_squad.py,modeling.py中模型构建的核心代码

1.Bert代码结构

BERT_第1张图片

2.两个微调模型run_classifier.py和run_squad.py的create _model 部分核心代码。

3.预训练模型层modeling.py 中的attention_layer代码,包含原理图和代码解析。

BERT_第2张图片

attention layer原理图

BERT_第3张图片

预训练阶段,对机器和数据量要求高,所幸,作者提供了主要语言英语和中文的预训练模 型,直接下载即可,中文预训练模型。因此,我们重点关注的是,如何构建和利用微调模型实现我们的目标。重点讲下作者在源码中给出的两个微调模型。看完之后,你可以会惊呼微调模型竟然这么简单。用run_classifier.py,整个50万样本量,微调阶段训练时间约为半个小时。

从代码中可以看到,run_squad.py和run_classifier.py微调模型是一层简单的全链接层,以此类推,如果你要实现命名实体识别等其他目标任务,可在预训练的模型基础上,加入少了全链接层。

  • 二、我趟过的一些坑

1.tensorflow服务化部署的坑。

Tensorflow服务化部署有好几套接口,有版本历史原因,导致相互之间不兼容,对用户来说可谓非常不友好。我这边提供一个可用的接口方法供参考。 TensorFlow 模型如何对外提供服务。 微调模型跑出结果模型是checkpoint的文件格式,需转化为.pt格式提供出来。

2.TPU改成GPU estimator

官网源码中给出的是TPU estimator接口,改成普通estimator接口方案就能跑起来了。

https://www.tensorflow.org/guide/estimators​www.tensorflow.org

 

给个示例,run_classifier.py中关于TPU estimator的修改,直接上代码吧

(1)main()函数中estimator定义部分的修改

BERT_第4张图片

源代码中的定义

修改后的定义

(2) model_fn()部分代码修改,给个train部分的示例,eval部分同理可得。

BERT_第5张图片

源码

BERT_第6张图片

修改后

3.Out of Memory问题

官网源码中Readme.md中有关于Out of Memory解决的方法,如果你遇到类似问题,一定要先看这部分文档。文中意思大概是调节两个参数max_seq_lengthtrain_batch_size,观察你GPU显存的占用情况 。我使用的GPU显存28GB,如果你想把微调模型跑起来,这个显存基本够了(train_batch_size=64,--max_seq_length=128 显存占用22GB)

  • 三、参考资料

1.Bert as Service hanxiao/bert-as-service

 

BERT_第7张图片

图优化方法

Bert预训练模型较为完整的服务化部署方法,预训练模型可作为NLP基础服务。源码中两个亮点:一是提供了图优化的方法,提升效率和降低显存消耗。Freezed图冻结把tf.Variable变为tf.Constant,Pruned去掉训练时多余的节点,Quantized降低浮点数维度,比如把int64改为int32。二是zeromq实现异步并发请求,设计了一套Bert服务化部署的软件架构。

2.参考我的另一篇博文,关于Bert的原理章鱼小丸子:NLP突破性成果 BERT 模型详细解读

  • 四、总结

关于Bert的效果,我未做定量分析,但从个人评估结果来看,其在公开数据集的泛化能力,明显优于利用词向量预训练的QAnet等问答其他模型。

关于Bert的性能,对服务进行压力测试,根据应用场景,我调节的max length=30,耗时均值在400ms左右,能满足一般应用qps要求。如果对运算速度要求更高的产品,需改为GPU分布式计算。

较低成本实践Bert的路径:

第一步:找一台满足GPU显存要求的机器(一般是28GB左右,不同情况略有不同)

第二步:设置一个你能拿到数据集的微调任务,如分类、问答、实体标注等。

第三步:修改微调代码跑起来,验证效果。

BERT

*****新的2019年2月7日:TfHub模块*****

BERT已上传至TensorFlow Hub。有关run_classifier_with_tfhub.py如何使用TF Hub模块的示例,请参阅 Colab上的浏览器中的示例。

*****新的2018年11月23日:未规范化的多语言模式+泰语+蒙古语*****

我们上传了一个新的多语言模型,它不对输入执行任何规范化(没有下壳,重音剥离或Unicode规范化),还包括泰语和蒙古语。

建议使用此版本开发多语言模型,尤其是使用非拉丁字母的语言。

这不需要任何代码更改,可以在此处下载:

  • BERT-Base, Multilingual Cased:104种语言,12层,768隐藏,12头,110M参数

*****新的2018年11月15日:SOTA SQuAD 2.0系统*****

我们发布了代码更改,以重现我们的83%F1 SQuAD 2.0系统,目前排行榜上第一名是3%。有关详细信息,请参阅自述文件的SQuAD 2.0部分。

*****新的2018年11月5日:第三方PyTorch和Chainer版本的BERT可用*****

来自HuggingFace的NLP研究人员制作了 PyTorch版本的BERT, 它与我们预先训练好的检查点兼容,并能够重现我们的结果。Sosuke Kobayashi也提供了BERT的 Chainer 版本 (谢谢!)我们没有参与PyTorch实现的创建或维护,所以请将任何问题直接提交给该存储库的作者。

*****新的2018年11月3日:提供多种语言和中文模式*****

我们提供了两种新的BERT型号:

  • BERT-Base, Multilingual (不推荐使用,Multilingual Cased代替使用):102种语言,12层,768隐藏,12头,110M参数
  • BERT-Base, Chinese:中文简体和繁体,12层,768隐藏,12头,110M参数

我们对中文使用基于字符的标记化,对所有其他语言使用WordPiece标记化。两种模型都应该开箱即用,不需要任何代码更改。我们确实更新了BasicTokenizerin 的实现tokenization.py以支持中文字符标记化,所以如果你分叉它请更新。但是,我们没有更改标记化API。

有关更多信息,请参阅 多语言自述文件。

*****结束新信息*****

介绍

BERT,或 idirectional Ë ncoder ř从对产权 Ť ransformers,是预训练语言表示的一种新方法,用于获得一个宽阵列自然语言处理(NLP)的任务的状态的最先进的结果。

我们的学术论文详细描述了BERT并提供了许多任务的完整结果,请访问:https: //arxiv.org/abs/1810.04805。

为了给出几个数字,这里是 SQuAD v1.1问题回答任务的结果:

SQuAD v1.1排行榜(2018年10月8日) 测试EM 测试F1
第一名乐团 - BERT 87.4 93.2
第二名合奏团 - nlnet 86.0 91.7
第一名单身模型 - BERT 85.1 91.8
第二名单身模特 - nlnet 83.5 90.1

以及几种自然语言推理任务:

系统 MultiNLI 问题NLI 赃物
BERT 86.7 91.1 86.3
OpenAI GPT(上一页SOTA) 82.2 88.1 75.0

还有许多其他任务。

而且,这些结果都是在几乎没有任务特定的神经网络架构设计的情况下获得的。

如果您已经知道BERT是什么并且您只想开始使用,您可以 下载预先训练过的模型并 在几分钟内完成最先进的微调。

什么是BERT?

BERT是一种预训练语言表示的方法,这意味着我们在大型文本语料库(如维基百科)上训练通用的“语言理解”模型,然后将该模型用于我们关心的下游NLP任务(如问题)接听)。BERT优于以前的方法,因为它是第一个用于预训练NLP的无监督深度双向系统。

无监督意味着BERT仅使用纯文本语料库进行训练,这很重要,因为大量纯文本数据在网络上以多种语言公开。

预先训练的表示也可以是无上下文的上下文的,并且上下文表示可以进一步是单向的或 双向的。诸如word2vec或 GloVe之类的无上下文模型为 词汇表中的每个单词生成单个“单词嵌入”表示,因此bankbank deposit和中具有相同的表示river bank。相反,上下文模型生成基于句子中其他单词的每个单词的表示。

BERT建立在最近的预训练上下文表示工作的基础上 - 包括半监督序列学习, 生成预训练, ELMo和 ULMFit - 但关键的是这些模型都是单向浅双向的。这意味着每个单词仅使用左侧(或右侧)的单词进行语境化。例如,在句子中I made a bank deposit,单向表示bank仅基于I made a但不 基于deposit。之前的一些工作确实结合了来自单独的左上下文和右上下文模型的表示,但仅以“浅层”方式。BERT使用其左右上下文代表“银行” - I made a ... deposit - 从深度神经网络的最底部开始,因此它是非常双向的

BERT使用一种简单的方法:我们屏蔽输入中15%的字,通过深度双向Transformer编码器运行整个序列 ,然后仅预测屏蔽的字。例如:

Input: the man went to the [MASK1] . he bought a [MASK2] of milk.
Labels: [MASK1] = store; [MASK2] = gallon

为了学习句子之间的关系,我们也培养上可以从任何单语语料库来生成一个简单的任务:给定两个句子A 和B,是B自带后的实际下句A,或者只是随机的句子从语料库?

Sentence A: the man went to the store .
Sentence B: he bought a gallon of milk .
Label: IsNextSentence
Sentence A: the man went to the store .
Sentence B: penguins are flightless .
Label: NotNextSentence

然后,我们在大型语料库(Wikipedia + BookCorpus)上训练了一个大型模型(12层到24层变换器)很长一段时间(1M更新步骤),那就是BERT。

使用BERT有两个阶段:预训练微调

预培训费用相当昂贵(4到16个云TPU为4天),但是每种语言都是一次性程序(目前的模型仅限英语,但多语言模型将在不久的将来发布)。我们正在发布一些预先培训的模型,这些模型是在谷歌预先培训过的。大多数NLP研究人员永远不需要从头开始训练他们自己的模型。

微调很便宜。本文中的所有结果可以在单个云TPU上最多1小时复制,或者在GPU上几小时复制,从完全相同的预训练模型开始。例如,SQUAD可以在单个Cloud TPU上训练大约30分钟,以获得91.0%的Dev F1得分,这是单系统最先进的。

BERT的另一个重要方面是它可以非常容易地适应许多类型的NLP任务。在本文中,我们展示了句子级别(例如,SST-2),句子对级别(例如,MultiNLI),单词级别(例如,NER)和跨度级别的最新结果。 (例如,SQuAD)任务几乎没有任务特定的修改。

该存储库中发布了什么?

我们发布以下内容:

  • 用于BERT模型架构的TensorFlow代码(主要是标准的 Transformer架构)。
  • 预先训练的检查站小写和套管两种版本 BERT-Base,并BERT-Large从纸张。
  • TensorFlow代码用于按钮复制本文最重要的微调实验,包括SQuAD,MultiNLI和MRPC。

此存储库中的所有代码都与CPU,GPU和云TPU一起开箱即用。

预先训练的模型

我们正在从论文中发布BERT-BaseBERT-Large模型。 Uncased表示文本在WordPiece标记化之前已经小写,例如John Smith变为john smith。该Uncased模型还剥离了任何重点标记。Cased表示保留真实案例和重音标记。通常情况下,Uncased除非您知道案例信息对您的任务很重要(例如,命名实体识别或词性标注),否则模型会更好。

这些模型都是在与源代码(Apache 2.0)相同的许可下发布的。

有关多语言和中文模型的信息,请参阅 多语言自述文件。

使用套管模型时,请务必转到--do_lower=False培训脚本。(或者do_lower_case=False直接传递给FullTokenizer你,如果你使用自己的脚本。)

这里是指向模型的链接(右键单击名称上的“将链接另存为...”):

  • BERT-Base, Uncased:12层,768隐藏,12头,110M参数
  • BERT-Large, Uncased:24层,1024个隐藏,16个头,340M参数
  • BERT-Base, Cased:12层,768隐藏,12头,110M参数
  • BERT-Large, Cased:24层,1024个隐藏,16个头,340M参数
  • BERT-Base, Multilingual Cased (New, recommended):104种语言,12层,768隐藏,12头,110M参数
  • BERT-Base, Multilingual Uncased (Orig, not recommended) (不推荐使用,Multilingual Cased代替使用):102种语言,12层,768隐藏,12头,110M参数
  • BERT-Base, Chinese:中文简体和繁体,12层,768隐藏,12头,110M参数

每个.zip文件包含三个项目:

  • TensorFlow检查点(bert_model.ckpt)包含预先训练的权重(实际上是3个文件)。
  • vocab.txt用于将WordPiece映射到word id的词汇文件()。
  • 配置文件(bert_config.json),指定模型的超参数。

使用BERT进行微调

重要提示:本文的所有结果都在单个云TPU上进行了微调,后者具有64GB的RAM。目前无法BERT-Large使用具有12GB - 16GB RAM的GPU在纸上重新生成大部分 结果,因为可以适合内存的最大批量大小太小。我们正在努力向此存储库添加代码,以便在GPU上实现更大的有效批量大小。有关更多详细信息,请参阅有关内存不足问题的部分。

此代码使用TensorFlow 1.11.0进行了测试。它使用Python2和Python3进行了测试(但在Python2中更彻底,因为这是在Google内部使用的)。

使用的微调示例BERT-Base应该能够使用给定的超参数在具有至少12GB RAM的GPU上运行。

使用云TPU进行微调

下面的大多数示例都假设您将使用像Titan X或GTX 1080这样的GPU在本地计算机上运行培训/评估。

但是,如果您可以访问要训练的Cloud TPU,只需将以下标志添加到run_classifier.pyrun_squad.py

  --use_tpu=True \
  --tpu_name=$TPU_NAME

有关如何使用Cloud TPU的信息,请参阅 Google Cloud TPU教程。或者,您可以使用Google Colab笔记本“ BERT FineTuning with Cloud TPU ”。

在云TPU上,预训练模型和输出目录需要在Google云端存储上。例如,如果您有一个名为的存储桶some_bucket,则可以使用以下标志:

  --output_dir=gs://some_bucket/my_output_dir/

解压缩的预训练模型文件也可以在Google Cloud Storage文件夹中找到gs://bert_models/2018_10_18。例如:

export BERT_BASE_DIR=gs://bert_models/2018_10_18/uncased_L-12_H-768_A-12

句子(和句子对)分类任务

在运行此示例之前,您必须通过运行 此脚本下载 GLUE数据 并将其解压缩到某个目录。接下来,下载 检查点并将其解压缩到某个目录。$GLUE_DIRBERT-Base$BERT_BASE_DIR

此示例代码BERT-Base对Microsoft Research Paraphrase Corpus(MRPC)语料库进行了微调,该语料库仅包含3,600个示例,并且可以在几分钟内在大多数GPU上进行微调。

export BERT_BASE_DIR = / path / to / bert / uncased_L-12_H-768_A-12
 export GLUE_DIR = / path / to / glue

python run_classifier.py \
  --task_name = MRPC \
  --do_train = true \
  --do_eval = true \
  --data_dir = $ GLUE_DIR / MRPC \
  --vocab_file = $ BERT_BASE_DIR /vocab.txt \
  --bert_config_file = $ BERT_BASE_DIR /bert_config.json \
  --init_checkpoint = $ BERT_BASE_DIR /bert_model.ckpt \
  --max_seq_length = 128 \
  --train_batch_size = 32 \
  --learning_rate = 2e-5 \
  --num_train_epochs = 3.0 \
  --output_dir = / TMP / mrpc_output /

你应该看到这样的输出:

***** Eval results *****
  eval_accuracy = 0.845588
  eval_loss = 0.505248
  global_step = 343
  loss = 0.505248

这意味着Dev设定精度为84.55%。像MRPC这样的小集在Dev设置精度方面具有很大的差异,即使从相同的训练前检查点开始也是如此。如果您重复运行多次(确保指向不同output_dir),您应该看到84%和88%之间的结果。

其他一些预先训练好的模型是现成的 run_classifier.py,所以按照这些例子直接使用BERT进行任何单句或句子对分类任务应该是直截了当的。

注意:您可能会看到一条消息Running train on CPU。这实际上只意味着它运行的是除了包含GPU的云TPU之外的其他东西。

从分类器预测

训练完分类器后,可以使用--do_predict = true命令在推理模式下使用它。您需要在输入文件夹中有一个名为test.tsv的文件。输出将在输出文件夹中名为test_results.tsv的文件中创建。每行将包含每个样本的输出,列是类概率。

export BERT_BASE_DIR = / path / to / bert / uncased_L-12_H-768_A-12
 export GLUE_DIR = / path / to / glue
 export TRAINED_CLASSIFIER = / path / to / fine / tuned / classifier

python run_classifier.py \
  --task_name = MRPC \
  --do_predict = true \
  --data_dir = $ GLUE_DIR / MRPC \
  --vocab_file = $ BERT_BASE_DIR /vocab.txt \
  --bert_config_file = $ BERT_BASE_DIR /bert_config.json \
  --init_checkpoint = $ TRAINED_CLASSIFIER \
  --max_seq_length = 128 \
  --output_dir = / TMP / mrpc_output /

SQuAD 1.1

斯坦福问答数据集(SQuAD)是一个回答基准数据集的流行问题。BERT(在发布时)在SQuAD上获得最先进的结果,几乎没有任务特定的网络架构修改或数据增加。但是,它确实需要半复杂的数据预处理和后处理来处理(a)SQUAD上下文段落的可变长度性质,以及(b)用于SQuAD训练的字符级答案注释。该处理已实施并记录在其中run_squad.py

要在SQuAD上运行,首先需要下载数据集。该 小队的网站似乎并没有任何再链接到1.1版的数据集,但必要的文件可以在这里找到:

  • 列车v1.1.json
  • DEV-v1.1.json
  • evaluate-v1.1.py

将这些下载到某个目录$SQUAD_DIR

由于内存限制,目前无法在12GB-16GB GPU上再现最先进的SQuAD结果(事实上,即使批量大小1似乎也不适合12GB GPU使用BERT-Large)。但是,BERT-Base可以使用这些超参数在GPU上训练相当强大的 模型:

python run_squad.py \
  --vocab_file = $ BERT_BASE_DIR /vocab.txt \
  --bert_config_file = $ BERT_BASE_DIR /bert_config.json \
  --init_checkpoint = $ BERT_BASE_DIR /bert_model.ckpt \
  --do_train =真\
  --train_file = $ SQUAD_DIR /train-v1.1.json \
  --do_predict =真\
  --predict_file = $ SQUAD_DIR /dev-v1.1.json \
  --train_batch_size = 12 \
  --learning_rate = 3e-5 \
  --num_train_epochs = 2.0 \
  --max_seq_length = 384 \
  --doc_stride = 128 \
  --output_dir = / TMP / squad_base /

开发集预测将保存到以下文件中调用的文件predictions.jsonoutput_dir

python $ SQUAD_DIR /evaluate-v1.1.py $ SQUAD_DIR /dev-v1.1.json ./squad/predictions.json

哪个应该产生这样的输出:

{  f1 :88.41249612335034, exact_match :81.2488174077578}

您应该会看到类似于论文中报告的88.5%的结果 BERT-Base

如果您可以访问Cloud TPU,则可以使用BERT-Large。这是一组超参数(与纸张略有不同),它一致地获得了大约90.5%-91.0%的F1单系统仅在SQuAD上训练:

python run_squad.py \
  --vocab_file = $ BERT_LARGE_DIR /vocab.txt \
  --bert_config_file = $ BERT_LARGE_DIR /bert_config.json \
  --init_checkpoint = $ BERT_LARGE_DIR /bert_model.ckpt \
  --do_train =真\
  --train_file = $ SQUAD_DIR /train-v1.1.json \
  --do_predict =真\
  --predict_file = $ SQUAD_DIR /dev-v1.1.json \
  --train_batch_size = 24 \
  --learning_rate = 3e-5 \
  --num_train_epochs = 2.0 \
  --max_seq_length = 384 \
  --doc_stride = 128 \
  --output_dir = gs:// some_bucket / squad_large / \
  --use_tpu =真\
  --tpu_name = $ TPU_NAME

例如,使用这些参数进行一次随机运行会产生以下Dev得分:

{  f1 :90.87081895814865, exact_match :84.38978240302744}

如果您在此之前对TriviaQA上的一个时期进行 微调,结果会更好,但您需要将TriviaQA转换为SQuAD json格式。

SQuAD 2.0

该模型也在实施和记录中run_squad.py

要在SQuAD 2.0上运行,首先需要下载数据集。必要的文件可以在这里找到:

  • 列车v2.0.json
  • DEV-v2.0.json
  • evaluate-v2.0.py

将这些下载到某个目录$SQUAD_DIR

在云TPU上,您可以使用BERT-Large运行,如下所示:

python run_squad.py \
  --vocab_file = $ BERT_LARGE_DIR /vocab.txt \
  --bert_config_file = $ BERT_LARGE_DIR /bert_config.json \
  --init_checkpoint = $ BERT_LARGE_DIR /bert_model.ckpt \
  --do_train =真\
  --train_file = $ SQUAD_DIR /train-v2.0.json \
  --do_predict =真\
  --predict_file = $ SQUAD_DIR /dev-v2.0.json \
  --train_batch_size = 24 \
  --learning_rate = 3e-5 \
  --num_train_epochs = 2.0 \
  --max_seq_length = 384 \
  --doc_stride = 128 \
  --output_dir = gs:// some_bucket / squad_large / \
  --use_tpu =真\
  --tpu_name = $ TPU_NAME \
  --version_2_with_negative =真

我们假设您已将所有内容从输出目录复制到名为./squad/的本地目录。初始开发设置预测将位于./squad/predictions.json,无答案得分(“”)与每个问题的最佳非空答案之间的差异将在文件中./squad/null_odds.json

运行此脚本以调整阈值以预测空答案与非空答案:

python $ SQUAD_DIR / evaluate-v2.0.py $ SQUAD_DIR / dev-v2.0.json ./squad/predictions.json --na-prob-file ./squad/null_odds.json

假设脚本输出“best_f1_thresh”THRESH。(典型值介于-1.0和-5.0之间)。您现在可以重新运行模型以使用派生阈值生成预测,或者您可以从./squad/nbest_predictions.json中提取相应的答案。

python run_squad.py \
  --vocab_file = $ BERT_LARGE_DIR /vocab.txt \
  --bert_config_file = $ BERT_LARGE_DIR /bert_config.json \
  --init_checkpoint = $ BERT_LARGE_DIR /bert_model.ckpt \
  --do_train =假\
  --train_file = $ SQUAD_DIR /train-v2.0.json \
  --do_predict =真\
  --predict_file = $ SQUAD_DIR /dev-v2.0.json \
  --train_batch_size = 24 \
  --learning_rate = 3e-5 \
  --num_train_epochs = 2.0 \
  --max_seq_length = 384 \
  --doc_stride = 128 \
  --output_dir = gs:// some_bucket / squad_large / \
  --use_tpu =真\
  --tpu_name = $ TPU_NAME \
  --version_2_with_negative =真\
  --null_score_diff_threshold = $ THRESH

内存不足的问题

本文中的所有实验都在云TPU上进行了微调,后者具有64GB的设备RAM。因此,当使用具有12GB-16GB RAM的GPU时,如果使用本文中描述的相同超参数,则可能会遇到内存不足问题。

影响内存使用的因素有:

  • max_seq_length:已发布的模型的序列长度最高可达512,但您可以使用较短的最大序列长度进行微调,以节省大量内存。这由max_seq_length我们的示例代码中的标志控制。

  • train_batch_size:内存使用量也与批量大小成正比。

  • 模型类型,BERT-BaseBERT-LargeBERT-Large模型需要的内存明显多于BERT-Base

  • 优化器:BERT的默认优化器是Adam,它需要大量额外的内存来存储mv向量。切换到更高内存效率的优化器可以减少内存使用量,但也会影响结果。我们还没有尝试过其他优化器来进行微调。

使用默认培训脚本(run_classifier.pyrun_squad.py),我们使用TensorFlow 1.11.0对单个Titan X GPU(12GB RAM)上的最大批量大小进行基准测试:

系统 Seq长度 最大批量大小
BERT-Base 64 64
... 128 32
... 256 16
... 320 14
... 384 12
... 512 6
BERT-Large 64 12
... 128 6
... 256 2
... 320 1
... 384 0
... 512 0

不幸的是,这些最大批量大小BERT-Large非常小,实际上会损害模型精度,无论使用的学习率如何。我们正在努力向此存储库添加代码,这将允许在GPU上使用更大的有效批量大小。代码将基于以下技术中的一种(或两种):

  • 梯度累积:小批量中的样本通常与梯度计算无关(不包括批量标准化,此处未使用)。这意味着可以在执行权重更新之前累积多个较小的小型集团的梯度,这将完全等同于单个较大的更新。

  • 梯度检查点:DNN训练期间GPU / TPU内存的主要用途是缓存前向传递中的中间激活,这是在后向传递中进行有效计算所必需的。“梯度检查点”通过以智能方式重新计算激活来交换内存以获得计算时间。

但是,这在当前版本中未实现。

使用BERT提取固定的特征向量(如ELMo)

在某些情况下,不是对端到端的整个预训练模型进行微调,而是获得预先训练的上下文嵌入是有益的,这些嵌入是从前面的隐藏层生成的每个输入令牌的固定上下文表示。训练模型。这也应该缓解大多数内存不足的问题。

作为一个例子,我们包括extract_features.py可以像这样使用的脚本:

句子A和句子B由|||分隔 句子的分隔符
配对任务,如问答和蕴涵。
对于单句输入,把一句话每行,不使用的
分隔符。
回声 '谁是吉姆汉森?||| Jim Henson是一个木偶 舞者 ' > /tmp/input.txt

python extract_features.py \
  --input_file = / tmp / input.txt \
  --output_file = / tmp / output.jsonl \
  --vocab_file = $ BERT_BASE_DIR /vocab.txt \
  --bert_config_file = $ BERT_BASE_DIR /bert_config.json \
  --init_checkpoint = $ BERT_BASE_DIR /bert_model.ckpt \
  --layers = -1,-2,-3,-4 \
  --max_seq_length = 128 \
  --batch_size = 8

这将创建一个JSON文件(每行输入一行),其中包含指定的每个Transformer层的BERT激活layers(-1是Transformer的最后隐藏层,等等)

请注意,此脚本将生成非常大的输出文件(默认情况下,每个输入标记大约15kb)。

如果您需要保持原始单词和标记化单词之间的对齐(用于投影训练标签),请参阅下面的标记化部分。

注意:您可能会看到类似于Could not find trained model in model_dir: /tmp/tmpuB5g5c, running initialization to predict.此消息的消息,这只是意味着我们使用的是init_from_checkpoint()API而不是保存的模型API。如果您未指定检查点或指定无效检查点,则此脚本将发出警告。

符号化

对于句子级任务(或句子对)任务,标记化非常简单。只要按照示例代码run_classifier.pyextract_features.py。句子级任务的基本程序是:

  1. 实例化一个实例 tokenizer = tokenization.FullTokenizer

  2. 使用标记对原始文本进行标记tokens = tokenizer.tokenize(raw_text)

  3. 截断到最大序列长度。(最多可以使用512,但如果可能,可能需要使用更短的内存和速度原因。)

  4. 在正确的位置添加[CLS][SEP]标记。

字级和跨度级任务(例如,SQuAD和NER)更复杂,因为您需要保持输入文本和输出文本之间的对齐,以便您可以投影训练标签。SQuAD是一个特别复杂的例子,因为输入标签是基于字符的,而SQuAD段落通常比我们的最大序列长度长。请参阅代码run_squad.py以显示我们如何处理此问题。

在我们描述处理单词级任务的一般方法之前,了解我们的标记器正在做什么是很重要的。它有三个主要步骤:

  1. 文本规范化:将所有空白字符转换为空格,并(对于Uncased模型)将输入小写并删除重音标记。例如,John Johanson's, → john johanson's,

  2. 标点符号拆分:拆分两侧的所有标点符号(即在所有标点符号周围添加空格)。标点符号定义为(a)具有P*Unicode类的任何内容,(b)任何非字母/数字/空格ASCII字符(例如,$技术上不是标点符号的字符)。例如,john johanson's, → john johanson ' s ,

  3. WordPiece标记化:将空白标记化应用于上述过程的输出,并将 WordPiece 标记化分别应用于每个标记。(我们的实现直接基于来自的tensor2tensor,链接的)。例如,john johanson ' s , → john johan ##son ' s ,

该方案的优点是它与大多数现有的英语标记符“兼容”。例如,假设您有一个词性标记任务,如下所示:

Input:  John Johanson 's   house
Labels: NNP  NNP      POS NN

标记化输出将如下所示:

Tokens: john johan ##son ' s house

至关重要的是,这将是原始文本John Johanson's house(在之前没有空格's)的输出。

如果您有一个带有字级注释的预标记化表示,您可以简单地单独标记每个输入字,并确定性地维护原始到标记化的对齐:

 ##输入 
orig_tokens = [约翰约翰森 ]
labels       = [  NNP  NNP  POS  NN  ]

 ##输出 
bert_tokens = []

令牌地图将是一个返回int - `orig_tokens`指数之间> INT映射
了`bert_tokens`索引。
orig_to_tok_map = []

tokenizer = tokenization.FullTokenizer(
     vocab_file = vocab_file,do_lower_case = True)

bert_tokens.append( [CLS] for orig_tokens 中的 orig_token:
  orig_to_tok_map.append(len(bert_tokens))
  bert_tokens.extend(tokenizer.tokenize(orig_token))
bert_tokens.append( [SEP]  bert_tokens == [ “[CLS]”, “约翰”, “约翰”, “##儿子”, “'”, “S”, “家”, “[SEP]”] 
 orig_to_tok_map == [1,2 2,4,6]

现在orig_to_tok_map可以用于投影labels到标记化表示。

存在常见的英语标记化方案,这将导致BERT如何被预训练之间的轻微不匹配。例如,如果您的输入标记化分解了收缩do n't,这将导致不匹配。如果可以这样做,你应该预先处理你的数据,将它们转换回原始文本,但如果不可能,这种不匹配可能不是什么大问题。

使用BERT进行预训练

我们正在发布代码,在任意文本语料库上做“蒙面LM”和“下一句话预测”。请注意,这不是用于本文的确切代码(原始代码是用C ++编写的,并且具有一些额外的复杂性),但是此代码确实生成了本文所述的预训练数据。

以下是如何运行数据生成。输入是纯文本文件,每行一个句子。(重要的是这些是“下一句预测”任务的实际句子)。文档由空行分隔。输出是一组tf.train.Examples序列化为TFRecord文件格式。

您可以使用现成的NLP工具包(如spaCy)执行句子分段 。该create_pretraining_data.py脚本将连接段,直到它们达到最大序列长度,以最大限度地减少填充的计算浪费(有关更多详细信息,请参阅脚本)。但是,您可能需要在输入数据中有意添加少量噪声(例如,随机截断2%的输入段),以使其在微调期间对非句子输入更加稳健。

此脚本将整个输入文件的所有示例存储在内存中,因此对于大型数据文件,您应该对输入文件进行分片并多次调用脚本。(您可以将文件glob传入run_pretraining.py,例如, tf_examples.tf_record*。)

max_predictions_per_seq是每个序列的掩码LM预测的最大数量。您应该将其设置为max_seq_length* 左右masked_lm_prob(脚本不会自动执行此操作,因为需要将确切的值传递给两个脚本)。

python create_pretraining_data.py \
  --input_file =。/ sample_text.txt \
  --output_file = / tmp / tf_examples.tfrecord \
  --vocab_file = $ BERT_BASE_DIR /vocab.txt \
  --do_lower_case =真\
  --max_seq_length = 128 \
  --max_predictions_per_seq = 20 \
  --masked_lm_prob = 0.15 \
  --random_seed = 12345 \
  --dupe_factor = 5

以下是如何进行预训练。init_checkpoint如果您是从头开始进行预训练,请不要包括在内。模型配置(包括词汇大小)在中指定bert_config_file。此演示代码仅预先训练少量步骤(20),但实际上您可能希望设置 num_train_steps为10000步或更多。在max_seq_length和 max_predictions_per_seq传递到参数run_pretraining.py必须是相同的create_pretraining_data.py

python run_pretraining.py \
  --input_file = / tmp / tf_examples.tfrecord \
  --output_dir = / tmp / pretraining_output \
  --do_train =真\
  --do_eval =真\
  --bert_config_file = $ BERT_BASE_DIR /bert_config.json \
  --init_checkpoint = $ BERT_BASE_DIR /bert_model.ckpt \
  --train_batch_size = 32 \
  --max_seq_length = 128 \
  --max_predictions_per_seq = 20 \
  --num_train_steps = 20 \
  --num_warmup_steps = 10 \
  --learning_rate = 2E-5

这将产生如下输出:

***** Eval results *****
  global_step = 20
  loss = 0.0979674
  masked_lm_accuracy = 0.985479
  masked_lm_loss = 0.0979328
  next_sentence_accuracy = 1.0
  next_sentence_loss = 3.45724e-05

请注意,由于我们的sample_text.txt文件很小,这个示例训练只会在几个步骤中过度拟合这些数据,并产生不切实际的高精度数字。

预训练提示和警告

  • 如果使用自己的词汇,一定要改变vocab_size在 bert_config.json。如果你使用更大的词汇而不改变它,你可能会因为未经检查的越界访问而在GPU或TPU训练时获得NaN。
  • 如果您的任务具有大型特定于域的语料库(例如,“电影评论”或“科学论文”),则从BERT检查点开始,对您的语料库执行额外的预训练步骤可能会有所帮助。
  • 我们在论文中使用的学习率是1e-4。但是,如果您从现有BERT检查点开始执行额外的预训练步骤,则应使用较小的学习率(例如,2e-5)。
  • 目前的BERT模型仅限英语,但我们计划发布多语言模型,该模型在不久的将来已经过多种语言的预训练(希望在2018年11月底之前)。
  • 较长的序列不成比例地昂贵,因为注意力是序列长度的二次方。换句话说,一批64个长度为512的序列比一批256个长度为128的序列要昂贵得多。完全连接/卷积成本相同,但512长度序列的注意力成本要高得多。因此,一个好的方法是预先训练90,000步骤,序列长度为128,然后进行10,000个额外步骤,序列长度为512.非常长的序列主要用于学习位置嵌入,这可能是学得很快。请注意,这确实需要使用不同的值生成数据两次max_seq_length
  • 如果您是从头开始进行预训练,请做好准备,预训练计算成本很高,特别是在GPU上。如果您是从头开始进行预训练,我们推荐的配方是预先培训一个BERT-Base可 抢占的Cloud TPU v2,大约需要2周,费用约为500美元(根据2018年10月的定价)。与本文中使用的内容相比,仅在单个云TPU上进行培训时,您将不得不缩小批量大小。建议使用适合TPU内存的最大批量大小。

预训练数据

我们将无法发布论文中使用的预处理数据集。对于维基百科,建议的预处理是下载 最新的转储,提取文本WikiExtractor.py,然后应用任何必要的清理将其转换为纯文本。

不幸的是,收集BookCorpus的研究人员 不再可以公开下载。该 项目加滕伯格数据集 是旧的书是公共领域稍小(200M字)集合。

Common Crawl是另一个非常大的文本集合,但您可能需要进行大量的预处理和清理才能提取可用的语料库以进行BERT预训练。

学习一个新的WordPiece词汇表

此存储库不包含用于学习新WordPiece词汇表的代码。原因是本文中使用的代码是用C ++实现的,它依赖于Google的内部库。对于英语,从词汇和预训练模型开始几乎总是更好。对于其他语言的学习词汇表,有许多可用的开源选项。但请注意,这些与我们的tokenization.py库不兼容 :

  • 谷歌的SentencePiece图书馆

  • tensor2tensor的WordPiece生成脚本

  • Rico Sennrich的字节对编码库

在Colab中使用BERT

如果您想将BERT与Colab配合使用,您可以开始使用笔记本电脑“ BERT FineTuning with Cloud TPU ”。 在撰写本文时(2018年10月31日),Colab用户可以完全免费访问Cloud TPU。注意:每个用户一个,可用性有限,需要一个带有存储的Google云端平台帐户(尽管可以通过免费信用购买存储以便注册GCP),并且此功能将来可能不再可用。单击刚刚链接的BERT Colab以获取更多信息。

常问问题

此代码是否与Cloud TPU兼容?GPU怎么样?

是的,此存储库中的所有代码都与CPU,GPU和云TPU一起开箱即用。但是,GPU培训仅适用于单GPU。

我得到了内存错误,出了什么问题?

有关更多信息,请参阅有关内存不足问题的部分。

有PyTorch版本吗?

没有正式的PyTorch实现。然而,来自HuggingFace的NLP研究人员制作了一个 PyTorch版本的BERT, 它与我们预先训练好的检查点兼容,并能够重现我们的结果。我们没有参与PyTorch实现的创建或维护,因此请向该存储库的作者提出任何问题。

有Chainer版本吗?

没有正式的Chainer实施。然而,Sosuke Kobayashi制作了Biner的Chainer 版本, 它与我们预先训练好的 检查点兼容,并且能够重现我们的结果。我们没有参与Chainer实施的创建或维护,因此请向该存储库的作者提出任何问题。

是否会发布其他语言的模型?

是的,我们计划在不久的将来发布多语言BERT模型。我们无法确切地确定将包含哪些语言,但它可能是一个单一的模型,其中包括大多数具有显着大小的维基百科的语言。

模型是否会大于BERT-Large发布?

到目前为止,我们还没有尝试过训练任何大于BERT-Large。如果我们能够获得重大改进,我们可能会发布更大的模型。

该库发布的许可证是什么?

所有代码模型都是在Apache 2.0许可下发布的。有关LICENSE更多信息,请参阅该 文件。

我怎么引用BERT?

现在,引用Arxiv论文:

@article{devlin2018bert,
  title={BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding},
  author={Devlin, Jacob and Chang, Ming-Wei and Lee, Kenton and Toutanova, Kristina},
  journal={arXiv preprint arXiv:1810.04805},
  year={2018}
}

如果我们将论文提交给会议或期刊,我们将更新BibTeX。

你可能感兴趣的:(NLP)