【Transformers】第 3 章:自动编码语言模型

   大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流

个人主页-Sonhhxg_柒的博客_CSDN博客 

欢迎各位→点赞 + 收藏⭐️ + 留言​

系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟

文章目录

技术要求

BERT——自动编码语言模型之一

BERT 语言模型预训练任务

更深入地了解 BERT 语言模型

任何语言的自动编码语言模型训练

与社区共享模型

了解其他自动编码模型

介绍 ALBERT

RoBERTa

ELECTRA

使用标记化算法

字节对编码

WordPiece 标记化

句子标记化

分词器库

培训 BPE

训练 WordPiece 模型

概括


在上一章中,我们查看并研究了 HuggingFace 的 Transformer 如何使用典型的 Transformer 模型。到目前为止,所有主题都包括如何使用预定义或预构建的模型,而关于特定模型及其训练的信息较少。

在本章中,我们将了解如何从头开始在任何给定语言上训练自动编码语言模型。该培训将包括模型的预培训和特定任务的培训。首先,我们将从有关 BERT 模型及其工作原理的基本知识开始。然后我们将使用一个简单的小型语料库来训练语言模型。之后,我们将看看如何在任何 Keras 模型中使用该模型。

对于本章将要学习的内容的概述,我们将讨论以下主题:

  • BERT——自动编码语言模型之一
  • 任何语言的自动编码语言模型训练
  • 与社区共享模型
  • 了解其他自动编码模型
  • 使用标记化算法

技术要求

本章的技术要求如下:

  • Anaconda
  • Transformers >= 4.0.0
  • PyTorch >= 1.0.2
  • TensorFlow >= 2.4.0
  • Datasets >= 1.4.1
  • Tokenizers

BERT——自动编码语言模型之一

Transformers 的双向编码器表示,也称为BERT,是最早的编码器之一自动编码语言模型以利用编码器 Transformer 堆栈,对语言建模稍作修改。

BERT 架构是基于 Transformer 原始实现的多层 Transformer 编码器。Transformer 模型本身最初是用于机器翻译任务的,但 BERT 所做的主要改进是利用这部分架构来提供更好的语言建模。这种语言模型经过预训练后,能够提供对其所训练语言的全局理解。

BERT 语言模型预训练任务

要有一个清晰的理解了 BERT 使用的掩码语言建模,让我们更详细地定义它。掩蔽语言建模是在输入(带有一些掩码标记的句子)上训练模型并获得作为填充了掩码标记的整个句子的输出的任务。但它如何以及为什么帮助模型在分类等下游任务上获得更好的结果?答案很简单:如果模型可以做完形填空测试(通过填空来评估语言理解的语言测试),那么它对语言本身有一个大致的理解。对于其他任务,它已经过预训练(通过语言建模)并且会表现得更好。

这是一个完形填空测试的例子:

George Washington was the first President of the ___ States.

预计曼联应该填补空白。对于掩码语言模型,应用相同的任务,并且需要填写掩码标记。但是,掩码标记是从句子中随机选择的。

其他BERT 的任务训练的是Next Sentence Prediction ( NSP )。这个预训练任务确保 BERT 在预测掩码时不仅可以学习所有标记之间的关系,还可以帮助它理解两个句子之间的关系。选择一对句子并将其提供给 BERT,其间带有[SEP]拆分器标记。从数据集中还可以知道第二个句子是否在第一个句子之后。

以下是 NSP 的示例:

It is required from reader to fill the blank. Bitcoin price is way over too high compared to other altcoins.

在此示例中,模型需要将其预测为否定(句子彼此不相关)。

这两个预训练任务使 BERT 能够理解语言本身。BERT 令牌嵌入为每个令牌提供上下文嵌入。上下文嵌入手段每个令牌都有一个与周围令牌完全相关的嵌入。与 Word2Vec 和此类模型不同,BERT 为每个令牌嵌入提供了更好的信息。另一方面,NSP 任务使 BERT 能够更好地嵌入[CLS]令牌。正如第一章所讨论的,这个标记提供了关于整个输入的信息。[CLS]用于分类任务,在预训练部分学习整个输入的整体嵌入。下图展示了 BERT 模型的整体情况。图 3.1展示了 BERT 模型各自的输入和输出:

【Transformers】第 3 章:自动编码语言模型_第1张图片

图 3.1 – BERT 模型

让我们继续下一节吧!

更深入地了解 BERT 语言模型

分词器是最许多 NLP 应用程序在其各自管道中的重要部分。对于 BERT,使用 WordPiece 标记化。一般来说,WordPieceSentencePieceBytePairEncoding ( BPE ) 是最广泛使用的三个已知的标记器,由不同的基于 Transformer 的架构使用,它们也包含在下一节。BERT 或任何其他基于 Transformer 的架构使用子词标记化的主要原因是此类标记器处理未知标记的能力。

BERT 还使用位置编码以确保将标记的位置提供给模型。如果您还记得第 1 章,从词袋到变形金刚,BERT 和类似模型使用非序列操作,例如密集神经层。传统模型(例如基于 LSTM 和 RNN 的模型)通过序列中标记的顺序来获取位置。为了向 BERT 提供这些额外信息,位置编码就派上用场了。

BERT 的预训练(例如自动编码模型)为模型提供了语言方面的信息,但实际上,在处理序列分类、令牌分类或问答等不同问题时,会使用模型输出的不同部分。

例如,在序列分类任务的情况下,例如情感分析或句子分类,原始 BERT 文章提出必须使用来自最后一层的[CLS]嵌入。然而,还有其他研究使用 BERT 使用不同的技术执行分类(使用来自所有标记的平均标记嵌入,在最后一层部署 LSTM,甚至在最后一层之上使用 CNN)。最后[CLS]任何分类器都可以使用用于序列分类的嵌入,但所提出的也是最常见的一种是密集层,其输入大小等于最终的令牌嵌入大小,输出大小等于具有 softmax 激活的类的数量功能。当输出可能是多标签并且问题本身是多标签分类问题时,使用 sigmoid 也是另一种选择。

为你带来有关 BERT 实际工作原理的更多详细信息,下图显示了 NSP 任务的示例。请注意,这里简化了标记化以便更好地理解:

【Transformers】第 3 章:自动编码语言模型_第2张图片

图 3.2 – NSP 任务的 BERT 示例

这BERT 模型有不同的变体,有不同的设置。例如,输入的大小是可变的。在前面的示例中,它设置为512,模型可以作为输入获得的最大序列大小为512。但是,此大小包括特殊标记[CLS][SEP],因此将减少到510。另一方面,使用 WordPiece 作为分词器会产生子词令牌,并且在我们可以有更少的词之前的序列大小,而在分词之后,大小会增加,因为如果词在预训练语料库中不常见,分词器会将它们分解成子词.

