Tokenizers 文档

快速入门

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

快速入门

让我们快速了解一下 🤗 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 模型实例化一个:

Python
Rust
Node
from tokenizers import Tokenizer
from tokenizers.models import BPE
tokenizer = Tokenizer(BPE(unk_token="[UNK]"))

为了在 wikitext 文件上训练我们的分词器,我们将需要实例化一个 [trainer]{.title-ref},在本例中是 BpeTrainer

Python
Rust
Node
from tokenizers.trainers import BpeTrainer
trainer = BpeTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])

我们可以设置训练参数,如 vocab_sizemin_frequency(此处保留其默认值 30,000 和 0),但最重要的部分是给出我们计划稍后使用的 special_tokens(它们在训练期间根本不使用),以便将它们插入到词汇表中。

您编写特殊 tokens 列表的顺序很重要:此处 "[UNK]" 将获得 ID 0,"[CLS]" 将获得 ID 1,依此类推。

我们现在可以训练我们的分词器,但这不会是最优的。如果没有预分词器将我们的输入拆分为单词,我们可能会得到与多个单词重叠的 tokens:例如,我们可能会得到一个 "it is" token,因为这两个单词经常彼此相邻出现。使用预分词器将确保没有 token 比预分词器返回的单词更大。在这里,我们想要训练一个 subword BPE 分词器,我们将使用最简单的预分词器,通过在空格上拆分。

Python
Rust
Node
from tokenizers.pre_tokenizers import Whitespace
tokenizer.pre_tokenizer = Whitespace()

现在,我们可以直接调用 Tokenizer.train 方法,并提供我们要使用的任何文件列表

Python
Rust
Node
files = [f"data/wikitext-103-raw/wiki.{split}.raw" for split in ["test", "train", "valid"]]
tokenizer.train(files, trainer)

这应该只需要几秒钟即可在完整的 wikitext 数据集上训练我们的分词器!要将分词器保存在一个文件中,该文件包含其所有配置和词汇表,只需使用 Tokenizer.save 方法

Python
Rust
Node
tokenizer.save("data/tokenizer-wiki.json")

您可以使用 Tokenizer.from_file classmethod 从该文件重新加载您的分词器

Python
Rust
Node
tokenizer = Tokenizer.from_file("data/tokenizer-wiki.json")

使用分词器

现在我们已经训练了一个分词器,我们可以使用 Tokenizer.encode 方法将其用于任何我们想要的文本

Python
Rust
Node
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")

这会将分词器的完整流程应用于文本,返回一个 Encoding 对象。要了解有关此流程以及如何应用(或自定义)其各个部分的更多信息,请查看此页面

然后,此 Encoding 对象具有深度学习模型(或其他)所需的所有属性。tokens 属性包含文本在 tokens 中的分割

Python
Rust
Node
print(output.tokens)
# ["Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?"]

同样,ids 属性将包含每个 tokens 在分词器词汇表中的索引

Python
Rust
Node
print(output.ids)
# [27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35]

🤗 Tokenizers 库的一个重要功能是它带有完整的对齐跟踪,这意味着您始终可以找回原始句子中与给定 token 对应的部分。这些存储在 Encoding 对象的 offsets 属性中。例如,假设我们想要找回导致 "[UNK]" token 出现的原因,它是列表中索引为 9 的 token,我们可以只请求该索引处的偏移量

Python
Rust
Node
print(output.offsets[9])
# (26, 27)

这些是与原始句子中的表情符号对应的索引

Python
Rust
Node
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 方法

Python
Rust
Node
tokenizer.token_to_id("[SEP]")
# 2

以下是如何设置后处理以提供传统的 BERT 输入

Python
Rust
Node
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。

为了检查这是否正常工作,让我们尝试编码与之前相同的句子

Python
Rust
Node
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?", "[SEP]"]

要检查句子对的结果,我们只需将两个句子传递给 Tokenizer.encode

Python
Rust
Node
output = tokenizer.encode("Hello, y'all!", "How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "[SEP]", "How", "are", "you", "[UNK]", "?", "[SEP]"]

然后,您可以使用以下命令检查分配给每个 token 的类型 ID 是否正确

Python
Rust
Node
print(output.type_ids)
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]

如果您使用 Tokenizer.save 保存您的分词器,后处理器将随之保存。

批量编码多个句子

为了获得 🤗 Tokenizers 库的全部速度,最好使用 Tokenizer.encode_batch 方法按批处理文本

Python
Rust
Node
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])

输出然后是 Encoding 对象列表,就像我们之前看到的那样。您可以一起处理任意数量的文本,只要它适合内存即可。

要处理一批句子对,请将两个列表传递给 Tokenizer.encode_batch 方法:句子 A 列表和句子 B 列表

Python
Rust
Node
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)

Python
Rust
Node
tokenizer.enable_padding(pad_id=3, pad_token="[PAD]")

我们可以设置填充的 direction(默认为右侧)或给定的 length,如果我们想要将每个样本填充到该特定数字(此处我们将其保持未设置,以填充到最长文本的大小)。

Python
Rust
Node
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])
print(output[1].tokens)
# ["[CLS]", "How", "are", "you", "[UNK]", "?", "[SEP]", "[PAD]"]

在这种情况下,分词器生成的 attention mask 会将填充考虑在内

Python
Rust
Node
print(output[1].attention_mask)
# [1, 1, 1, 1, 1, 1, 1, 0]

预训练

Python
Rust
Node

使用预训练的分词器

您可以从 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
< > 更新 在 GitHub 上