Tokenizers 文档
快速入门
并获得增强的文档体验
开始使用
快速入门
让我们快速了解一下 🤗 Tokenizers 库的特性。该库提供了当今最常用的分词器的实现,既易于使用又速度极快。
从零开始构建分词器
为了说明 🤗 Tokenizers 库的速度有多快,让我们在几秒钟内使用 wikitext-103(516M 文本)训练一个新的分词器。首先,您需要下载此数据集并使用以下命令解压缩:
wget https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-103-raw-v1.zip unzip wikitext-103-raw-v1.zip
训练分词器
在本教程中,我们将构建和训练一个字节对编码 (BPE) 分词器。有关不同类型分词器的更多信息,请查看 🤗 Transformers 文档中的指南。在这里,训练分词器意味着它将通过以下方式学习合并规则:
- 从训练语料库中存在的所有字符开始作为 tokens。
- 识别最常见的 tokens 对并将其合并为一个 token。
- 重复此过程,直到词汇表(例如,tokens 的数量)达到我们想要的大小。
该库的主要 API 是 class
Tokenizer
,以下是如何使用 BPE 模型实例化一个:
from tokenizers import Tokenizer
from tokenizers.models import BPE
tokenizer = Tokenizer(BPE(unk_token="[UNK]"))
为了在 wikitext 文件上训练我们的分词器,我们将需要实例化一个 [trainer]{.title-ref},在本例中是 BpeTrainer
from tokenizers.trainers import BpeTrainer
trainer = BpeTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])
我们可以设置训练参数,如 vocab_size
或 min_frequency
(此处保留其默认值 30,000 和 0),但最重要的部分是给出我们计划稍后使用的 special_tokens
(它们在训练期间根本不使用),以便将它们插入到词汇表中。
您编写特殊 tokens 列表的顺序很重要:此处 "[UNK]"
将获得 ID 0,"[CLS]"
将获得 ID 1,依此类推。
我们现在可以训练我们的分词器,但这不会是最优的。如果没有预分词器将我们的输入拆分为单词,我们可能会得到与多个单词重叠的 tokens:例如,我们可能会得到一个 "it is"
token,因为这两个单词经常彼此相邻出现。使用预分词器将确保没有 token 比预分词器返回的单词更大。在这里,我们想要训练一个 subword BPE 分词器,我们将使用最简单的预分词器,通过在空格上拆分。
from tokenizers.pre_tokenizers import Whitespace
tokenizer.pre_tokenizer = Whitespace()
现在,我们可以直接调用 Tokenizer.train
方法,并提供我们要使用的任何文件列表
files = [f"data/wikitext-103-raw/wiki.{split}.raw" for split in ["test", "train", "valid"]]
tokenizer.train(files, trainer)
这应该只需要几秒钟即可在完整的 wikitext 数据集上训练我们的分词器!要将分词器保存在一个文件中,该文件包含其所有配置和词汇表,只需使用 Tokenizer.save
方法
tokenizer.save("data/tokenizer-wiki.json")
您可以使用 Tokenizer.from_file
classmethod
从该文件重新加载您的分词器
tokenizer = Tokenizer.from_file("data/tokenizer-wiki.json")
使用分词器
现在我们已经训练了一个分词器,我们可以使用 Tokenizer.encode
方法将其用于任何我们想要的文本
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
这会将分词器的完整流程应用于文本,返回一个 Encoding
对象。要了解有关此流程以及如何应用(或自定义)其各个部分的更多信息,请查看此页面。
然后,此 Encoding
对象具有深度学习模型(或其他)所需的所有属性。tokens
属性包含文本在 tokens 中的分割
print(output.tokens)
# ["Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?"]
同样,ids
属性将包含每个 tokens 在分词器词汇表中的索引
print(output.ids)
# [27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35]
🤗 Tokenizers 库的一个重要功能是它带有完整的对齐跟踪,这意味着您始终可以找回原始句子中与给定 token 对应的部分。这些存储在 Encoding
对象的 offsets
属性中。例如,假设我们想要找回导致 "[UNK]"
token 出现的原因,它是列表中索引为 9 的 token,我们可以只请求该索引处的偏移量
print(output.offsets[9])
# (26, 27)
这些是与原始句子中的表情符号对应的索引
sentence = "Hello, y'all! How are you 😁 ?"
sentence[26:27]
# "😁"
后处理
我们可能希望我们的分词器自动添加特殊 tokens,例如 "[CLS]"
或 "[SEP]"
。为此,我们使用后处理器。TemplateProcessing
是最常用的,您只需指定用于处理单句和句子对的模板,以及特殊 tokens 及其 ID。
当我们构建分词器时,我们在特殊 tokens 列表的位置 1 和 2 中设置了 "[CLS]"
和 "[SEP]"
,因此这应该是它们的 ID。为了再次检查,我们可以使用 Tokenizer.token_to_id
方法
tokenizer.token_to_id("[SEP]")
# 2
以下是如何设置后处理以提供传统的 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]", tokenizer.token_to_id("[CLS]")),
("[SEP]", tokenizer.token_to_id("[SEP]")),
],
)
让我们更详细地了解这段代码。首先,我们指定单句的模板:这些句子应具有 "[CLS] $A [SEP]"
的形式,其中 $A
代表我们的句子。
然后,我们指定句子对的模板,它应具有 "[CLS] $A [SEP] $B [SEP]"
的形式,其中 $A
代表第一个句子,$B
代表第二个句子。模板中添加的 :1
代表我们希望输入的每个部分具有的 type IDs
:它默认为所有内容为 0(这就是为什么我们没有 $A:0
),在这里,我们将第二个句子和最后一个 "[SEP]"
token 的 type IDs 设置为 1。
最后,我们指定我们使用的特殊 tokens 及其在分词器词汇表中的 ID。
为了检查这是否正常工作,让我们尝试编码与之前相同的句子
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?", "[SEP]"]
要检查句子对的结果,我们只需将两个句子传递给 Tokenizer.encode
output = tokenizer.encode("Hello, y'all!", "How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "[SEP]", "How", "are", "you", "[UNK]", "?", "[SEP]"]
然后,您可以使用以下命令检查分配给每个 token 的类型 ID 是否正确
print(output.type_ids)
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
如果您使用 Tokenizer.save
保存您的分词器,后处理器将随之保存。
批量编码多个句子
为了获得 🤗 Tokenizers 库的全部速度,最好使用 Tokenizer.encode_batch
方法按批处理文本
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])
输出然后是 Encoding
对象列表,就像我们之前看到的那样。您可以一起处理任意数量的文本,只要它适合内存即可。
要处理一批句子对,请将两个列表传递给 Tokenizer.encode_batch
方法:句子 A 列表和句子 B 列表
output = tokenizer.encode_batch(
[["Hello, y'all!", "How are you 😁 ?"], ["Hello to you too!", "I'm fine, thank you!"]]
)
当编码多个句子时,您可以使用 Tokenizer.enable_padding
自动将输出填充到最长句子的长度,其中包含 pad_token
及其 ID(我们可以像之前一样使用 Tokenizer.token_to_id
再次检查填充 token 的 ID)
tokenizer.enable_padding(pad_id=3, pad_token="[PAD]")
我们可以设置填充的 direction
(默认为右侧)或给定的 length
,如果我们想要将每个样本填充到该特定数字(此处我们将其保持未设置,以填充到最长文本的大小)。
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])
print(output[1].tokens)
# ["[CLS]", "How", "are", "you", "[UNK]", "?", "[SEP]", "[PAD]"]
在这种情况下,分词器生成的 attention mask
会将填充考虑在内
print(output[1].attention_mask)
# [1, 1, 1, 1, 1, 1, 1, 0]
预训练
使用预训练的分词器
您可以从 Hugging Face Hub 加载任何分词器,只要仓库中提供 tokenizer.json
文件即可。
from tokenizers import Tokenizer
tokenizer = Tokenizer.from_pretrained("bert-base-uncased")
从旧版词汇表文件导入预训练的分词器
您也可以直接导入预训练的分词器,只要您有其词汇表文件即可。例如,以下是如何导入经典的预训练 BERT 分词器
from tokenizers import BertWordPieceTokenizer
tokenizer = BertWordPieceTokenizer("bert-base-uncased-vocab.txt", lowercase=True)
只要您已下载文件 bert-base-uncased-vocab.txt
并使用
wget https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-vocab.txt