下图展示了针对不同任务的 BERT。对于 NER 任务,使用每个标记的输出而不是[CLS]。在问答的情况下,问题和答案使用[SEP]分隔符标记连接,答案使用Start/End和最后一层的Span输出进行注释。在这种情况下,段落是询问问题的上下文:

【Transformers】第 3 章:自动编码语言模型_第3张图片

图 3.3 – 各种 NLP 任务的 BERT 模型

不管所有在这些任务中,BERT 最重要的能力是它对文本的上下文表示。它在各种任务中取得成功的原因是因为 Transformer 编码器架构以密集向量的形式表示输入。这些向量可以通过非常简单的分类器轻松转换为输出。

到目前为止,您已经了解了 BERT 及其工作原理。您已经了解了有关 BERT 可用于各种任务的详细信息以及该架构的要点。

在下一节中,您将学习如何预训练 BERT 并在训练后使用它。

任何语言的自动编码语言模型训练

我们有讨论了 BERT 的工作原理以及可以使用 HuggingFace 存储库提供的预训练版本。在本节中,您将学习如何使用 HuggingFace 库来训练您自己的 BERT。

在开始之前,必须拥有良好的训练数据,这些数据将用于语言建模。该数据称为语料库,通常是一大堆数据(有时是经过预处理和清理的)。这个未标记的语料库必须适合您希望训练语言模型的用例;例如,如果你想为英语使用特殊的 BERT。尽管有大量巨大的、良好的数据集,例如作为 Common Crawl ( Common Crawl ),我们更喜欢一个小的以便更快的训练。

50K 电影评论的 IMDB 数据集(可在IMDB Dataset of 50K Movie Reviews | Kaggle获得)是一个用于情绪分析的大型数据集,但如果您将其用作训练语言模型的语料库,则规模很小:

1.您可以轻松使用以下代码下载并保存为.txt格式,用于语言模型和分词器训练:

import pandas as pd

imdb_df = pd.read_csv("IMDB Dataset.csv")
reviews = imdb_df.review.to_string(index=None)
with open("corpus.txt", "w") as f:
      f.writelines(reviews)

2.准备好语料库后,必须训练分词器。分词器库为 WordPiece 分词器提供快速简便的培训。为了在您的语料库上训练它,需要运行以下代码:

from tokenizers import BertWordPieceTokenizer

bert_wordpiece_tokenizer =BertWordPieceTokenizer()
bert_wordpiece_tokenizer.train("corpus.txt")

3.这将训练标记器。您可以使用经过训练的标记器对象的get_vocab()函数访问经过训练的词汇表。您可以使用以下代码获取词汇表:

bert_wordpiece_tokenizer.get_vocab()

以下是输出:

{'almod': 9111, 'events': 3710, 'bogart': 7647, 'slapstick': 9541, 'terrorist': 16811, 'patter': 9269, '183': 16482, '##cul': 14292 , 'sophie': 13109, 'thinki': 10265, '玷污': 16310, '##outh': 14729, 'peckinpah': 17156, 'gw': 6157, '##cat': 14290, '## eing':14256,'成功':12747,'roomm':7363,'stalwart':13347,...}

4.必须保存标记器以供以后使用。使用对象的save_model()函数并提供目录将保存分词器词汇表以供进一步使用:

bert_wordpiece_tokenizer.save_model("tokenizer")

5.你可以使用from_file()函数重新加载它:

tokenizer = BertWordPieceTokenizer.from_file("tokenizer/vocab.txt")

6.您可以按照以下示例使用标记器:

tokenized_sentence = tokenizer.encode("Oh it works just fine")
tokenized_sentence.tokens

['[CLS]', 'oh', 'it', 'works', 'just', 'fine','[SEP]']

特殊标记[CLS][SEP]将自动添加到标记列表中,因为 BERT 需要它们来处理输入。

7.让我们使用我们的分词器尝试另一个句子:

tokenized_sentence = tokenizer.encode("ohoh i thougt it might be workingg well")

['[CLS]', 'oh', '##o', '##h', 'i', 'thoug', '##t', 'it', 'might', 'be', 'working', '##g', 'well', '[SEP]']

8.对于嘈杂和拼写错误的文本,这似乎是一个很好的标记器。现在您已经准备好并保存了标记器,您可以训练自己的 BERT。第一步是使用Transformers库中的BertTokenizerFast 。您需要使用以下命令从上一步加载经过训练的标记器:

from Transformers import BertTokenizerFast

tokenizer =  BertTokenizerFast.from_pretrained("tokenizer")

我们使用了 BertTokenizerFast,因为它是 HuggingFace 建议的文档。还有BertTokenizer,根据库文档中的定义,它的实现速度不如快速版本。在大多数预训练模型的文档和卡片中,强烈建议使用BertTokenizerFast版本。

9.下一步是使用以下命令准备语料库以进行更快的训练:

from Transformers import LineByLineTextDataset

dataset = LineByLineTextDataset(tokenizer=tokenizer,
                                file_path="corpus.txt",
                                block_size=128)

10.并且需要为掩码语言建模提供数据整理器:

from Transformers import DataCollatorForLanguageModeling

data_collator = DataCollatorForLanguageModeling(
                      tokenizer=tokenizer,
                      mlm=True,
                      mlm_probability=0.15)

数据整理者获取数据并为训练做准备。例如,上面的数据整理器以0.15的概率获取数据并准备用于掩码语言建模。使用这种机制的目的是动态进行预处理,从而可以使用更少的资源。另一方面,它减慢了训练过程,因为每个样本都必须在训练时进行动态预处理。

11.培训参数还为训练阶段的训练器提供信息,可以使用以下命令设置:

from Transformers import TrainingArguments

training_args = TrainingArguments(
                      output_dir="BERT",
                      overwrite_output_dir=True,
                      num_train_epochs=1,
                      per_device_train_batch_size=128)

12.我们现在将制作 BERT 模型本身,我们将使用默认配置(注意头的数量、Transformer 编码器层等):

from Transformers import BertConfig, BertForMaskedLM

bert = BertForMaskedLM(BertConfig())

13.最后一步是制作一个 trainer 对象:

from Transformers import Trainer

trainer = Trainer(model=bert,
                      args=training_args,
                      data_collator=data_collator,
                      train_dataset=dataset)

14.最后,您可以使.用以下命令训练您的语言模型:

trainer.save_model("MyBERT")

它将向您显示一个进度条,指示在培训中取得的进展:

图 3.4 – BERT 模型训练进度

在模型训练过程中,将使用一个名为runs的日志目录来分步存储检查点:

 

图 3.5 – BERT 模型检查点

