发布者:软件工程师Arno Eigenwillig和开发者倡导者Luiz GUStavo Martins
BERT和其他Transformer编码器架构在自然语言处理(NLP)中非常成功,用于计算文本的矢量空间表示,既推动了学术基准的技术水平,也推动了谷歌搜索等大规模应用。BERT自创建以来一直适用于TensorFlow,但最初依赖于非TensorFlow的Python代码来将原始文本转换为模型输入。
今天,我们很高兴地宣布了一个更精简的方法来使用完全在TensorFlow中构建的BERT。这个解决方案使得预训练的编码器和匹配的文本预处理 模型都可以在TensorFlow Hub上使用。TensorFlow中的BERT现在只需几行代码就可以在文本输入上运行。
预处理模型的动画,使您可以轻松地将文本输入到BERT中(如下所述)。
# Load BERT and the preprocessing model from TF Hub.preprocess = hub.load('https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/1')encoder = hub.load('https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/3')# Use BERT on a batch of raw text inputs.input = preprocess(['Batch of inputs', 'TF Hub makes BERT easy!', 'More text.'])pooled_output = encoder(input)["pooled_output"]print(pooled_output)tf.Tensor([[-0.8384154 -0.26902363 -0.3839138 ... -0.3949695 -0.58442086 0.8058556 ] [-0.8223734 -0.2883956 -0.09359277 ... -0.13833837 -0.6251748 0.88950026] [-0.9045408 -0.37877116 -0.7714909 ... -0.5112085 -0.70791864 0.92950743]],shape=(3, 768), dtype=float32)
这些编码器和预处理模型是用TensorFlow模型园的NLP库建立的,并以SavedModel格式导出到TensorFlow Hub。在引擎盖下,预处理使用TF.text库中的TensorFlow操作来完成输入文本的标记化--允许你建立自己的TensorFlow模型,从原始文本输入到预测输出,不需要Python在循环中。这加快了计算速度,删除了模板代码,不容易出错,并实现了完整的文本到输出模型的序列化,使BERT更容易在生产中服务。
为了更详细地展示这些模型如何帮助你,我们已经发布了两个新的教程。
- 初学者教程解决的是一个情感分析任务,不需要任何特殊的定制就能达到很好的模型质量。这是使用BERT和预处理模型的最简单方法。
- 高级教程解决了GLUE基准中的NLP分类任务,在TPU上运行。它还展示了在你需要多段输入的情况下如何使用预处理模型。
选择一个BERT模型
BERT模型在大型文本语料库(例如,维基百科文章档案)上进行预训练,使用自我监督的任务,如从周围环境中预测一个句子中的单词。这种类型的训练允许模型学习文本语义的强大表示,而不需要标记数据。然而,它也需要大量的计算来进行训练--在16个TPU上需要4天时间(正如2018年BERT论文中所报告的那样)。幸运的是,在这种昂贵的预训练完成一次后,我们可以在许多不同的任务中有效地重复使用这种丰富的表示。
TensorFlow Hub提供了各种BERT和类似BERT的模型。
- 八个BERT模型带有原BERT作者发布的训练好的权重。
- 24个小型BERT具有相同的一般结构,但更少和/或更小的Transformer块,这让你可以探索速度、尺寸和质量之间的权衡。
- ALBERT:这些是四种不同规模的 "A Lite BERT",通过在各层之间共享参数来减少模型的大小(但不是计算时间)。
- 8个BERT专家都具有相同的BERT架构和规模,但提供了不同的预训练领域和中间微调任务的选择,以便与目标任务更紧密地结合。
- Electra具有与BERT相同的架构(有三种不同的尺寸),但在类似生成对抗网络(GAN)的设置中被预先训练为一个判别器。
- BERT与Talking-Heads Attention和Gated GELU[base,large]对Transformer架构的核心有两项改进。
- Lambert已经用LAMB优化器和RoBERTa的几种技术进行了训练。
- MuRIL是印度语言的多语言表述,在17种印度语言(包括英语)和它们的音译对应物上进行了预训练。
- MobileBERT(英文,多语种),是一个瘦身版的BERT,通过对维基百科上的教师BERT模型进行提炼训练,BooksCorpus。
- ......以及更多即将到来的。
这些模型是BERT编码器。上面的链接会带你到它们在TF Hub上的文档,其中提到了与每个模型一起使用的正确预处理模型。
我们鼓励开发者访问这些模型页面,以了解每个模型所针对的不同应用。由于它们的通用界面,通过改变编码器模型及其预处理的URL,可以很容易地在你的特定任务上进行实验和比较不同编码器的性能。
预处理模型
对于每个BERT编码器,都有一个匹配的预处理模型。它使用TF.text库提供的TensorFlow操作,将原始文本转换为编码器所期望的数字输入张量。与纯Python的预处理不同,这些操作可以成为TensorFlow模型的一部分,直接从文本输入中服务。TF Hub的每个预处理模型都已经配置了一个词汇表及其相关的文本规范化逻辑,不需要进一步设置。
我们已经看到了上面使用预处理模型的最简单方式。让我们再仔细看看。
preprocess = hub.load('https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/1')input = preprocess(["This is an amazing movie!"]) {'input_word_ids': <tf.Tensor: shape=(1, 128), dtype=int32, numpy= array([[ 101, 2023, 2003, 2019, 6429, 3185, 999, 102, 0, ...]])>, 'input_mask': <tf.Tensor: shape=(1, 128), dtype=int32, numpy= array([[ 1, 1, 1, 1, 1, 1, 1, 1, 0, ...,]])>, 'input_type_ids': <tf.Tensor: shape=(1, 128), dtype=int32, numpy= array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, ...,]])>}
像这样调用preprocess()
,将原始文本输入转化为BERT编码器的固定长度的输入序列。你可以看到,它由一个张量input_word_ids组成,其中包含每个标记化输入的数字id,包括开始、结束和填充标记,加上两个辅助张量:input_mask(用于区分非填充标记和填充标记)和每个标记的input_type_ids(可以区分每个输入的多个文本段,我们将在下面讨论这个)。
同样的预处理SavedModel还提供了第二个更精细的API,它支持将一个或两个不同的文本段放入编码器的一个输入序列。我们来看看一个句子的包含任务,其中BERT被用来预测一个前提是否包含一个假设。
text_premises = ["The fox jumped over the lazy dog.", "Good day."]tokenized_premises = preprocess.tokenize(text_premises) <tf.RaggedTensor [[[1996], [4419], [5598], [2058], [1996], [13971], [3899], [1012]], [[2204], [2154], [1012]]]> text_hypotheses = ["The dog was lazy.", # Entailed. "Axe handle!"] # Not entailed.tokenized_hypotheses = preprocess.tokenize(text_hypotheses) <tf.RaggedTensor [[[1996], [3899], [2001], [13971], [1012]], [[12946], [5047], [999]]]>
每次标记化的结果是一个数字标记ID的RaggedTensor,完整地代表了每个文本输入。如果某些前提和假设对太长,不适合下一步的BERT输入的seq_length,你可以在这里做额外的预处理,如修剪文本段或将其分割成多个编码器输入。
然后,标记化的输入被打包成一个固定长度的输入序列,用于BERT编码器。
encoder_inputs = preprocess.bert_pack_inputs( [tokenized_premises, tokenized_hypotheses], seq_length=18) # Optional argument, defaults to 128. {'input_word_ids': <tf.Tensor: shape=(2, 18), dtype=int32, numpy= array([[ 101, 1996, 4419, 5598, 2058, 1996, 13971, 3899, 1012, 102, 1996, 3899, 2001, 13971, 1012, 102, 0, 0], [ 101, 2204, 2154, 1012, 102, 12946, 5047, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0]])>, 'input_mask': <tf.Tensor: shape=(2, 18), dtype=int32, numpy= array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]])>, 'input_type_ids': <tf.Tensor: shape=(2, 18), dtype=int32, numpy= array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]])>}
打包的结果是已经很熟悉的输入字数(input_word_ids)、输入掩码(input_mask)和输入类型(input_type_ids)的dict(对于第一个和第二个输入,它们分别为0和1)。所有输出都有一个共同的seq_length(默认为128)。会超过seq_length的输入在打包时被截断成大致相等的大小。
加速模型训练
TensorFlow Hub提供BERT编码器和预处理模型作为独立的部分,以实现加速训练,特别是在TPU上。
张量处理单元(TPU)是谷歌定制开发的加速器硬件,擅长于大规模的机器学习计算,如那些需要微调BERT的计算。TPU对密集的张量进行操作,并期望像字符串这样的可变长度数据已经被主机CPU转化为固定大小的张量。
BERT编码器模型和其相关的预处理模型之间的分割使得编码器的微调计算可以作为模型训练的一部分分配给TPU,而预处理模型在主机CPU上执行。预处理计算可以在数据集上异步运行,使用tf.data.Dataset.map()
,其密集的输出可以被TPU上的编码器模型所消耗。像这样的异步预处理也可以提高其他加速器的性能。
我们的高级BERT教程可以在使用TPU工作者的Colab运行时间中运行,并演示了这种端到端的情况。
总结
在TensorFlow中使用BERT和类似的模型已经变得更简单了。TensorFlow Hub提供了大量预训练的BERT编码器和文本预处理模型,只需几行代码就可以轻松使用。
看看我们的交互式初级和高级教程,了解更多关于如何使用模型进行句子和句子对分类。让我们知道你用这些新的BERT模型建立了什么,并在你的帖子上标注#TFHub。
鸣谢。
我们要感谢一些同事对这项工作的贡献。
新的预处理模型是与陈晨、Terry Huang、Mark Omernick和Rajagopal Ananthanarayanan合作创建的。
额外的BERT模型已经由Sebastian Ebert(小型BERT)、Le Hou和Hongkun Yu(Lambert、Talking Heads)此次发布到TF Hub。
Mark Daoust、Josh Gordon和Elizabeth Kemp大大改进了本帖和相关教程中的材料表述。Tom Small提供了精美的BERT动画。