google-research/bert

GitHub: google-research/bert

这是一个基于Transformer架构的预训练深度双向语言表示模型,用于提升各类自然语言处理任务的性能。

Stars: 39977 | Forks: 9707

# BERT **\*\*\*\*\* 2020 年 3 月 11 日更新:更小的 BERT 模型 \*\*\*\*\*** 这是 24 个更小的 BERT 模型(仅限英语,不区分大小写,使用 WordPiece 掩码训练)的发布,相关内容引用自 [Well-Read Students Learn Better: On the Importance of Pre-training Compact Models](https://arxiv.org/abs/1908.08962)。 我们已经证明,标准的 BERT 配方(包括模型架构和训练目标)在广泛的模型尺寸范围内都是有效的,不仅限于 BERT-Base 和 BERT-Large。较小的 BERT 模型旨在用于计算资源受限的环境。它们可以像原始 BERT 模型一样进行微调。然而,在知识蒸馏(knowledge distillation)的背景下,即微调标签由更大且更准确的教师模型生成时,它们最为有效。 我们的目标是让计算资源较少的机构能够进行研究,并鼓励社区寻求除增加模型容量之外的创新发展方向。 您可以从[这里][all]下载全部 24 个模型,或者从下表中单独下载: | |H=128|H=256|H=512|H=768| |---|:---:|:---:|:---:|:---:| | **L=2** |[**2/128 (BERT-Tiny)**][2_128]|[2/256][2_256]|[2/512][2_512]|[2/768][2_768]| | **L=4** |[4/128][4_128]|[**4/256 (BERT-Mini)**][4_256]|[**4/512 (BERT-Small)**][4_512]|[4/768][4_768]| | **L=6** |[6/128][6_128]|[6/256][6_256]|[6/512][6_512]|[6/768][6_768]| | **L=8** |[8/128][8_128]|[8/256][8_256]|[**8/512 (BERT-Medium)**][8_512]|[8/768][8_768]| | **L=10** |[10/128][10_128]|[10/256][10_256]|[10/512][10_512]|[10/768][10_768]| | **L=12** |[12/128][12_128]|[12/256][12_256]|[12/512][12_512]|[**12/768 (BERT-Base)**][12_768]| 请注意,此版本中包含的 BERT-Base 模型仅为了完整性;它是在与原始模型相同的机制下重新训练的。 以下是测试集上相应的 GLUE 分数: |Model|Score|CoLA|SST-2|MRPC|STS-B|QQP|MNLI-m|MNLI-mm|QNLI(v2)|RTE|WNLI|AX| |---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |BERT-Tiny|64.2|0.0|83.2|81.1/71.1|74.3/73.6|62.2/83.4|70.2|70.3|81.5|57.2|62.3|21.0| |BERT-Mini|65.8|0.0|85.9|81.1/71.8|75.4/73.3|66.4/86.2|74.8|74.3|84.1|57.9|62.3|26.1| |BERT-Small|71.2|27.8|89.7|83.4/76.2|78.8/77.0|68.1/87.0|77.6|77.0|86.4|61.8|62.3|28.6| |BERT-Medium|73.5|38.0|89.6|86.6/81.6|80.4/78.4|69.6/87.9|80.0|79.1|87.7|62.2|62.3|30.5| 对于每个任务,我们从以下列表中选择了最佳微调超参数,并训练了 4 个 epochs: - batch sizes: 8, 16, 32, 64, 128 - learning rates: 3e-4, 1e-4, 5e-5, 3e-5 如果您使用这些模型,请引用以下论文: ``` @article{turc2019, title={Well-Read Students Learn Better: On the Importance of Pre-training Compact Models}, author={Turc, Iulia and Chang, Ming-Wei and Lee, Kenton and Toutanova, Kristina}, journal={arXiv preprint arXiv:1908.08962v2 }, year={2019} } ``` **\*\*\*\*\* 2019 年 5 月 31 日更新:全词掩码模型 \*\*\*\*\*** 这是几个新模型的发布,它们是对预处理代码改进的 结果。 在原始的预处理代码中,我们随机选择 WordPiece 标记进行 掩码。例如: `Input Text: the man jumped up , put his basket on phil ##am ##mon ' s head` `Original Masked Input: man up , put his on phil ##mon ' s head` 新技术称为全词掩码(Whole Word Masking)。在这种情况下,我们总是 一次性掩盖对应于一个单词的*所有*标记。总体掩码 率保持不变。 `Whole Word Masked Input: the man up , put his basket on ' s head` 训练过程是相同的——我们仍然独立预测每个被掩码的 WordPiece 标记。 改进来自于这样一个事实:对于被拆分成多个 WordPieces 的单词,原始预测任务太“容易”了。 这可以通过在数据生成期间传递标志 `--do_whole_word_mask=True` 给 `create_pretraining_data.py` 来启用。 带有全词掩码的预训练模型链接如下。数据和 训练在其他方面完全相同,且模型与原始模型具有相同的结构和 词汇表。我们仅包含 BERT-Large 模型。使用 这些模型时,请在论文中明确说明您使用的是 BERT-Large 的全词掩码变体。 * **[`BERT-Large, Uncased (Whole Word Masking)`](https://storage.googleapis.com/bert_models/2019_05_30/wwm_uncased_L-24_H-1024_A-16.zip)**: 24 层,1024 隐藏层,16 个注意力头,340M 参数 * **[`BERT-Large, Cased (Whole Word Masking)`](https://storage.googleapis.com/bert_models/2019_05_30/wwm_cased_L-24_H-1024_A-16.zip)**: 24 层,1024 隐藏层,16 个注意力头,340M 参数 Model | SQUAD 1.1 F1/EM | Multi NLI Accuracy ---------------------------------------- | :-------------: | :----------------: BERT-Large, Uncased (Original) | 91.0/84.3 | 86.05 BERT-Large, Uncased (Whole Word Masking) | 92.8/86.7 | 87.07 BERT-Large, Cased (Original) | 91.5/84.8 | 86.09 BERT-Large, Cased (Whole Word Masking) | 92.9/86.7 | 86.46 **\*\*\*\*\* 2019 年 2 月 7 日更新:TfHub 模块 \*\*\*\*\*** BERT 已上传至 [TensorFlow Hub](https://tfhub.dev)。请参阅 `run_classifier_with_tfhub.py` 以获取如何使用 TF Hub 模块的示例, 或在浏览器上运行示例 [Colab](https://colab.sandbox.google.com/github/google-research/bert/blob/master/predicting_movie_reviews_with_bert_on_tf_hub.ipynb)。 **\*\*\*\*\* 2018 年 11 月 23 日更新:未规范化的多语言模型 + 泰语 + 蒙古语 \*\*\*\*\*** 我们上传了一个新的多语言模型,该模型*不*对输入执行任何规范化 (无小写化、去除重音符号或 Unicode 规范化),并且 额外包含泰语和蒙古语。 **建议使用此版本来开发多语言模型, 特别是对于使用非拉丁字母的语言。** 这不需要任何代码更改,可以在这里下载: * **[`BERT-Base, Multilingual Cased`](https://storage.googleapis.com/bert_models/2018_11_23/multi_cased_L-12_H-768_A-12.zip)**: 104 种语言,12 层,768 隐藏层,12 个注意力头,110M 参数 **\*\*\*\*\* 2018 年 11 月 15 日更新:SOTA SQuAD 2.0 系统 \*\*\*\*\*** 我们发布了代码更改以重现我们的 83% F1 SQuAD 2.0 系统,该系统目前在排行榜上领先 3%,位居第一。有关详细信息,请参阅 README 的 SQuAD 2.0 部分。 **\*\*\*\*\* 2018 年 11 月 5 日更新:第三方 PyTorch 和 Chainer 版本的 BERT 现已可用 \*\*\*\*\*** 来自 HuggingFace 的 NLP 研究人员制作了 [PyTorch 版本的 BERT](https://github.com/huggingface/pytorch-pretrained-BERT) 它与我们的预训练检查点兼容,并且能够重现 我们的结果。Sosuke Kobayashi 还制作了一个 [Chainer 版本的 BERT](https://github.com/soskek/bert-chainer) (谢谢!)。我们没有参与 PyTorch 实现的创建或维护,因此请将任何问题指向该存储库的作者。 **\*\*\*\*\* 2018 年 11 月 3 日更新:多语言和中文模型现已可用 \*\*\*\*\*** 我们发布了两个新的 BERT 模型: * **[`BERT-Base, Multilingual`](https://storage.googleapis.com/bert_models/2018_11_03/multilingual_L-12_H-768_A-12.zip) (不推荐,请改用 `Multilingual Cased`)**:102 种语言, 12 层,768 隐藏层,12 个注意力头,110M 参数 * **[`BERT-Base, Chinese`](https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip)**: 中文简体和繁体,12 层,768 隐藏层,12 个注意力头,110M 参数 我们使用基于字符的分词来处理中文,而使用 WordPiece 分词来处理 所有其他语言。这两种模型应该可以开箱即用,无需任何 代码更改。我们确实更新了 `tokenization.py` 中 `BasicTokenizer` 的实现 以支持中文字符分词,因此如果您 派生了该文件,请进行更新。但是,我们没有更改分词 API。 更多信息,请参阅 [多语言 README](https://github.com/google-research/bert/blob/master/multilingual.md)。 **\*\*\*\*\* 新信息结束 \*\*\*\*\*** ## 简介 **BERT**,即来自 **T**ransformers 的 **B**idirectional **E**ncoder **R**epresentations,是一种预训练语言表示的新方法,它在广泛的自然语言处理 (NLP)任务中获得了最先进的结果。 我们的学术论文详细描述了 BERT 并提供了在许多 任务上的完整结果,可以在这里找到: [https://arxiv.org/abs/1810.04805](https://arxiv.org/abs/1810.04805)。 为了给出一些数据,以下是 [SQuAD v1.1](https://rajpurkar.github.io/SQuAD-explorer/) 问答 任务的结果: SQuAD v1.1 Leaderboard (2018 年 10 月 8 日) | Test EM | Test F1 ------------------------------------- | :------: | :------: 第一名 集成模型 - BERT | **87.4** | **93.2** 第二名 集成模型 - nlnet | 86.0 | 91.7 第一名 单模型 - BERT | **85.1** | **91.8** 第二名 单模型 - nlnet | 83.5 | 90.1 以及几个自然语言推理任务: System | MultiNLI | Question NLI | SWAG ----------------------- | :------: | :----------: | :------: BERT | **86.7** | **91.1** | **86.3** OpenAI GPT (Prev. SOTA) | 82.2 | 88.1 | 75.0 还有许多其他任务。 此外,所有这些结果几乎不需要针对特定任务的 神经网络架构设计。 如果您已经了解 BERT 并且只想开始使用,您可以 [下载预训练模型](#pre-trained-models) 并 在几分钟内 [运行最先进的微调](#fine-tuning-with-bert)。 ## 什么是 BERT? BERT 是一种预训练语言表示的方法,这意味着我们在 大型文本语料库(如 Wikipedia)上训练通用的“语言理解”模型,然后将该模型用于我们关心的下游 NLP 任务(如问答)。BERT 优于之前的方法,因为它是第一个用于预训练 NLP 的*无监督*、*深度双向*系统。 *无监督*意味着 BERT 仅使用纯文本语料库进行训练,这很重要,因为网络上公开有大量纯文本数据,且涵盖多种语言。 预训练的表示也可以是*上下文无关*或*上下文相关*的, 而上下文相关的表示可以进一步分为*单向*或 *双向*。上下文无关的模型,如 [word2vec](https://www.tensorflow.org/tutorials/representation/word2vec) 或 [GloVe](https://nlp.stanford.edu/projects/glove/) 为词汇表中的单词生成单个“词嵌入”表示,因此 `bank` 在 `bank deposit` 和 `river bank` 中具有相同的表示。上下文相关的模型 则根据句子中的其他单词生成每个单词的表示。 BERT 建立在最近的预训练上下文相关表示的工作基础之上—— 包括 [Semi-supervised Sequence Learning](https://arxiv.org/abs/1511.01432)、 [Generative Pre-Training](https://blog.openai.com/language-unsupervised/)、 [ELMo](https://allennlp.org/elmo) 和 [ULMFit](http://nlp.fast.ai/classification/2018/05/15/introducting-ulmfit.html)—— 但至关重要的是,这些模型都是*单向*或*浅层双向*的。这意味着每个单词仅使用其左侧(或右侧)的单词进行上下文化。例如,在句子 `I made a bank deposit` 中, `bank` 的单向表示仅基于 `I made a` 而不基于 `deposit`。之前的一些工作确实结合了单独的左上下文和右上下文模型的表示,但只是以“浅层”的方式。BERT 同时使用其左右上下文表示“bank”——`I made a ... deposit`—— 从深度神经网络的底层开始,因此它是*深度双向*的。 BERT 对此采用了一种简单的方法:我们掩盖输入中 15% 的单词, 将整个序列通过深度双向 [Transformer](https://arxiv.org/abs/1706.03762) 编码器运行,然后仅预测 被掩盖的单词。例如: ``` 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](http://yknzhu.wixsite.com/mbweb))上长时间(100 万个更新步骤)训练一个大模型(12 层到 24 层的 Transformer),这就是 BERT。 使用 BERT 有两个阶段:*预训练*和*微调*。 **预训练**相当昂贵(在 4 到 16 个 Cloud TPU 上需要四天),但对于每种语言来说是一次性程序(目前的模型仅限英语,但多语言模型将在不久的将来发布)。我们发布了论文中的许多预训练模型,这些模型是在 Google 预训练的。 大多数 NLP 研究人员永远不需要从头开始预训练自己的模型。 **微调**很便宜。论文中的所有结果都可以 在单个 Cloud TPU 上最多 1 小时内,或者在 GPU 上几小时内, 从完全相同的预训练模型开始重现。例如,SQuAD 可以在单个 Cloud TPU 上训练大约 30 分钟,以达到 91.0% 的 Dev F1 分数,这是单系统的最先进水平。 BERT 的另一个重要方面是它可以很容易地适应许多类型的 NLP 任务。在论文中,我们在句子级(例如,SST-2)、句子对级(例如,MultiNLI)、单词级(例如,NER)和片段级(例如,SQuAD)任务上展示了最先进的结果,几乎不需要针对特定任务的修改。 ## 此存储库中发布了什么? 我们发布了以下内容: * BERT 模型架构的 TensorFlow 代码(主要是一个标准的 [Transformer](https://arxiv.org/abs/1706.03762) 架构)。 * 论文中 `BERT-Base` 和 `BERT-Large` 的小写和区分大小写版本的预训练检查点。 * 用于按按钮重现论文中最重要的微调实验的 TensorFlow 代码,包括 SQuAD、MultiNLI 和 MRPC。 此存储库中的所有代码都可以在 CPU、GPU 和 Cloud TPU 上开箱即用。 ## 预训练模型 我们正在发布论文中的 `BERT-Base` 和 `BERT-Large` 模型。 `Uncased` 意味着文本在 WordPiece 分词之前已被小写化, 例如,`John Smith` 变为 `john smith`。`Uncased` 模型还会去除任何重音符号。`Cased` 意味着保留真实的大小写和重音符号。通常,除非您知道大小写信息对您的任务很重要(例如,命名实体识别或词性标注),否则 `Uncased` 模型更好。 这些模型都是在与源代码相同的许可证(Apache 2.0)下发布的。 有关多语言和中文模型的信息,请参阅 [多语言 README](https://github.com/google-research/bert/blob/master/multilingual.md)。 **使用区分大小写的模型时,请务必将 `--do_lower=False` 传递给训练脚本。(如果您使用自己的脚本,请直接将 `do_lower_case=False` 传递给 `FullTokenizer`。)** 模型的链接在这里(右键单击名称,选择“另存链接为...”): * **[`BERT-Large, Uncased (Whole Word Masking)`](https://storage.googleapis.com/bert_models/2019_05_30/wwm_uncased_L-24_H-1024_A-16.zip)**: 24 层,1024 隐藏层,16 个注意力头,340M 参数 * **[`BERT-Large, Cased (Whole Word Masking)`](https://storage.googleapis.com/bert_models/2019_05_30/wwm_cased_L-24_H-1024_A-16.zip)**: 24 层,1024 隐藏层,16 个注意力头,340M 参数 * **[`BERT-Base, Uncased`](https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-12_H-768_A-12.zip)**: 12 层,768 隐藏层,12 个注意力头,110M 参数 * **[`BERT-Large, Uncased`](https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-24_H-1024_A-16.zip)**: 24 层,1024 隐藏层,16 个注意力头,340M 参数 * **[`BERT-Base, Cased`](https://storage.googleapis.com/bert_models/2018_10_18/cased_L-12_H-768_A-12.zip)**: 12 层,768 隐藏层,12 个注意力头,110M 参数 * **[`BERT-Large, Cased`](https://storage.googleapis.com/bert_models/2018_10_18/cased_L-24_H-1024_A-16.zip)**: 24 层,1024 隐藏层,16 个注意力头,340M 参数 * **[`BERT-Base, Multilingual Cased (New, recommended)`](https://storage.googleapis.com/bert_models/2018_11_23/multi_cased_L-12_H-768_A-12.zip)**: 104 种语言,12 层,768 隐藏层,12 个注意力头,110M 参数 * **[`BERT-Base, Multilingual Uncased (Orig, not recommended)`](https://storage.googleapis.com/bert_models/2018_11_03/multilingual_L-12_H-768_A-12.zip) (不推荐,请改用 `Multilingual Cased`)**:102 种语言, 12 层,768 隐藏层,12 个注意力头,110M 参数 * **[`BERT-Base, Chinese`](https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip)**: 中文简体和繁体,12 层,768 隐藏层,12 个注意力头,110M 参数 每个 .zip 文件包含三项: * 包含预训练权重的 TensorFlow 检查点(`bert_model.ckpt`)(实际上是 3 个文件)。 * 用于将 WordPiece 映射到单词 ID 的词汇表文件(`vocab.txt`)。 * 指定模型超参数的配置文件(`bert_config.json`)。 ## 使用 BERT 进行微调 **重要**:论文中的所有结果都是在单个 Cloud TPU 上进行微调的,该 TPU 具有 64GB 的 RAM。目前不可能使用具有 12GB - 16GB RAM 的 GPU 重现论文中的大多数 `BERT-Large` 结果,因为 可以放入内存的最大 batch size 太小了。我们正在向此存储库添加代码,以允许在 GPU 上使用更大的有效 batch size。有关更多详细信息,请参阅 [内存不足问题](#out-of-memory-issues) 部分。 此代码已通过 TensorFlow 1.11.0 测试。它已在 Python2 和 Python3 上进行了测试(但在 Python2 上测试得更彻底,因为这是 Google 内部使用的版本)。 使用 `BERT-Base` 的微调示例应该能够使用给定的超参数在至少具有 12GB RAM 的 GPU 上运行。 ### 使用 Cloud TPUs 进行微调 下面的大多数示例假设您将在本地机器上使用像 Titan X 或 GTX 1080 这样的 GPU 运行训练/评估。 但是,如果您有权访问想要训练的 Cloud TPU,只需将 以下标志添加到 `run_classifier.py` 或 `run_squad.py`: ``` --use_tpu=True \ --tpu_name=$TPU_NAME ``` 请参阅 [Google Cloud TPU 教程](https://cloud.google.com/tpu/docs/tutorials/mnist) 以了解如何使用 Cloud TPUs。或者,您可以使用 Google Colab 笔记本 "[BERT FineTuning with Cloud TPUs](https://colab.research.google.com/github/tensorflow/tpu/blob/master/tools/colab/bert_finetuning_with_cloud_tpus.ipynb)"。 在 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 ``` ### 句子(和句子对)分类任务 在运行此示例之前,您必须通过运行 [此脚本](https://gist.github.com/W4ngatang/60c2bdb54d156a41194446737ce03e2e) 下载 [GLUE 数据](https://gluebenchmark.com/tasks) 并将其解压到某个目录 `$GLUE_DIR`。接下来,下载 `BERT-Base` 检查点并将其解压到某个目录 `$BERT_BASE_DIR`。 此示例代码在 Microsoft Research Paraphrase Corpus (MRPC) 语料库上微调 `BERT-Base`,该语料库仅包含 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`。这实际上只是意味着它正在运行在非 Cloud TPU 的设备上,其中包括 GPU。 #### 从分类器进行预测 一旦训练了分类器,您就可以使用 --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 上获得了最先进的结果,几乎没有进行任何针对特定任务的网络架构修改或数据增强。然而,它确实需要半复杂的数据预处理和后处理来处理 SQuAD 上下文段落的可变长度性质以及用于 SQuAD 训练的字符级答案注释。此处理在 `run_squad.py` 中实现和记录。 要在 SQuAD 上运行,您首先需要下载数据集。[SQuAD 网站](https://rajpurkar.github.io/SQuAD-explorer/) 似乎不再 链接到 v1.1 数据集,但必要的文件可以在这里找到: * [train-v1.1.json](https://rajpurkar.github.io/SQuAD-explorer/dataset/train-v1.1.json) * [dev-v1.1.json](https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json) * [evaluate-v1.1.py](https://github.com/allenai/bi-att-flow/blob/master/squad/evaluate-v1.1.py) 将这些下载到某个目录 `$SQUAD_DIR`。 由于内存限制,论文中最先进的 SQuAD 结果目前无法在 12GB-16GB GPU 上重现(实际上,即使 batch size 为 1, 使用 `BERT-Large` 似乎也无法放入 12GB GPU)。但是,可以使用这些超参数在 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/ ``` Dev 集预测将保存到 `output_dir` 中名为 `predictions.json` 的文件中: ``` python $SQUAD_DIR/evaluate-v1.1.py $SQUAD_DIR/dev-v1.1.json ./squad/predictions.json ``` 这将产生如下输出: ``` {"f1": 88.41249612335034, "exact_match": 81.2488174077578} ``` 您应该会看到与论文中报告的 `BERT-Base` 的 88.5% 类似的结果。 如果您有权访问 Cloud TPU,则可以使用 `BERT-Large` 进行训练。这是一组超参数(与论文略有不同),始终获得仅在 SQuAD 上训练的约 90.5%-91.0% 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](http://nlp.cs.washington.edu/triviaqa/) 上微调一个 epoch,结果 会更好,但您需要将 TriviaQA 转换为 SQuAD json 格式。 ### SQuAD 2.0 该模型也在 `run_squad.py` 中实现和记录。 要在 SQuAD 2.0 上运行,您首先需要下载数据集。必要的 文件可以在这里找到: * [train-v2.0.json](https://rajpurkar.github.io/SQuAD-explorer/dataset/train-v2.0.json) * [dev-v2.0.json](https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v2.0.json) * [evaluate-v2.0.py](https://worksheets.codalab.org/rest/bundles/0x6b567e1cf2e041ec80d7098f031c5c9e/contents/blob/) 将这些下载到某个目录 `$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/ 的本地目录。初始的 Dev 集预测将位于 ./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 ``` ### 内存不足问题 论文中的所有实验都是在 Cloud TPU 上进行微调的,该 TPU 具有 64GB 设备 RAM。因此,当使用具有 12GB - 16GB RAM 的 GPU 时,如果您使用论文中描述的相同超参数, 很可能会遇到内存不足问题。 影响内存使用的因素包括: * **`max_seq_length`**:发布的模型训练的序列长度 高达 512,但您可以使用较短的最大序列长度进行微调以节省大量内存。这由我们示例代码中的 `max_seq_length` 标志控制。 * **`train_batch_size`**:内存使用量也与 batch size 成正比。 * **模型类型,`BERT-Base` 与 `BERT-Large`**:`BERT-Large` 模型 比 `BERT-Base` 需要多得多的内存。 * **优化器**:BERT 的默认优化器是 Adam,它需要大量额外的内存来存储 `m` 和 `v` 向量。切换到内存效率更高的优化器可以减少内存使用,但也可能影响结果。我们没有尝试其他优化器进行微调。 使用默认的训练脚本(`run_classifier.py` 和 `run_squad.py`),我们 使用 TensorFlow 1.11.0 在单个 Titan X GPU(12GB RAM)上对最大 batch size 进行了基准测试: System | Seq Length | Max Batch Size ------------ | ---------- | -------------- `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` 的这些最大 batch size 太小了, 无论使用的学习率如何,它们实际上都会损害模型的准确性。我们正在向此存储库添加代码,允许在 GPU 上使用更大的有效 batch size。该代码将基于以下一种(或两种)技术: * **梯度累积**:通常,小批量中的样本在梯度计算方面是独立的(不包括此处未使用的批归一化)。这意味着可以在执行权重更新之前累积多个较小批量的梯度,这将完全等同于单个较大的更新。 * [**梯度检查点**](https://github.com/openai/gradient-checkpointing): 在 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 文件(每行输入一行),其中包含由 `layers` 指定的每个 Transformer 层的 BERT 激活(-1 是 Transformer 的最终隐藏层,等等) 请注意,此脚本将产生非常大的输出文件(默认情况下,每个输入标记大约 15kb)。 如果您需要维护原始文本和分词单词之间的对齐(用于 投影训练标签),请参阅下面的 [分词](#tokenization) 部分。 **注意:** 您可能会看到类似 `Could not find trained model in model_dir: /tmp/tmpuB5g5c, running initialization to predict.` 的消息。此消息是预期的,它 只是意味着我们使用的是 `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. **标点符号拆分**:在两侧拆分*所有*标点符号 (即在所有标点符号周围添加空格)。标点符号定义为 那些具有 `P*` Unicode 类别的字符, 任何非字母/数字/空格的 ASCII 字符(例如像 `$` 这样技术上不是标点符号的字符)。例如,`john johanson's, → john johanson ' s ,` 3. **WordPiece 分词**:对上述过程的输出应用空格分词,并分别对每个标记应用 [WordPiece](https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/data_generators/text_encoder.py) 分词。(我们的实现直接基于 `tensor2tensor` 中的实现,该实现已链接)。例如,`john johanson ' s , → john johan ##son ' s ,` 这种方案的优势在于它与大多数现有的 英语分词器“兼容”。例如,假设您有一个词性标注 任务,如下所示: ``` Input: John Johanson 's house Labels: NNP NNP POS NN ``` 分词后的输出将如下所示: 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++ 编写的,并且有一些额外的复杂性),但此代码确实按照论文中的描述生成预训练数据。 以下是运行数据生成的方法。输入是一个纯文本文件,每行一个句子。(对于“下一句预测”任务,这些必须是实际的句子,这很重要)。文档由空行分隔。输出是一组序列化为 `TFRecord` 文件格式的 `tf.train.Example`。 您可以使用现成的 NLP 工具包(如 [spaCy](https://spacy.io/))执行句子分割。`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=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 步或更多。传递给 `run_pretraining.py` 的 `max_seq_length` 和 `max_predictions_per_seq` 参数必须与 `create_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` 文件非常小,此示例训练 将仅在几步内就过度拟合该数据,并产生不切实际的高准确率数字。 ### 预训练提示和注意事项 * **如果您使用自己的词汇表,请确保更改 `bert_config.json` 中的 `vocab_size`。如果您在不更改此设置的情况下使用更大的词汇表,由于未检查的越界访问,您在 GPU 或 TPU 上训练时很可能会得到 NaN。** * 如果您的任务有大量特定领域的语料库可用(例如,“电影 评论”或“科学论文”),从 BERT 检查点开始,在您的语料库上运行额外的预训练步骤可能会很有益。 * 我们在论文中使用的学习率是 1e-4。但是,如果您从现有的 BERT 检查点开始执行额外的预训练步骤,您应该使用较小的学习率(例如,2e-5)。 * 当前的 BERT 模型仅限英语,但我们确实计划在不久的将来发布一个在多种语言上预训练的多语言模型(希望在 2018 年 11 月底之前)。 * 更长的序列成本不成比例地高,因为注意力的计算与序列长度成二次方关系。换句话说,一批 64 个长度为 512 的序列比一批 256 个长度为 128 的序列要昂贵得多。全连接/卷积成本是相同的,但对于 512 长度的序列,注意力成本要高得多。因此,一个好的配方是,例如,使用 128 的序列长度预训练 90,000 步,然后使用 512 的序列长度再预训练 10,000 步。 非常长的序列主要用于学习位置嵌入,这可以学得相当快。请注意,这确实需要使用不同的 `max_seq_length` 值两次生成数据。 * 如果您从头开始预训练,请做好准备,预训练在计算上非常昂贵,尤其是在 GPU 上。如果您从头开始预训练,我们推荐的配方是在单个[可抢占的 Cloud TPU v2](https://cloud.google.com/tpu/docs/pricing)上预训练 `BERT-Base`,大约需要 2 周,费用约为 500 美元(基于 2018 年 10 月的价格)。与论文中使用的相比,仅在单个 Cloud TPU 上训练时,您必须减小 batch size。建议使用适合 TPU 内存的最大 batch size。 ### 预训练数据 我们将**无法**发布论文中使用的预处理数据集。 对于 Wikipedia,建议的预处理是下载[最新转储](https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-pages-articles.xml.bz2),使用 [`WikiExtractor.py`](https://github.com/attardi/wikiextractor) 提取文本,然后进行任何必要的清理以将其转换为纯文本。 不幸的是,收集 [BookCorpus](http://yknzhu.wixsite.com/mbweb) 的研究人员不再将其提供用于公开下载。[Project Guttenberg Dataset](https://web.eecs.umich.edu/~lahiri/gutenberg_dataset.html) 是一个稍小(200M 单词)的旧书集合,属于公共领域。 [Common Crawl](http://commoncrawl.org/) 是另一个非常大的文本集合,但您可能需要进行大量的预处理和清理才能提取出可用于预训练 BERT 的语料库。 ### 学习新的 WordPiece 词汇表 此存储库不包含*学习*新 WordPiece 词汇表的代码。 原因是论文中代码是用 C++ 实现的,依赖于 Google 的内部库。对于英语,几乎总是最好从我们的词汇表和预训练模型开始。对于学习其他语言的词汇表,有许多可用的开源选项。但是,请记住,这些与我们的 `tokenization.py` 库不兼容: * [Google's SentencePiece library](https://github.com/google/sentencepiece) * [tensor2tensor's WordPiece generation script](https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/data_generators/text_encoder_build_subword.py) * [Rico Sennrich's Byte Pair Encoding library](https://github.com/rsennrich/subword-nmt) ## 在 Colab 中使用 BERT 如果您想在 [Colab](
标签:BERT, GLUE, IPv6支持, NLP, NLU, TensorFlow, Transformer, WordPiece, 人工智能, 代码库, 机器学习研究, 模型下载, 模型压缩, 模型微调, 深度学习, 用户模式Hook绕过, 知识蒸馏, 紧凑模型, 自然语言理解, 轻量化模型, 迁移学习, 逆向工具, 预训练模型