15.之后训练完成后,您可以使用以下命令轻松保存模型:

trainer.save_model("MyBERT")

到目前为止,您已经了解了如何从头开始为您想要的任何特定语言训练 BERT。您已经学习了如何使用您准备的语料库来训练分词器和 BERT 模型。

16.您为 BERT 提供的默认配置是此训练过程中最重要的部分,它定义了 BERT 的架构及其超参数。您可以使用以下代码查看这些参数:

from Transformers import BertConfig

BertConfig()

以下是输出:

【Transformers】第 3 章:自动编码语言模型_第4张图片

图 3.6 – BERT 模型配置

如果你想从原始 BERT 配置 ( https://github.com/google-research/bert )复制TinyMiniSmallBase和相关模型,您可以更改这些设置:

【Transformers】第 3 章:自动编码语言模型_第5张图片

图 3.7 – BERT 模型配置 (https://github.com/google-research/bert)

请注意,更改这些参数,尤其是max_position_embeddingnum_attention_headsnum_hidden_​​layersintermediate_sizehidden_​​size,会直接影响训练时间。增加它们会显着增加大型语料库的训练时间。

17.例如,你可以使用以下代码轻松地为微型版本的 BERT 进行新配置,以加快训练速度:

tiny_bert_config = BertConfig(max_position_embeddings=512, hidden_size=128,
           num_attention_heads=2,
           num_hidden_layers=2,
           intermediate_size=512)

tiny_bert_config

以下是代码的结果:

【Transformers】第 3 章:自动编码语言模型_第6张图片

图 3.8 – Tiny BERT 模型配置

18.通过使用相同的方法,我们可以使用以下配置制作一个微型 BERT 模型:

tiny_bert = BertForMaskedLM(tiny_bert_config)

19.并使用相同的训练参数,你可以训练这个小小的新 BERT:

trainer = Trainer(model=tiny_bert, args=training_args,
                     data_collator=data_collator,
                     train_dataset=dataset)

trainer.train()

以下是输出:

图 3.9 – Tiny BERT 模型配置

很明显,训练时间显着减少,但你应该知道,这是 BERT 的一个微小版本,层数和参数较少,不如 BERT Base。

到目前为止,您已经学会了如何从头开始训练自己的模型,但必须注意的是,在处理用于训练语言模型的数据集或利用它来执行特定任务的训练时,使用数据集库是更好的选择。

20.BERT 语言模型也可以作为嵌入层与任何深度学习模型结合使用。例如,您可以加载任何预训练的 BERT 模型或您自己在上一步中训练的版本。以下代码显示了必须如何加载它才能在 Keras 模型中使用:

from Transformers import TFBertModel, BertTokenizerFast

bert = TFBertModel.from_pretrained("bert-base-uncased")

tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased")

21但是您不需要整个模型;相反,您可以使用以下代码访问图层:

bert.layers

[]

22.如您所见,有只是 TFBertMainLayer 的一个层您可以在 Keras 模型中访问它。但在使用它之前,最好先对其进行测试,看看它提供了什么样的输出:

tokenized_text = tokenizer.batch_encode_plus(
                   ["hello how is it going with you",
                   "lets test it"],
                    return_tensors="tf",
                    max_length=256,
                    truncation=True,
                    pad_to_max_length=True)

bert(tokenized_text)

输出如下:

【Transformers】第 3 章:自动编码语言模型_第7张图片

图 3.10 – BERT 模型输出

可以看出从结果来看,有两个输出:一个用于最后一个隐藏状态,一个用于池化输出。最后一个隐藏状态为来自 BERT 的所有令牌嵌入提供了分别在开始和结束处附加的[CLS][SEP]令牌。

23.现在您已经了解了有关 BERT 的 TensorFlow 版本的更多信息,您可以使用这个新的嵌入来制作 Keras 模型:

from tensorflow import keras
import tensorflow as tf

max_length = 256
tokens = keras.layers.Input(shape=(max_length,),
                           dtype=tf.dtypes.int32)

masks = keras.layers.Input(shape=(max_length,),
                          dtype=tf.dtypes.int32)

embedding_layer = bert.layers[0]([tokens,masks])[0][:,0,:]

dense = tf.keras.layers.Dense(units=2,
        activation="softmax")(embedding_layer)

model = keras.Model([tokens,masks],dense)

24.模型对象,它是一个 Keras 模型,有两个输入:一个用于令牌,一个用于掩码。标记具有来自标记器输出的token_ids并且掩码将具有attention_mask。让我们尝试一下,看看会发生什么:

tokenized = tokenizer.batch_encode_plus(
["hello how is it going with you",
"hello how is it going with you"],
return_tensors="tf",
max_length= max_length,
truncation=True,
pad_to_max_length=True)

25.在使用tokenizer时,使用max_lengthtruncationpad_to_max_length很重要。这些参数通过将输出填充到之前定义的最大长度 256 来确保输出具有可用的形状。现在您可以使用此示例运行模型:

model([tokenized["input_ids"],tokenized["attention_mask"]])

以下是输出:

图 3.11 – BERT 模型分类输出

26.什么时候训练模型,您需要使用compile函数对其进行编译:

model.compile(optimizer="Adam",
loss="categorical_crossentropy",
metrics=["accuracy"])

model.summary()

输出如下:

【Transformers】第 3 章:自动编码语言模型_第8张图片

图 3.12 – BERT 模型总结

27.从模型摘要可以看出,该模型有 109,483,778 个可训练参数,包括 BERT。但是,如果您对 BERT 模型进行了预训练,并且希望将其冻结在特定于任务的训练中,则可以使用以下命令执行此操作:

model.layers[2].trainable = False

据我们所知,嵌入层的层索引是2,所以我们可以简单地冻结它。如果重新运行汇总函数,您将看到可训练参数减少到 1,538,这是最后一层的参数数量:

【Transformers】第 3 章:自动编码语言模型_第9张图片

图 3.13 – 可训练参数较少的 BERT 模型摘要

28.您还记得,我们使用 IMDB 情感分析数据集来训练语言模型。现在您可以使用它来训练基于 Keras 的情感分析模型。但首先,您需要准备输入和输出:

将熊猫导入为 pd

import pandas as pd

imdb_df = pd.read_csv("IMDB Dataset.csv")
reviews = list(imdb_df.review)
tokenized_reviews = tokenizer.batch_encode_plus(reviews, return_tensors="tf",
                           max_length=max_length,
                           truncation=True,
                           pad_to_max_length=True)

import numpy as np

train_split = int(0.8 * \ len(tokenized_reviews["attention_mask"]))
train_tokens = tokenized_reviews["input_ids"][:train_split]
test_tokens = tokenized_reviews["input_ids"][train_split:]
train_masks = tokenized_reviews["attention_mask"][:train_split]

test_masks = tokenized_reviews["attention_mask"][train_split:]
sentiments = list(imdb_df.sentiment)
labels = np.array([[0,1] if sentiment == "positive" else [1,0] for sentiment in sentiments])

train_labels = labels[:train_split]
test_labels = labels[train_split:]

29.最后,您的数据已准备就绪,您可以拟合您的模型:

 model.fit([train_tokens,train_masks],train_labels,
             epochs=5)

拟合模型后,您的模型就可以使用了。到目前为止,您已经学习了如何为分类任务执行模型训练。您已经学习了如何保存它,并且在下一节中,您将了解如何与社区共享经过训练的模型。

与社区共享模型

拥抱脸提供了一个非常好用的模型共享机制:

  1. 您可以简单地使用以下cli工具登录:
    Transformers-cli login
  2. 使用自己的凭据登录后,您可以创建存储库:
    Transformers-cli repo create a-fancy-model-name
  3. 您可以为a-fancy-model-name参数输入任何模型名称,然后确保您有 git-lfs:
    git lfs install

    Git LFS 是用于处理大文件的 Git 扩展。HuggingFace 预训练模型通常是需要由 Git 处理的额外库(例如 LFS)的大文件。

  4. 然后您可以克隆您刚刚创建的存储库:
    git clone https://huggingface.co/username/a-fancy-model-name
  5. 之后,您可以根据需要从存储库中添加和删除,然后,就像使用 Git 一样,您必须运行以下命令:
    git add . && git commit -m "Update from $USER"
    git push

自动编码模型依赖于原始 Transformer 的左侧编码器,在解决分类问题方面非常有效。尽管 BERT 是自动编码模型的典型示例,但文献中讨论了许多替代方案。让我们来看看这些重要的替代方案。

了解其他自动编码模型

在这一部分,我们将回顾稍微修改原始 BERT 的自动编码模型替代方案。这些替代的重新实现通过利用许多来源导致了更好的下游任务:优化预训练过程和层数或头数,提高数据质量,设计更好的目标函数等等。改进的来源大致分为两部分:更好的架构设计选择预训练控制

最近分享了许多有效的替代方案,因此无法在此处全部理解和解释。我们可以看看文献中引用最多的一些模型以及 NLP 基准测试中最常用的模型。让我们从Albert作为 BERT 的重新实现开始,它特别关注架构设计选择。

介绍 ALBERT

的表现语言模型被认为随着它们的规模变大而改进。然而,由于内存限制和较长的训练时间,训练此类模型变得越来越具有挑战性。至针对这些问题,Google 团队提出了Albert 模型A Lite BERT for Self-Supervised Learning of Language Representations),这确实是对 BERT 架构的重新实现,利用了几种降低内存消耗和提高训练速度的新技术。新设计使语言模型的扩展性比原始 BERT 好得多。除了参数减少 18 倍外,Albert 的训练速度比原始 BERT-large 模型快 1.7 倍。

Albert 模型主要包括对原始 BERT 的三个修改:

  • 分解嵌入参数化
  • 跨层参数共享
  • 句间连贯损失

前两个修改是参数减少方法,与原始 BERT 中的模型大小和内存消耗问题有关。第三个对应一个新的目标函数:Sentence-Order Prediction ( SOP ),代替Next Sentence Prediction ( NSP ) 任务原始的 BERT,这导致模型更薄并提高了性能。

分解嵌入参数化用于将大词汇嵌入矩阵分解为两个小矩阵,将隐藏层的大小与词汇的大小分开。这种分解将嵌入参数从O(V × H) 减少到 O(V × E + E × H),其中V词汇H隐藏层大小E嵌入,这可以更有效地使用总模型参数如果满足 H >> E。

跨层参数共享防止参数的总数随着网络变得更深而增加。该技术被认为是提高参数效率的另一种方法,因为我们可以通过共享或复制来保持参数大小更小。在原始论文中,他们尝试了多种共享参数的方法,例如跨层共享仅 FF 参数或共享仅注意力参数或整个参数。

Albert 的另一个修改是句子间连贯性损失。正如我们已经讨论过的,BERT 架构利用了两种损失计算,即掩蔽语言建模MLM ) 损失和NSP。NSP 带有二元交叉熵损失,用于预测两个片段是否在原始文本中连续出现。反例是通过从不同的文档中选择两个片段来获得的。然而,Albert 团队批评 NSP 是一个主题检测问题,这被认为是一个相对容易的问题。因此,该团队提出了主要基于连贯性而不是主题预测的损失。他们利用了 SOP 损失,它侧重于对句子间的连贯性进行建模,而不是主题预测。SOP loss 使用与 BERT 相同的正例技术(即来自同一文档的两个连续段),作为负例,使用相同的两个连续段但它们的顺序交换。然后,该模型被迫在话语级别学习连贯性属性之间的更细粒度的区别。

  1. 对比一下原版BERT 和 Albert 配置与Transformers库。以下代码展示了如何配置 BERT-Base 初始模型。正如您在输出中看到的,参数数量约为 110 M:
    #BERT-BASE (L=12, H=768, A=12, Total Parameters=110M)
    
    from Transformers import BertConfig, BertModel
    
    bert_base= BertConfig()
    model = BertModel(bert_base)
    print(f"{model.num_parameters() /(10**6)} million parameters")

    109.48224 million parameters

  2. 以下代码展示了如何使用Transformers 库中的两个AlbertConfigAlbertModel定义 Albert 模型:
    # Albert-base Configuration
    
    from Transformers import AlbertConfig, AlbertModel
    
    albert_base = AlbertConfig(hidden_size=768,
                                  num_attention_heads=12,
                                  intermediate_size=3072,)
    
    model = AlbertModel(albert_base)
    
    print(f"{model.num_parameters() /(10**6)} million parameters")

    11.683584 million parameters

    正因为如此,默认的 Albert 配置指向 Albert-xxlarge。我们需要设置隐藏大小、注意力头的数量和中间大小以适应 Albert-base。代码显示Albert-base 模式为 11M,比 BERT-base 模型小 10 倍。关于 ALBERT 的原始论文报告了基准测试,如下表所示:

    图 3.14 – Albert 模型基准测试

  3. 从现在开始,为了从头开始训练 Albert 语言模型,我们需要经历与我们在 BERT 训练中已经说明的阶段类似的阶段。通过使用统一的 Transformers API 前面的部分。没有必要在这里解释相同的步骤!相反,让我们加载一个已经训练好的 Albert 语言模型,如下所示:
    from Transformers import AlbertTokenizer, AlbertModel
    
    tokenizer = AlbertTokenizer.from_pretrained("albert-base-v2")
    
    model = AlbertModel.from_pretrained("albert-base-v2")
    text = "The cat is so sad ."
    
    encoded_input = tokenizer(text, return_tensors='pt')
    output = model(**encoded_input)
  4. 前面的代码从 HuggingFace 集线器或我们的本地缓存目录(如果已缓存)下载 Albert 模型权重及其配置,这意味着您之前已经调用了AlbertTokenizer.from_pretrained()函数。由于模型对象是预训练的语言模型,我们现在可以用这个模型做的事情是有限的。我们需要在下游任务上对其进行训练,以便能够将其用于推理,这将是后续章节的主题。相反,我们可以利用其掩码语言模型目标,如下所示:
    from Transformers import pipeline
    
    fillmask= pipeline('fill-mask', model='albert-base-v2')
    pd.DataFrame(fillmask("The cat is so [MASK] ."))

    以下是输出:

