大数据文摘出品
来源:Google Colab
编译:武帅、曹培信
2018年10月,Google AI团队推出了Bert,可以说Bert一出生就自带光环。
在斯坦福大学机器阅读理解水平测试SQuAD1.1中,Bert在全部两个衡量指标上,全面超越人类表现。并且在另外11种不同NLP测试中均创造了历史以来最好成绩,将GLUE基准提升7.6%,将MultiNLI的准确率提提升5.6%。
然而这个拥有12层神经网络的“多头怪”(这里指BERT-Base,BERT-Large有24层),在4个 Cloud TPU 上需要训练 4 天(BERT-Large需要16个Cloud TPU),如此高的训练成本让许多想尝试的同学望而却步。
不过,谷歌也给广大程序员带来了福音!我们可以借助谷歌云TPU训练Bert了!并且只需要花费1美元,在Google Colab上还出了完整的教程!
快跟文摘菌一起薅谷歌羊毛!
在本次实验中,我们将借助谷歌云,在任意文本数据上预训练当下最先进的NLP模型—BERT。
BERT模型起源:
https://arxiv.org/abs/1810.04805?source=post_page
本指南包含了模型预训练的所有阶段,包括:
搭建训练环境
下载原始文本数据
文本数据预处理
学习新词汇表
切分预训练数据
将数据和模型存储到谷歌云
在云TPU上训练模型
先回答几个问题
这份指南有什么用?
借助本指南,你可以在任意文本数据上训练BERT模型。特别是当开源社区没有你需要的语言或示例的预训练模型时,它会帮助到你。
谁需要这份指南?
这份指南适用于对BERT感兴趣但对当前可用的开源模型的性能并不满意的NLP研究人员。
我该如何开始?
要想长时间地保存训练数据和模型,你需要一个谷歌云端存储分区(Google Cloud Storage Bucket,GCSB)。请按照这份谷歌云TPU快速入门指南创建一个谷歌云平台账户和谷歌云存储分区。
谷歌云TPU快速入门指南:
https://cloud.google.com/tpu/docs/quickstart?source=post_page
每一个谷歌云的新用户都可获得300美元的免费金额。
链接:
https://cloud.google.com/free/?source=post_page
本教程的1到5步出于演示目的,在没有谷歌云存储的情况下也能进行。但是,在这种情况下,你将无法训练模型。
需要什么?
在第二代TPU(TPUv2)上预训练一个BERT模型大约需要54小时。Google Colab并不是为执行此类需要长时间运行的任务而设计的,它每隔8小时便会中断训练过程。因此,为了训练过程不被中断,你需要使用付费的抢占式的TPUv2。
译者注:Google Colab,谷歌免费提供的用于机器学习的平台
相关链接:
https://www.jianshu.com/p/000d2a9d36a0
译者注:抢占式,一种进程调度方式,允许将逻辑上可继续运行的进程暂停,适合通用系统。
相关链接:
https://blog.csdn.net/qq_34173549/article/details/79936219
也就是说,在撰写本文时(2019年5月9日),通过Google Colab提供的一块TPU,花费大约1美元,就可以在谷歌云上存储所需要的数据和模型,并预训练一个BERT模型。
我该如何遵循指南?
下面的代码是Python和Bash的组合。它在Colab Jupyter环境中运行。因此,它可以很方便地在那里运行。
然而,除了实际的模型训练部分之外,本指南列出的其他步骤都可以在单独的机器上运行。特别是当你的数据集过大或者十分私密而无法在Colab环境中进行预处理时,这就显得十分有用了。
好的,给我看看代码
代码链接:
https://colab.research.google.com/drive/1nVn6AFpQSzXBt8_ywfx6XR8ZfQXlKGAz?source=post_page#scrollTo=ODimOhBR05yR
我需要修改代码吗?
代码中唯一需要你修改的地方就是谷歌云存储的账户名。其他的地方默认就好。
还有别的么?
说句题外话,除了这个程序,我还发布了一个训练好的俄罗斯语BERT模型。
下载链接:
https://storage.googleapis.com/bert_resourses/russian_uncased_L-12_H-768_A-12.zip?source=post_page
我希望相关研究人员可以发布其他语言的预训练模型。这样就可以改善我们每个人的NLP环境。现在,让我们进入正题吧!
第1步:搭建训练环境
首先,我们导入需要用到的包。
在Jupyter Notebook中可以通过使用一个感叹号‘!’直接执行bash命令。如下所示:
!pip install sentencepiece
整个演示过程我将会用同样的方法使用几个bash命令。
现在,让我们导入包并在谷歌云中自行授权。
import os
搭建BERT训练环境
第2步:获取数据
接下来我们获取文本数据语料库。这次实验我们将采用OpenSubtitles 数据集。
链接:
https://www.opensubtitles.org/en/?source=post_page
该数据集有65种语言可以使用。
链接:
http://opus.nlpl.eu/OpenSubtitles-v2016.php?source=post_page
与更常用的文本数据集(如维基百科)不同,该数据集并不需要进行任何复杂的数据预处理。它也预先格式化了,每行一个句子,便于后续处理。
你也可以通过设置相应的语言代码来使用该数据集。
AVAILABLE = {'af','ar','bg','bn','br','bs','ca','cs',
下载OPUS数据
出于演示目的,我们默认只使用语料库的一小部分。
在实际训练模型时,请务必取消选中DEMO_MODE复选框以使用大100倍的数据集。
请放心,一亿行语句足以训练出一个相当不错的BERT模型。
DEMO_MODE = True #@param {type:"boolean"}
拆分数据集
第3步:文本预处理
我们下载的原始文本数据包含了标点符号,大写字母以及非UTF编码的符号,这些都需要提前删除。在模型推断时,我们也需要对新数据集采取同样的做法。
如果你的用例需要不同的预处理方法(例如在模型推断时大写字母或者标点符号是需要保留的),那么就修改代码中的函数以满足你的需求。
regex_tokenizer = nltk.RegexpTokenizer("\w+")
定义预处理例程
现在让我们对整个数据集进行预处理吧。
RAW_DATA_FPATH = "dataset.txt" #@param {type: "string"}
应用预处理
第4步:构建词汇表
下一步,我们将学习一个新的词汇表,用于表示我们的数据集。
因为 BERT 论文中使用了谷歌内部未开源的 WordPiece 分词器,因此,这里我们只能使用一元文法模式(unigram mode)下开源的 SentencePiece 分词器了。
链接:
https://github.com/google/sentencepiece?source=post_page
虽然它与BERT并不直接兼容,但我们可以通过一个小技巧让它工作。
SentencePiece需要相当多的运行内存(RAM),因此在Colab上运行整个数据集会导致内核崩溃。为避免这一情况发生,我们将随机地对数据集的一小部分进行子采样,从而构建词汇表。当然,也可以使用运行内存更大的计算机来执行此步骤。这完全取决于你。
此外,SentencePiece默认将BOS和EOS控制符号添加到词汇表中。我们可以通过手动地把它们的词id设为-1来禁用它们。
VOC_SIZE的典型值介于32000到128000之间。我们将保留NUM_PLACEHOLDERS标记,以防有人想在训练前的阶段完成后更新词汇和微调模型。
MODEL_PREFIX = "tokenizer" #@param {type: "string"}
学习SentencePiece词汇表
现在,看看我们是如何使SentencePiece在BERT模型中工作的。
下面是官方仓库的一个英语BERT预训练模型中通过WordPiece词汇表标记后的语句。
链接:
https://github.com/google-research/bert?source=post_page
模型下载:
https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-12_H-768_A-12.zip?source=post_page
>>> wordpiece.tokenize("Colorless geothermal substations are generating furiously")
我们看到,WordPiece分词器在两个单词中间以“##”在前的形式预设了子词。单词开头的子词并没有发生变化。如果子词出现在开头和单词的中间,则两个版本(带或不带‘##’)都会添加到词汇表中。
SentencePiece创建了两个文件:tokenizer.model and tokenizer.vocab。让我们来看看学到的词汇:
def read_sentencepiece_vocab(filepath):
读取学习后的SentencePiece词汇表
给出结果:
Learnt vocab size: 31743
我们观察到,SentencePiece和WordPiece给出的结果完全相反。从这篇文档中可以看出SentencePiece首先用元符号“_”(UnicodeMath编码字符:U+2581)替换掉空格,如 “Hello World”被替换为成:
Hello▁World.
链接:
https://github.com/google/sentencepiece/blob/master/README.md?source=post_page
然后,此文本被分割为小块,如下所示:
[Hello] [▁Wor] [ld] [.]
在空格之后出现的子词(也是大多数单词的开头)通常前面加上了 ‘_’,而其它的并没有变化。这不包括那些仅出现在句子开头而不是其他地方的子词。然而,这些情况很少发生。
因此,为了获得类似于WordPiece的词汇表,我们需要进行一个简单的转换,将那些‘_’符号删除并将‘##’符号添加到不含它的标记中。
我们还添加了一些BERT架构所需要的特殊控制符号。按照惯例,我们把它们放在了词汇表的开头。
此外,我们也在词汇表中添加了一些占位符标记。
如果某人希望用新的特定任务的标记来更新模型时,以上那些做法就十分有用了。此时,将原先的占位符标记替换为新的标记,预训练数据就会重新生成,模型也会在新数据上进行微调。
def parse_sentencepiece_token(token):
转换词汇表以用于BERT
最后,我们将获得的词汇表写入文件。
VOC_FNAME = "vocab.txt" #@param {type:"string"}
将词汇表写入文件
现在,让我们看看新词汇表在实践中是如何运作的:
>>> testcase = "Colorless geothermal substations are generating furiously"
第5步:生成预训练数据
借助于手头的词汇表,我们已经可以生成BERT模型的预训练数据了。因为我们的数据集可能非常大,所以我们将其切分:
mkdir ./shards
切分数据集
现在,对于每个切片,我们需要从BERT仓库中调用create_pretraining_data.py 脚本。为此,我们使用xargs命令。
在我们开始生成数据前,我们需要设置一些参数并传递给脚本。你可以在README文档中找到有关它们含义的更多信息。
链接:
https://github.com/google-research/bert/blob/master/README.md?source=post_page
MAX_SEQ_LENGTH = 128 #@param {type:"integer"}
定义预训练数据的参数
运行这一步可能需要相当长的时间,具体取决于数据集的大小。
XARGS_CMD = ("ls ./shards/ | "
创建预训练数据
第6步:创建持久化存储
为了保存我们来之不易的财产,我们将其保存在谷歌云存储上。如果你已经创建了谷歌云存储分区,那么这是很容易实现的。
我们将在谷歌云存储上设置两个目录:一个用于数据,一个用于模型。在模型目录下,我们将放置模型词汇表和配置文件。
在继续操作之前,请在此配置你的BUCKET_NAME变量,否则你将无法训练模型。
BUCKET_NAME = "bert_resourses" #@param {type:"string"}
配置GCS bucket名称
下面是BERT的超参数配置示例。若更改将自担风险!
# use this for BERT-base
配置BERT的超参数并保存到磁盘
现在,我们将我们的成果存放到谷歌云存储。
if BUCKET_NAME:
上传成果到谷歌云存储
第7步:训练模型
我们已准备好开始训练我们的模型了。
建议过去步骤中的一些参数不要更改,以便于快速重启训练过程。
确保整个实验所设置的参数完全相同。
BUCKET_NAME = "bert_resourses" #@param {type:"string"}
配置训练过程
准备训练运行配置,建立评估器和输入函数,启动BERT。
model_fn = model_fn_builder(
建立评估模型和输入函数
启动!
estimator.train(input_fn=train_input_fn, max_steps=TRAIN_STEPS)
启动BERT
使用默认参数训练模型100万步大约需要53个小时。如果内核出于某种原因重新启动,你可以从最新的检查点继续训练。
以上就是在云TPU上从头开始预训练BERT模型的指南。
还能做点什么?
我们已经训练好了模型,那接下来呢?
这是一个全新的话题。你可以做如下几件事:
将预训练模型作为通用NLU模块
针对某些特定的分类任务微调模型
使用BERT作为构件块创建另一个深度学习模型
链接:
https://towardsdatascience.com/building-a-search-engine-with-bert-and-tensorflow-c6fdc0186c8a
真正有趣的东西还在后面,所以睁大你的眼睛。同时,看看bear-as-a-service这个很棒的项目,将你刚训好的模型部署到线上去吧!
项目链接:
https://github.com/hanxiao/bert-as-service?source=post_page
最最重要的,不断学习!