Tokenizers 文档
分词管道
并获得增强的文档体验
开始入门
分词管道
当调用 Tokenizer.encode
或 Tokenizer.encode_batch
时,输入文本将经过以下管道
归一化 (normalization)
预分词 (pre-tokenization)
模型 (model)
后处理 (post-processing)
我们将详细了解每个步骤中发生的情况,以及何时需要 decode <decoding>
一些 token id,以及 🤗 Tokenizers 库如何允许您根据需要自定义每个步骤。如果您已经熟悉这些步骤并想通过查看一些代码来学习,请跳转到 我们的从零开始的 BERT 示例 <example>
。
对于需要 Tokenizer
的示例,我们将使用在 快速入门
中训练的分词器,您可以使用以下代码加载它
from tokenizers import Tokenizer
tokenizer = Tokenizer.from_file("data/tokenizer-wiki.json")
归一化 (Normalization)
简而言之,归一化 (Normalization) 是一组应用于原始字符串的操作,使其不那么随机或更“干净”。 常见操作包括去除空格、删除重音字符或将所有文本转换为小写。 如果您熟悉 Unicode 归一化,它也是大多数分词器中应用的一种非常常见的归一化操作。
每个归一化操作在 🤗 Tokenizers 库中都由 Normalizer
表示,您可以使用 normalizers.Sequence
组合其中几个。 以下是一个应用 NFD Unicode 归一化并删除重音符号的 normalizer 示例
from tokenizers import normalizers
from tokenizers.normalizers import NFD, StripAccents
normalizer = normalizers.Sequence([NFD(), StripAccents()])
您可以通过将 normalizer 应用于任何字符串来手动测试它
normalizer.normalize_str("Héllò hôw are ü?")
# "Hello how are u?"
在构建 Tokenizer
时,您只需更改相应的属性即可自定义其 normalizer
tokenizer.normalizer = normalizer
当然,如果您更改 tokenizer 应用归一化的方式,您可能应该之后从头开始重新训练它。
预分词 (Pre-Tokenization)
预分词 (Pre-tokenization) 是将文本拆分为更小的对象,这些对象为训练结束时您的 token 设置了上限。 考虑这个问题的一个好方法是,预分词器会将您的文本拆分为“单词”,然后,您的最终 token 将是这些单词的一部分。
预分词输入的一种简单方法是按空格和标点符号拆分,这由 pre_tokenizers.Whitespace
预分词器完成
from tokenizers.pre_tokenizers import Whitespace
pre_tokenizer = Whitespace()
pre_tokenizer.pre_tokenize_str("Hello! How are you? I'm fine, thank you.")
# [("Hello", (0, 5)), ("!", (5, 6)), ("How", (7, 10)), ("are", (11, 14)), ("you", (15, 18)),
# ("?", (18, 19)), ("I", (20, 21)), ("'", (21, 22)), ('m', (22, 23)), ("fine", (24, 28)),
# (",", (28, 29)), ("thank", (30, 35)), ("you", (36, 39)), (".", (39, 40))]
输出是一个元组列表,每个元组包含一个单词及其在原始句子中的跨度(用于确定最终 Encoding
的 offsets
)。 请注意,按标点符号拆分将拆分此示例中的缩略词,如 "I'm"
。
您可以将任何 PreTokenizer
组合在一起。 例如,这是一个预分词器,它将按空格、标点符号和数字拆分,将数字分隔为单个数字
from tokenizers import pre_tokenizers
from tokenizers.pre_tokenizers import Digits
pre_tokenizer = pre_tokenizers.Sequence([Whitespace(), Digits(individual_digits=True)])
pre_tokenizer.pre_tokenize_str("Call 911!")
# [("Call", (0, 4)), ("9", (5, 6)), ("1", (6, 7)), ("1", (7, 8)), ("!", (8, 9))]
正如我们在 快速入门
中看到的,您只需更改相应的属性即可自定义 Tokenizer
的预分词器
tokenizer.pre_tokenizer = pre_tokenizer
当然,如果您更改预分词器的方式,您可能应该之后从头开始重新训练您的分词器。
模型 (Model)
一旦输入文本被归一化和预分词,Tokenizer
就会对预 token 应用模型。 这是管道中需要在您的语料库上进行训练的部分(如果您使用的是预训练的分词器,则已训练)。
模型的作用是使用它学习的规则将您的“单词”拆分为 token。 它还负责将这些 token 映射到模型词汇表中相应的 ID。
此模型在初始化 Tokenizer
时传递,因此您已经知道如何自定义这部分。 目前,🤗 Tokenizers 库支持
models.BPE
models.Unigram
models.WordLevel
models.WordPiece
有关每个模型及其行为的更多详细信息,您可以查看 此处
后处理 (Post-Processing)
后处理 (Post-processing) 是分词管道的最后一步,用于在返回 Encoding
之前对其执行任何额外的转换,例如添加潜在的特殊 token。
正如我们在快速入门中看到的,我们可以通过设置相应的属性来自定义 Tokenizer
的后处理器。 例如,以下是如何进行后处理以使输入适合 BERT 模型
from tokenizers.processors import TemplateProcessing
tokenizer.post_processor = TemplateProcessing(
single="[CLS] $A [SEP]",
pair="[CLS] $A [SEP] $B:1 [SEP]:1",
special_tokens=[("[CLS]", 1), ("[SEP]", 2)],
)
请注意,与预分词器或 normalizer 相反,您无需在更改 tokenizer 的后处理器后重新训练它。
全部整合:从零开始构建 BERT 分词器
让我们将所有这些部分放在一起,构建一个 BERT 分词器。 首先,BERT 依赖于 WordPiece,因此我们使用此模型实例化一个新的 Tokenizer
from tokenizers import Tokenizer
from tokenizers.models import WordPiece
bert_tokenizer = Tokenizer(WordPiece(unk_token="[UNK]"))
然后我们知道 BERT 通过删除重音符号和转换为小写来预处理文本。 我们还使用 unicode normalizer
from tokenizers import normalizers
from tokenizers.normalizers import NFD, Lowercase, StripAccents
bert_tokenizer.normalizer = normalizers.Sequence([NFD(), Lowercase(), StripAccents()])
预分词器只是按空格和标点符号拆分
from tokenizers.pre_tokenizers import Whitespace
bert_tokenizer.pre_tokenizer = Whitespace()
后处理使用我们在上一节中看到的模板
from tokenizers.processors import TemplateProcessing
bert_tokenizer.post_processor = TemplateProcessing(
single="[CLS] $A [SEP]",
pair="[CLS] $A [SEP] $B:1 [SEP]:1",
special_tokens=[
("[CLS]", 1),
("[SEP]", 2),
],
)
我们可以使用此分词器并在 wikitext 上对其进行训练,就像在 快速入门
中一样
from tokenizers.trainers import WordPieceTrainer
trainer = WordPieceTrainer(vocab_size=30522, special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])
files = [f"data/wikitext-103-raw/wiki.{split}.raw" for split in ["test", "train", "valid"]]
bert_tokenizer.train(files, trainer)
bert_tokenizer.save("data/bert-wiki.json")
解码 (Decoding)
除了编码输入文本外,Tokenizer
还具有用于解码的 API,即将模型生成的 ID 转换回文本。 这是通过方法 Tokenizer.decode
(用于一个预测文本)和 Tokenizer.decode_batch
(用于一批预测)完成的。
decoder
将首先将 ID 转换回 token(使用 tokenizer 的词汇表)并删除所有特殊 token,然后用空格连接这些 token
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
print(output.ids)
# [1, 27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35, 2]
tokenizer.decode([1, 27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35, 2])
# "Hello , y ' all ! How are you ?"
如果您使用的模型添加了特殊字符来表示给定“单词”的子 token(例如 WordPiece 中的 "##"
),您将需要自定义 decoder
以正确处理它们。 例如,如果我们采用之前的 bert_tokenizer
,默认解码将给出
output = bert_tokenizer.encode("Welcome to the 🤗 Tokenizers library.")
print(output.tokens)
# ["[CLS]", "welcome", "to", "the", "[UNK]", "tok", "##eni", "##zer", "##s", "library", ".", "[SEP]"]
bert_tokenizer.decode(output.ids)
# "welcome to the tok ##eni ##zer ##s library ."
但是通过将其更改为正确的 decoder,我们得到
from tokenizers import decoders
bert_tokenizer.decoder = decoders.WordPiece()
bert_tokenizer.decode(output.ids)
# "welcome to the tokenizers library."