【Transformers】第 3 章:自动编码语言模型_第10张图片

图 3.15 – albert-base-v2 的填充掩码输出结果

填充蒙版管道使用SoftMax()函数计算每个词汇标记的分数,并对最可能的标记进行排序,其中可爱是赢家概率分数为 0.281。您可能会注意到token_str列中的条目以_字符开头,这是由于 Albert 的标记器的元空间组件。

让我们看看下一个替代方案RoBERTa,它主要关注预训练阶段。

RoBERTa

鲁棒优化的 BERT 预训练方法RoBERTa ) 是另一个流行的 BERT 重新实现。它有在培训策略方面提供了比架构设计更多的改进。它在 GLUE 上的几乎所有单个任务中都优于 BERT。动态遮罩是其原始设计选择之一。尽管静态掩蔽对于某些任务来说效果更好,但 RoBERTa 团队表明动态掩蔽可以很好地实现整体性能。让我们比较一下 BERT 的变化,总结一下所有的特性如下:

架构的变化如下:

  • 去除下一句预测训练目标
  • 动态更改掩蔽模式而不是静态掩蔽,这是通过在将序列提供给模型时生成掩蔽模式来完成的
  • BPE子词分词器

这培训变化如下:

  • 控制训练数据:使用更多的数据,例如 160 GB 而不是 BERT 最初使用的 16 GB。研究不仅考虑了数据的大小,还考虑了质量和多样性。
  • 高达 500K 预训练步骤的更长迭代。
  • 更长的批量大小。
  • 更长的序列,导致更少的填充。
  • 大型 50K BPE 词汇表,而不是 30K BPE 词汇表。

