规范化和预标记
在我们更深入地研究 Transformer 模型使用的三种最常见的子词标记化算法(字节对编码 [BPE]、WordPiece 和 Unigram)之前,我们将先看看每个标记器对文本进行的预处理。以下是标记化管道步骤的高级概述
在将文本拆分为子词(根据其模型)之前,标记器执行两个步骤:*规范化* 和 *预标记*。
规范化
规范化步骤包括一些常规清理,例如删除不必要的空格、小写化和/或删除重音符号。如果您熟悉 Unicode 规范化(例如 NFC 或 NFKC),标记器也可能会应用此步骤。
🤗 变压器 tokenizer
具有一个名为 backend_tokenizer
的属性,它提供了对 🤗 标记器库中底层标记器的访问。
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
检查点加载标记器,并将其传递给相同的示例。您在 cased 和 uncased 版本的标记器之间可以观察到哪些主要区别?
预标记
正如我们将在下一节中看到的那样,标记器不能仅在原始文本上进行训练。相反,我们首先需要将文本拆分为小的实体,例如单词。这就是预标记步骤发挥作用的地方。正如我们在 第 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!