LLM 课程文档

Normalization 和 预分词

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

Normalization 和 预分词

Ask a Question Open In Colab Open In Studio Lab

在我们更深入地研究 Transformer 模型中使用的三种最常见的子词分词算法(字节对编码 [BPE]、WordPiece 和 Unigram)之前,我们首先来看看每个分词器应用于文本的预处理。以下是分词流程中步骤的高级概述

The tokenization pipeline.

在将文本拆分为子词(根据其模型)之前,分词器执行两个步骤:Normalization预分词

Normalization

Normalization 步骤包括一些常规清理,例如删除不必要的空格、转换为小写和/或删除音调符号。如果您熟悉 Unicode normalization(例如 NFC 或 NFKC),这也是分词器可能应用的内容。

🤗 Transformers 的 tokenizer 有一个名为 backend_tokenizer 的属性,可以访问来自 🤗 Tokenizers 库的底层分词器

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
print(type(tokenizer.backend_tokenizer))
<class 'tokenizers.Tokenizer'>

tokenizer 对象的 normalizer 属性有一个 normalize_str() 方法,我们可以使用它来查看 normalization 是如何执行的

print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
'hello how are u?'

在本例中,由于我们选择了 bert-base-uncased 检查点,因此应用的 normalization 将文本转换为小写并删除了音调符号。

✏️ 试一试!bert-base-cased 检查点加载一个分词器,并将相同的示例传递给它。您可以看到 cased 和 uncased 版本的 tokenizer 之间的主要区别是什么?

预分词

正如我们将在接下来的章节中看到的,分词器不能仅在原始文本上进行训练。相反,我们首先需要将文本拆分成小的实体,例如单词。这就是预分词步骤的用武之地。正如我们在 第 2 章 中看到的,基于单词的分词器可以简单地将原始文本按照空格和标点符号拆分成单词。这些单词将是分词器在其训练期间可以学习的子词的边界。

要查看快速分词器如何执行预分词,我们可以使用 tokenizer 对象的 pre_tokenizer 属性的 pre_tokenize_str() 方法

tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")
[('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]

请注意,分词器是如何已经跟踪偏移量的,这就是它如何为我们提供我们在上一节中使用的偏移量映射。在这里,分词器忽略了两个空格,并将它们替换为一个空格,但 areyou 之间的偏移量跳跃是为了解释这一点。

由于我们正在使用 BERT 分词器,因此预分词涉及按空格和标点符号进行拆分。其他分词器可能对此步骤有不同的规则。例如,如果我们使用 GPT-2 分词器

tokenizer = AutoTokenizer.from_pretrained("gpt2")
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")

它也会按空格和标点符号拆分,但它会保留空格并将它们替换为 Ġ 符号,使其能够在我们解码令牌时恢复原始空格

[('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
 ('?', (19, 20))]

另请注意,与 BERT 分词器不同,此分词器不会忽略双空格。

对于最后一个示例,让我们看一下基于 SentencePiece 算法的 T5 分词器

tokenizer = AutoTokenizer.from_pretrained("t5-small")
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")
[('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]

与 GPT-2 分词器一样,这个分词器保留空格并将它们替换为特定令牌 (_),但 T5 分词器仅按空格拆分,而不按标点符号拆分。另请注意,它默认在句子的开头(在 Hello 之前)添加了一个空格,并忽略了 areyou 之间的双空格。

现在我们已经了解了一些不同的分词器如何处理文本,我们可以开始探索底层的算法本身。我们将首先快速了解广泛适用的 SentencePiece;然后,在接下来的三个部分中,我们将研究用于子词分词的三种主要算法是如何工作的。

SentencePiece

SentencePiece 是一种用于文本预处理的分词算法,您可以将其与我们在接下来的三个部分中将看到的任何模型一起使用。它将文本视为 Unicode 字符序列,并将空格替换为特殊字符 。与 Unigram 算法(参见第 7 节)结合使用,它甚至不需要预分词步骤,这对于不使用空格字符的语言(如中文或日语)非常有用。

SentencePiece 的另一个主要特点是可逆分词:由于没有对空格进行特殊处理,因此解码令牌只需将它们连接起来并将 _ 替换为空格即可完成 —— 这将产生 normalization 后的文本。正如我们之前看到的,BERT 分词器会删除重复的空格,因此其分词是不可逆的。

算法概述

在以下各节中,我们将深入探讨三种主要的子词分词算法:BPE(由 GPT-2 等使用)、WordPiece(例如 BERT 使用)和 Unigram(由 T5 等使用)。在我们开始之前,这里简要概述了它们各自的工作原理。如果您尚未理解,请在阅读完接下来的每个部分后随时返回此表。

模型 BPE WordPiece Unigram
训练 从小型词汇表开始,学习合并令牌的规则 从小型词汇表开始,学习合并令牌的规则 从大型词汇表开始,学习删除令牌的规则
训练步骤 合并与最常见对对应的令牌 合并与最佳分数对对应的令牌,该分数基于对的频率,优先考虑每个单独令牌频率较低的对 删除词汇表中所有将使整个语料库计算的损失最小化的令牌
学习内容 合并规则和词汇表 仅词汇表 每个令牌都有分数的词汇表
编码 将单词拆分为字符并应用训练期间学习的合并 找到从开头开始的最长子词,该子词在词汇表中,然后对其余单词执行相同的操作 使用训练期间学习的分数,找到最有可能拆分为令牌的拆分

现在让我们深入了解 BPE!

< > 在 GitHub 上更新