感谢 Transformers 统一 API,就像上面的 Albert 模型管道,我们初始化 RoBERTa 模型如下:

from Transformers import RobertaConfig, RobertaModel

conf= RobertaConfig()

model = RobertaModel(conf)
print(f"{model.num_parameters() /(10**6)} million parameters")

109.48224 million parameters

为了加载预训练模型,我们执行以下代码:

from Transformers import RobertaTokenizer, RobertaModel

tokenizer = RobertaTokenizer.from_pretrained('roberta-base')

model = RobertaModel.from_pretrained('roberta-base')
text = "The cat is so sad ."

encoded_input = tokenizer(text, return_tensors='pt')
output = model(**encoded_input)

这些线条说明模型如何处理给定的文本。最后一层的输出表示目前没有用。正如我们多次提到的,我们需要微调主要的语言模型。以下执行使用roberta-base模型应用填充掩码函数:

from Transformers import pipeline

fillmask= pipeline("fill-mask ",model="roberta-base",
                       tokenizer=tokenizer)

pd.DataFrame(fillmask("The cat is so  ."))

以下是输出:

【Transformers】第 3 章:自动编码语言模型_第11张图片

图 3.16 – roberta-base 的填充掩码任务结果

与之前的 ALBERT填充掩码模型一样,该管道对合适的候选词进行排名。请忽略标记中的前缀Ġ - 这是由字节级 BPE 标记器生成的编码空格字符,我们将在后面讨论。您应该已经注意到,我们在 ALBERT 和 RoBERTa 管道中使用了[MASK]标记,以便为屏蔽标记保留位置。这是因为tokenizer的配置。要了解将使用哪个令牌表达式,您可以检查tokenizer.mask_token。请看以下执行:

tokenizer = AlbertTokenizer.from_pretrained('albert-base-v2')

print(tokenizer.mask_token)

[MASK]

tokenizer = RobertaTokenizer.from_pretrained('roberta-base')

print(tokenizer.mask_token)

为了确保正确使用掩码令牌,我们可以在管道中添加fillmask.tokenizer.mask_token表达式,如下所示:

fillmask(f"The cat is very {fillmask.tokenizer.mask_token}.")

ELECTRA

ELECTRA模型(由 Kevin Clark 等人于 2020 年提出)专注于一种新的掩码语言利用替换标记检测训练目标的模型。期间在预训练中,模型被迫学习区分真实输入标记和合成生成的替换,其中合成的负例是从合理的标记中采样的,而不是随机采样的标记。Albert 模型批评 BERT 的 NSP 目标是一个主题检测问题,并且使用了低质量的负样本。ELECTRA 训练两个神经网络,一个生成器和一个判别器,以便前者产生高质量的负样本,而后者将原始标记与替换标记区分开来。我们从计算机视觉领域了解 GAN 网络,其中生成器G生成假图像并试图欺骗判别器D, 鉴别器网络试图避免被愚弄。ELECTRA 模型应用几乎相同的生成器 - 鉴别器方法来用高质量的负样本替换原始令牌,这些负样本是合理的替换但综合生成的。

为了不与其他示例重复相同的代码,我们仅提供一个简单的填充掩码示例发电机如下:

fillmask = pipeline("fill-mask", model="google/electra-small-generator")

fillmask(f"The cat is very \{fillmask.tokenizer.mask_token} .")

您可以在以下链接中查看完整的模型列表:https ://huggingface.co/Transformers/model_summary.html 。

模型检查点可以在Models - Hugging Face找到。

做得好!我们终于完成了自动编码模型部分。现在我们将继续讨论标记化算法,它对 Transformer 的成功有重要影响。

使用标记化算法

在开口部分在本章中,我们使用特定的分词器训练 BERT 模型,即BertWordPieceTokenizer. 现在值得在这里详细讨论标记化过程。标记化是一种在输入神经网络架构之前将文本输入拆分为标记并为每个标记分配标识符的方法。最直观的方法是将序列在空间上分成更小的块。然而,这样的方法不能满足某些语言的要求,例如日语,并且还可能导致巨大的词汇问题。几乎所有的 Transformer 模型都利用子词标记化,不仅可以降低维度,还可以对训练中未见的稀有(或未知)词进行编码。标记化依赖于这样的想法,即每个单词,包括稀有词或未知词,都可以分解成有意义的更小的块,这些块是训练语料库中广泛看到的符号。

