什么是 BERT?

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

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

预训练的表示也可以是无上下文的上下文的,上下文表示还可以是单向的或 双向的。上下文无关模型(例如 word2vec或 GloVe)为词汇表中的每个单词生成单个“词嵌入”表示,因此在和bank中具有相同的表示。相反,上下文模型会根据句子中的其他单词生成每个单词的表示。bank depositriver 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 和BB后面的实际下一个句子是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)上长时间(1M 更新步骤)训练一个大型模型(12 层到 24 层 Transformer) ,这就是 BERT。

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

预训练相当昂贵(在 4 到 16 个 Cloud TPU 上需要 4 天),但对于每种语言都是一次性的(当前模型仅支持英语,但多语言模型将在不久的将来发布)。我们正在从论文中发布一些在 Google 进行预训练的预训练模型。大多数 NLP 研究人员永远不需要从头开始预训练他们自己的模型。

微调成本低。从完全相同的预训练模型开始,论文中的所有结果最多可以在单个 Cloud TPU 上复制 1 小时,或者在 GPU 上复制几个小时。例如,可以在单个 Cloud TPU 上对 SQuAD 进行大约 30 分钟的训练,以达到 91.0% 的 Dev F1 分数,这是最先进的单个系统。

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

此存储库中发布了什么?

我们正在发布以下内容:

  • BERT 模型架构(主要是标准的 Transformer架构)的 TensorFlow 代码。
  • 小写和大写版本的预训练检查点 BERT-Base以及BERT-Large来自论文的检查点。
  • TensorFlow 代码用于一键复制论文中最重要的微调实验,包括 SQuAD、MultiNLI 和 MRPC。

此存储库中的所有代码都可与 CPU、GPU 和 Cloud 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-Large, Uncased (Whole Word Masking):24层,1024隐藏,16头,340M参数
  • BERT-Large, Cased (Whole Word Masking):24层,1024隐藏,16头,340M参数
  • 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 文件包含三个项目:

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

使用 BERT 进行微调

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

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

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

使用 Cloud TPU 进行微调

下面的大多数示例都假设您将使用 Titan X 或 GTX 1080 等 GPU 在本地机器上运行训练/评估。

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

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

请参阅 Google Cloud TPU 教程 ,了解如何使用 Cloud TPU。或者,您可以使用 Google Colab 笔记本“ BERT FineTuning with Cloud TPUs ”。

在 Cloud TPU 上,预训练模型和输出目录需要位于 Google Cloud Storage 上。例如,如果您有一个名为 的存储桶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

这意味着开发集准确率为 84.55%。即使从相同的预训练检查点开始,像 MRPC 这样的小集在 Dev 集的准确性上也有很大的差异。如果您重新运行多次(确保指向不同的output_dir),您应该会看到 84% 和 88% 之间的结果。

一些其他的预训练模型是现成的 run_classifier.py,因此可以直接按照这些示例将 BERT 用于任何单句或句子对分类任务。

注意:您可能会看到一条消息Running train on CPU。这实际上只是意味着它运行在包含 GPU 的 Cloud 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/

小队 1.1

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

要在 SQuAD 上运行,您首先需要下载数据集。SQuAD 网站似乎不再链接到 v1.1 数据集,但可以在此处找到必要的文件:

  • 火车-v1.1.json
  • 开发-v1.1.json
  • evaluate-v1.1.py

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

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

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=True \
  --train_file=$SQUAD_DIR/train-v1.1.json \
  --do_predict=True \
  --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% 仅在 SQuAD 上训练的 F1 单系统:

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=True \
  --train_file=$SQUAD_DIR/train-v1.1.json \
  --do_predict=True \
  --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=True \
  --tpu_name=$TPU_NAME

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

{"f1": 90.87081895814865, "exact_match": 84.38978240302744}

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

小队 2.0

该模型也在run_squad.py.

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

  • 火车-v2.0.json
  • 开发-v2.0.json
  • evaluate-v2.0.py

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

在 Cloud 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=True \
  --train_file=$SQUAD_DIR/train-v2.0.json \
  --do_predict=True \
  --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=True \
  --tpu_name=$TPU_NAME \
  --version_2_with_negative=True

我们假设您已将输出目录中的所有内容复制到名为 ./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=False \
  --train_file=$SQUAD_DIR/train-v2.0.json \
  --do_predict=True \
  --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=True \
  --tpu_name=$TPU_NAME \
  --version_2_with_negative=True \
  --null_score_diff_threshold=$THRESH

内存不足问题

论文中的所有实验都在具有 64GB 设备 RAM 的 Cloud TPU 上进行了微调。因此,在使用具有 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) 上对最大批量大小进行了基准测试:

系统 序列长度 最大批量大小
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 上使用更大的有效批量大小。该代码将基于以下一种(或两种)技术:

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

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

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

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

在某些情况下,不是端到端微调整个预训练模型,而是获得预训练的上下文嵌入,这是从预训练的隐藏层生成的每个输入标记的固定上下文表示。 - 训练模型。这也应该可以缓解大多数内存不足的问题。

例如,我们包含extract_features.py可以像这样使用的脚本:

# Sentence A and Sentence B are separated by the ||| delimiter for sentence
# pair tasks like question answering and entailment.
# For single sentence inputs, put one sentence per line and DON'T use the
# delimiter.
echo 'Who was Jim Henson ? ||| Jim Henson was a puppeteer' > /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.This message is expected 的消息,这仅表示我们正在使用init_from_checkpoint()API 而不是保存的模型 API。如果您没有指定检查点或指定无效的检查点,此脚本会报错。

代币化

对于句子级任务(或句子对)任务,标记化非常简单。只需按照 和 中的示例run_classifier.py代码extract_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链接的 from )。例如,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)。

如果您有一个带有单词级别注释的预标记表示,您可以简单地独立标记每个输入单词,并确定性地保持原始到标记的对齐方式:

### Input
orig_tokens = ["John", "Johanson", "'s",  "house"]
labels      = ["NNP",  "NNP",      "POS", "NN"]

### Output
bert_tokens = []

# Token map will be an int -> int mapping between the `orig_tokens` index and
# the `bert_tokens` index.
orig_to_tok_map = []

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

bert_tokens.append("[CLS]")
for orig_token in orig_tokens:
  orig_to_tok_map.append(len(bert_tokens))
  bert_tokens.extend(tokenizer.tokenize(orig_token))
bert_tokens.append("[SEP]")

# bert_tokens == ["[CLS]", "john", "johan", "##son", "'", "s", "house", "[SEP]"]
# orig_to_tok_map == [1, 2, 4, 6]

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

有一些常见的英语标记化方案会导致 BERT 的预训练方式略有不匹配。例如,如果您的输入标记化拆分了诸如 之类的收缩do n't,这将导致不匹配。如果可以这样做,您应该预处理您的数据以将它们转换回看起来很原始的文本,但如果不可能,这种不匹配可能不是什么大问题。

使用 BERT 进行预训练

我们正在发布代码来对任意文本语料库进行“蒙面 LM”和“下一句预测”。请注意,这不是论文中使用的确切代码(原始代码是用 C++ 编写的,并且有一些额外的复杂性),但该代码确实会生成论文中描述的预训练数据。

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

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

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

是每个序列的max_predictions_per_seq最大屏蔽 LM 预测数。您应该将其设置为大约max_seq_lengthmasked_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=True \
  --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_seqrun_pretraining.pycreate_pretraining_data.py

python run_pretraining.py \
  --input_file=/tmp/tf_examples.tfrecord \
  --output_dir=/tmp/pretraining_output \
  --do_train=True \
  --do_eval=True \
  --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上预训练 a ,这需要大约 2 周时间,成本约为 500 美元(基于 2018 年 10 月的定价)。与论文中使用的相比,仅在单个 Cloud TPU 上进行训练时,您将不得不缩小批量大小。建议使用适合 TPU 内存的最大批量大小。

预训练数据

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

不幸的是,收集 BookCorpus的研究人员不再提供公开下载。Project Guttenberg 数据集是一个较小的 (2 亿字)公共领域的旧书集合。

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

学习一个新的 WordPiece 词汇

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

  • Google 的 SentencePiece 库

  • tensor2tensor 的 WordPiece 生成脚本

  • Rico Sennrich 的字节对编码库

在 Colab 中使用 BERT

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

FAQ

此代码与 Cloud TPU 兼容吗?GPU 呢?

是的,这个存储库中的所有代码都可以与 CPU、GPU 和 Cloud TPU 一起使用。但是,GPU 训练仅限于单 GPU。

我收到内存不足错误,怎么了?

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

有可用的 PyTorch 版本吗?

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

是否有可用的 Chainer 版本?

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

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

是的,我们计划在不久的将来发布多语言 BERT 模型。我们无法就将包含哪些语言做出确切承诺,但它可能是一个单一模型,其中包含大多数拥有庞大维基百科的语言。

BERT-Large发布更大的模型吗?

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

这个库是在什么许可证下发布的?

所有代码模型均在 Apache 2.0 许可下发布。有关详细信息,请参阅 LICENSE文件。

你可能感兴趣的:(自然语言处理(NLP),bert,自然语言处理,深度学习)