规范化和预标记化
在我们更深入地研究 Transformer 模型中使用的三种最常见的子词标记化算法(字节对编码 [BPE]、WordPiece 和 Unigram)之前,我们将首先了解每个标记器对文本应用的预处理。以下是标记化管道中步骤的高级概述
在根据其模型将文本拆分为子标记之前,标记器会执行两个步骤:规范化和预标记化。
规范化
规范化步骤涉及一些常规清理,例如去除多余的空格、小写化和/或去除重音符号。如果您熟悉Unicode 规范化(例如 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()
方法,我们可以使用它来查看规范化是如何执行的
print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
'hello how are u?'
在这个例子中,由于我们选择了 bert-base-uncased
检查点,因此应用的规范化进行了小写化并去除了重音符号。
✏️ 试试看!从 bert-base-cased
检查点加载标记器,并将相同的示例传递给它。您可以在标记器的区分大小写版本和非区分大小写版本之间看到哪些主要区别?
预标记化
正如我们将在接下来的章节中看到的,标记器不能仅在原始文本上进行训练。相反,我们首先需要将文本拆分为小的实体,例如单词。这就是预标记化步骤发挥作用的地方。正如我们在第 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))]
请注意,标记器已经在跟踪偏移量,这就是它如何能够为我们提供我们在上一节中使用的偏移量映射的原因。这里标记器忽略了两个空格并将它们替换为一个,但偏移量在 are
和 you
之间跳转以考虑这一点。
由于我们使用的是 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
之前)默认添加了一个空格,并忽略了 are
和 you
之间的双空格。
现在我们已经了解了一些不同的标记器如何处理文本,我们可以开始探索底层算法本身。我们将首先快速了解广泛适用的 SentencePiece;然后,在接下来的三个部分中,我们将研究用于子词标记化的三种主要算法的工作原理。
SentencePiece
SentencePiece 是一种用于文本预处理的标记化算法,您可以将其与我们将在接下来的三个部分中看到的任何模型一起使用。它将文本视为 Unicode 字符序列,并将空格替换为特殊字符
。与 Unigram 算法结合使用(参见第 7 节),它甚至不需要预标记化步骤,这对于不使用空格字符的语言(如中文或日语)非常有用。
SentencePiece 的另一个主要特征是可逆标记化:由于没有对空格进行特殊处理,因此解码标记只需将它们连接起来并将 _
替换为空格即可——这将导致规范化的文本。正如我们之前看到的,BERT 标记器会删除重复的空格,因此其标记化不可逆。
算法概述
在以下各节中,我们将深入探讨三种主要的子词标记化算法:BPE(例如 GPT-2 等使用)、WordPiece(例如 BERT 使用)和 Unigram(例如 T5 等使用)。在我们开始之前,这里简要概述了它们各自的工作原理。如果您还没有理解,请不要犹豫,在阅读完下一节后返回此表。
模型 | BPE | WordPiece | Unigram |
---|---|---|---|
训练 | 从一个小词汇表开始,学习合并标记的规则 | 从一个小词汇表开始,学习合并标记的规则 | 从一个大词汇表开始,学习删除标记的规则 |
训练步骤 | 合并对应于最常见对的标记 | 合并对应于得分最高的对的标记,该得分基于对的频率,优先考虑每个单独标记频率较低的对 | 删除词汇表中所有会最小化在整个语料库上计算的损失的标记 |
学习 | 合并规则和词汇表 | 仅词汇表 | 带有所关联分数的词汇表 |
编码 | 将单词拆分为字符,并应用训练期间学习的合并规则 | 从开头找到最长的子词,该子词位于词汇表中,然后对单词的其余部分执行相同的操作 | 找到最可能的标记拆分,使用训练期间学习的分数 |
现在让我们深入了解BPE!