在 Moses 和nltk库中开发的一些传统标记器应用了先进的基于规则的技术。但是与 Transformer 一起使用的标记化算法是基于自我监督学习并从语料库中提取规则的。基于规则的标记化的简单直观解决方案基于使用字符、标点符号或空格。基于字符的标记化会导致语言模型失去输入意义。尽管它可以减少词汇量,这很好,但它使模型难以通过字符cat的编码来捕捉cat的含义. 而且,输入序列的维度变得非常大。同样,基于标点符号的模型无法正确处理某些表达式,例如 Havetai n't

最近,一些高级子词标记化算法,例如 BPE,已经成为 Transformer 架构中不可或缺的一部分。这些现代标记化过程包括两个阶段: 预标记化阶段简单地将输入拆分为使用空间或语言相关规则的标记。其次,tokenization训练阶段是训练tokenizer,并基于token构建一个合理大小的基础词汇。在训练我们自己的分词器之前,让我们加载一个预训练的分词器。以下代码从Transformers库中加载一个BertTokenizerFast类型的土耳其标记器,词汇量为 32K:

from Transformers import AutoModel, AutoTokenizer

tokenizerTUR = AutoTokenizer.from_pretrained(
                   "dbmdz/bert-base-turkish-uncased")

print(f"VOC size is: {tokenizerTUR.vocab_size}")
print(f"The model is: {type(tokenizerTUR)}")

VOC size is: 32000

The model is: Transformers.models.bert.tokenization_bert_fast.BertTokenizerFast

以下代码为bert-base-uncased模型加载英文 BERT 分词器:

from Transformers import AutoModel, AutoTokenizer

tokenizerEN = AutoTokenizer.from_pretrained("bert-base-uncased")

print(f"VOC size is: {tokenizerEN.vocab_size}")
print(f"The model is {type(tokenizerEN)}")

VOC size is: 30522

The model is ... BertTokenizerFast

让我们看看它们是如何工作的!我们用这两个标记器标记了电讯这个词:

word_en="telecommunication"

print(f"is in Turkish Model ? {word_en in tokenizerTUR.vocab}")

print(f"is in English Model ? {word_en in tokenizerEN.vocab}")

is in Turkish Model ? False

is in English Model ? True

word_en标记已经在英语标记器的词汇中,但不在土耳其语的词汇中。那么让我们看看土耳其分词器会发生什么:

tokens=tokenizerTUR.tokenize(word_en)

tokens

['tel', '##eco', '##mm', '##un', '##ica', '##tion']

由于土耳其分词器模型的词汇表中没有这样的词,因此它需要将词分解成对其有意义的部分。所有这些拆分标记都已存储在模型词汇表中。请注意以下执行的输出:

[t in tokenizerTUR.vocab for t in tokens]

[True, True, True, True, True, True]

让我们用我们已经加载的英文分词器对同一个词进行分词:

tokenizerEN.tokenize(word_en)

['telecommunication']

由于英语模型的基础词汇中有电信一词,因此不需要将其分解为一个整体,而是将其作为一个整体。通过从语料库中学习,分词器能够将一个词转换成大部分符合语法逻辑的子组件。让我们从土耳其语中举一个困难的例子。作为一种黏着性语言,土耳其语允许我们在词干中添加许多后缀来构造很长的词。这是文本中使用的土耳其语中最长的单词之一(https://en.wikipedia.org/wiki/Longest_word_in_Turkish):

好像你是我们可能无法解除武装的人之一。

这意味着好像您恰好来自那些我们将无法轻松/快速地制造不成功的人的人。土耳其 BERT 分词器可能在训练中没有看到这个词,但它已经看到了它的部分;muvaffak(成功)作为词干,##iyet(成功),##siz(不成功),##leş(变得不成功)等等。土耳其语分词器在以下情况下提取似乎在土耳其语语法上合乎逻辑的组件将结果与 Wikipedia 文章进行比较:

print(tokenizerTUR.tokenize(long_word_tur))

['muvaffak', '##iyet', '##siz', '##les', '##tir', '##ici', '##les', '##tir', '##iver', '##emeye', '##bilecekleri', '##mi', '##z', '##den', '##mis', '##siniz', '##cesine']

土耳其分词器是 WordPiece 算法的一个示例,因为它适用于 BERT 模型。包括 BERT、DistilBERT 和 ELECTRA 在内的几乎所有语言模型都需要 WordPiece 分词器。

现在我们准备看看 Transformers 使用的标记化方法。首先,我们将稍微讨论一下 BPE、WordPiece 和 SentencePiece 的广泛使用的标记化,然后使用 HuggingFace 的快速标记器库对它们进行训练。

字节对编码

BPE是一种数据压缩技术。它扫描数据序列并用单个符号迭代地替换最频繁的字节对。它首先在Sennrich等人的带有子词单元的稀有词的神经机器翻译中被改编和提出。2015年,解决机器翻译的生词和生词问题。目前,它已成功用于 GPT-2 和许多其他最先进的模型中。许多现代标记化算法都基于这种压缩技术。

它将文本表示为一系列字符 n-gram,也称为字符级子词。训练最初从语料库中所有 Unicode 字符(或符号)的词汇表开始。这对于英语可能很小,但对于字符丰富的语言(例如日语)可能很大。然后,它迭代地计算字符二元组,并用特殊的新符号替换最常见的二元组。例如,th是经常出现的符号。我们用第 th 个符号替换连续的符号。该过程保持迭代运行,直到词汇表达到所需的词汇量大小。最常见的词汇量约为 30K。

BPE 尤其有效地表示未知单词。然而,它可能不保证处理稀有词和/或包含稀有子词的词。在这种情况下,它将稀有字符与一个特殊符号相关联这可能会导致一些文字失去意义。作为一个潜在的解决方案,已经提出了字节级 BPE ( BBPE ),它使用 256 字节的词汇集而不是 Unicode 字符来确保每个基本字符都包含在词汇表中。

WordPiece 标记化

WordPiece是另一种流行的分词算法,广泛用于 BERT、DistilBERT 和 Electra。它是由 Schuster 和 Nakajima 在 2012 年提出来解决日语和韩语语音问题的。这项工作背后的动机是,虽然对英语来说不是一个大问题,但分词是许多亚洲人的重要预处理语言,因为在这些语言中很少使用空格。因此,我们在亚洲语言的 NLP 研究中经常遇到分词方法。与 BPE 类似,WordPiece 使用大型语料库来学习词汇和合并规则。BPE 和 BBPE 基于共现统计学习合并规则,而 WordPiece 算法使用最大似然估计从语料库中提取合并规则。它首先用 Unicode 字符初始化词汇,也称为词汇符号。它将训练语料库中的每个单词视为符号列表(最初是 Unicode 字符),然后根据似然最大化而不是频率,迭代地生成一个新符号,将所有可能的候选符号对中的两个符号合并。

句子标记化

以前的标记化算法将文本视为以空格分隔的单词列表。这个基于空间的拆分在某些语言中不起作用。在德语中,复合名词不带空格,例如 menschenrechte(人权)。解决方案是使用特定于语言的预分词器。在德语中,NLP 管道利用复合拆分器模块来检查一个单词是否可以细分为更小的单词。但是,东亚语言(例如中文、日语、韩语和泰语)不使用单词之间的空格。SentencePiece算法旨在克服这一空间限制,即是 Kudo 等人提出的一种简单且与语言无关的分词器。在 2018 年。它将输入视为原始输入流,其中空格是字符集的一部分。使用 SentencePiece 的分词器生成_字符,这也是我们在前面的 Albert 模型示例的输出中看到_的原因。其他使用 SentencePiece 的流行语言模型是 XLNet、Marian 和 T5。

到目前为止,我们已经讨论了子词标记化方法。是时候开始使用标记器库进行训练实验了。

分词器库

您可能已经注意到,已经训练过的土耳其语和英语分词器是前面代码示例中Transformers库的一部分。另一方面,HuggingFace 团队独立于Transformers库提供了分器库,以提高速度并为我们提供更多自由。该库最初是用 Rust 编写的,它使多核并行计算成为可能,并用 Python ( GitHub - huggingface/tokenizers: Fast State-of-the-Art Tokenizers optimized for Research and Production ) 包装。

要安装tokenizers库,我们使用这个:

$ pip install tokenizers

标记器库提供了几个组件,以便我们可以构建一个端到端的标记器,从预处理原始文本到解码标记化的单元 ID:

Normalizer→PreTokenizer→建模→后处理器→解码

下图描述了标记化管道:

【Transformers】第 3 章:自动编码语言模型_第12张图片

图 3.17 – 标记化管道

  • Normalizer允许我们应用原始文本处理,例如小写、剥离、Unicode 规范化和删除重音符号。
  • PreTokenizer为下一个训练阶段准备语料库。它根据规则将输入拆分为标记,例如空格。
  • 模型训练是一种子词标记化算法,例如我们已经讨论过的BPEBBPEWordPiece 。它发现子词/词汇并学习生成规则。
  • 后处理提供了与 Transformers 模型(例如 BertProcessors)兼容的高级类构造。我们主要在输入架构之前向标记化输入添加特殊标记,例如[CLS][SEP] 。
  • 解码器负责将令牌 ID 转换回原始字符串。它只是用于检查正在发生的事情。

培训 BPE

让我们训练一个 BPE 分词器使用莎士比亚的戏剧:

  1. 它的加载方式如下:
    import nltk
    from nltk.corpus import gutenberg
    
    nltk.download('gutenberg')
    nltk.download('punkt')
    
    plays=['shakespeare-macbeth.txt','shakespeare-hamlet.txt',
          'shakespeare-caesar.txt']
    
    shakespeare=[" ".join(s) for ply in plays for s in gutenberg.sents(ply)]

    对于前面的所有标记化算法,我们将需要一个后处理器 ( TemplateProcessing )。我们需要定制后处理器以方便特定语言模型的输入。例如,以下模板将适用于 BERT 模型,因为它需要输入开头的[CLS]标记和结尾和中间的[SEP]标记。

  2. 我们定义模板如下:
    from tokenizers.processors import TemplateProcessing
    special_tokens=["[UNK]","[CLS]","[SEP]","[PAD]","[MASK]"]
    
    temp_proc= TemplateProcessing(
        single="[CLS] $A [SEP]",
        pair="[CLS] $A [SEP] $B:1 [SEP]:1",
        special_tokens=[
            ("[CLS]", special_tokens.index("[CLS]")),
            ("[SEP]", special_tokens.index("[SEP]")),
        ],
    )
  3. 我们导入必要的组件来构建端到端的标记化管道:
    from tokenizers import Tokenizer
    from tokenizers.normalizers import (Sequence,Lowercase, NFD, StripAccents)
    from tokenizers.pre_tokenizers import Whitespace
    from tokenizers.models import BPE
    from tokenizers.decoders import BPEDecoder
  4. 我们从实例化BPE如下:
    tokenizer = Tokenizer(BPE())
  5. 预处理部分有两个组件:normalizerpre-tokenizer。我们可能有不止一个归一化器。因此,我们组成了一个包含多个规范器的规范器组件序列,其中NFD()是 Unicode 规范器,而StripAccents()删除重音符号。对于预标记化,Whitespace()会根据空格轻轻打破文本。由于解码器组件必须与模型兼容,因此BPE模型选择了BPEDecoder
    tokenizer.normalizer = Sequence([NFD(),Lowercase(),StripAccents()])
    tokenizer.pre_tokenizer = Whitespace()
    tokenizer.decoder = BPEDecoder()
    tokenizer.post_processor=temp_proc
  6. 出色地!我们已准备好在数据上训练分词器。下面的执行实例化了 BpeTrainer(),它通过设置超参数帮助我们组织整个训练过程。我们将词汇量参数设置为 5K,因为我们的莎士比亚语料库相对较小。对于大型项目,我们使用更大的语料库,通常将词汇量设置为 30K 左右:
    from tokenizers.trainers import BpeTrainer
    
    trainer = BpeTrainer(vocab_size=5000,
                            special_tokens= special_tokens)
    
    tokenizer.train_from_iterator(shakespeare,
                                      trainer=trainer)
    
    print(f"Trained vocab size: {tokenizer.get_vocab_size()}" )

    Trained vocab size: 5000

    我们有完成培训!

    重要的提示

    从文件系统训练:为了开始训练过程,我们将内存中的莎士比亚对象作为字符串列表传递给tokenizer.train_from_iterator()。对于具有大量语料库的大型项目,我们需要设计一个 Python 生成器,它主要通过使用文件系统中的文件而不是内存存储来生成字符串行。您还应该检查tokenizer.train()以从文件系统存储中进行训练,如上面 BERT 训练部分中所应用的那样。

  7. 让我们从剧本 Macbeth 中随机抽取一个句子,将其命名为sen,并使用我们新的分词器对其进行分词:
    sen= "Is this a dagger which I see before me,the handle toward my hand?"
    
    sen_enc=tokenizer.encode(sen)
    print(f"Output: {format(sen_enc.tokens)}")

    Output: ['[CLS]', 'is', 'this', 'a', 'dagger', 'which', 'i', 'see', 'before', 'me', ',', 'the', 'hand', 'le', 'toward', 'my', 'hand', '?', '[SEP]']

  8. 多亏了上面的后处理器功能,我们在适当的位置看到了额外的[CLS][SEP]标记。只有一个拆分词,句柄hand , le ),因为我们向模型传递了模型已经知道的麦克白戏剧中的一个句子。此外,我们使用了一个小型语料库,并且分词器没有强制使用压缩。让我们传递一个具有挑战性的短语Hugging Face,分词器可能不知道:
    sen_enc2=tokenizer.encode("Macbeth and Hugging Face")
    
    print(f"Output: {format(sen_enc2.tokens)}")

    Output: ['[CLS]', 'macbeth', 'and', 'hu', 'gg', 'ing', 'face', '[SEP]']

  9. 拥抱一词是小写的并分成三部分hugging,因为模型词汇表包含除Hugging之外的所有其他标记。现在让我们通过两句话:
    two_enc=tokenizer.encode("I like Hugging Face!",
    "He likes Macbeth!")
    
    print(f"Output: {format(two_enc.tokens)}")

    Output: ['[CLS]', 'i', 'like', 'hu', 'gg', 'ing', 'face', '!', '[SEP]', 'he', 'li', 'kes', 'macbeth', '!', '[SEP]']

    请注意,后处理器注入了[SEP]令牌作为指标。

  10. 是时候保存模型了。我们可以保存子词标记化模型或整个标记化管道。首先,让我们只保存 BPE 模型:
    tokenizer.model.save('.')

    ['./vocab.json', './merges.txt']

  11. 该模型保存了两个关于词汇和合并规则的文件。merge.txt文件由 4,948 条合并规则组成
    $ wc -l ./merges.txt

    4948 ./merges.txt

  12. 排名前五的规则如下所示,我们看到 ( t , h ) 是排名第一的规则,因为它是最频繁的对。为了进行测试,模型会扫描文本输入,并在适用时尝试首先合并这两个符号:
    $ head -3 ./merges.txt

    t h

    o u

    a n

    th e

    r e

    BPE 算法对基于频率的规则。当您在莎士比亚语料库中手动计算字符二元组时,您会发现 ( t , h ) 是最频繁的对。

  13. 现在让我们保存并加载整个标记化管道:
    tokenizer.save("MyBPETokenizer.json")
    tokenizerFromFile = Tokenizer.from_file("MyBPETokenizer.json")
    sen_enc3 = tokenizerFromFile.encode("I like Hugging Face and Macbeth")
    print(f"Output: {format(sen_enc3.tokens)}")

    Output: ['[CLS]', 'i', 'like', 'hu', 'gg', 'ing', 'face', 'and', 'macbeth', '[SEP]']

我们成功地重新加载了分词器!

训练 WordPiece 模型

在本节中,我们将训练 WordPiece 模型:

  1. 我们首先导入必要的模块:
    from tokenizers.models import WordPiece
    from tokenizers.decoders import WordPiece as WordPieceDecoder
    from tokenizers.normalizers import BertNormalizer
  2. 以下几行实例化了一个空的 WordPiece 分词器并为训练做准备。BertNormalizer是一个预定义的 normalizer 序列,包括清理文本、转换重音、处理汉字和小写的过程:
    tokenizer = Tokenizer(WordPiece())
    tokenizer.normalizer=BertNormalizer()
    tokenizer.pre_tokenizer = Whitespace()
    tokenizer.decoder= WordPieceDecoder()
    
    
  3. 现在,我们为 WordPiece()实例化一个合适的训练器WordPieceTrainer ()来组织培训过程:
    from tokenizers.trainers import WordPieceTrainer
    
    trainer = WordPieceTrainer(vocab_size=5000,\
                 special_tokens=["[UNK]", "[CLS]", "[SEP]",\
                 "[PAD]", "[MASK]"])
    
    tokenizer.train_from_iterator(shakespeare,trainer=trainer)
    
    output = tokenizer.encode(sen)
    print(output.tokens)

    ['is', 'this', 'a', 'dagger', 'which', 'i', 'see', 'before', 'me', ',', 'the', 'hand', ' ##le', 'toward', 'my', 'hand', '?']

  4. 让我们使用WordPieceDecoder()来正确处理句子:
    tokenizer.decode(output.ids)

    'is this a dagger which i see before me, the handle toward my hand?'

  5. 我们在输出中没有遇到任何[UNK]标记,因为标记器以某种方式知道或拆分输入以进行编码。让我们强制模型生成[UNK] 标记,如下面的代码所示。让我们将土耳其语句子传递给我们的分词器:
    tokenizer.encode("Kralsın aslansın Macbeth!").tokens

    '[UNK]', '[UNK]', 'macbeth', '!']

    做得好!我们有几个未知的标记,因为标记器没有找到从合并规则和基本词汇表中分解给定单词的方法。

到目前为止,我们已经设计了从规范器组件到解码器组件的所有标记化管道。另一方面,tokenizers库为我们提供了一个已经制作(未经训练)的空标记化管道,其中包含适当的组件来构建用于生产的快速原型。以下是一些预制的标记器:

  • CharBPETokenizer : 原始 BPE
  • ByteLevelBPETokenizer : BPE 的字节级版本
  • SentencePieceBPETokenizer :一种与SentencePiece使用的兼容的 BPE 实现
  • BertWordPieceTokenizer : 著名的 BERT 分词器,使用 WordPiece

以下代码导入这些管道:

from tokenizers import (ByteLevelBPETokenizer,
                            CharBPETokenizer,
                            SentencePieceBPETokenizer,
                            BertWordPieceTokenizer)

所有这些管道都是为我们设计的。其余过程(例如训练、保存模型和使用标记器)与我们之前的 BPE 和 WordPiece 训练过程相同。

做得好!我们取得了长足的进步,并训练了我们的第一个 Transformer 模型及其标记器。

概括

在本章中,我们在理论上和实践上都体验了自动编码模型。从 BERT 的基本知识开始,我们从头开始训练它以及相应的分词器。我们还讨论了如何在其他框架内工作,例如 Keras。除了 BERT,我们还回顾了其他自动编码模型。为了避免过多的代码重复,我们没有提供训练其他模型的完整实现。在 BERT 训练期间,我们训练了 WordPiece 标记化算法。在最后一部分中,我们检查了其他标记化算法,因为它们都值得讨论和理解。

自动编码模型使用原始 Transformer 的左侧解码器,并且主要针对分类问题进行了微调。在下一章中,我们将讨论和了解 Transformers 的正确解码器部分以实现语言生成模型。

你可能感兴趣的:(《,Transformers》,语言模型,深度学习,人工智能)