前言
在深入探讨 Transformer 模型中常用的三种最常见的子词分词算法(Byte-Pair Encoding [BPE],WordPiece 和 Unigram)之前,我们首先来看看每个分词器对文本应用的预处理步骤。以下是分词管道中步骤的高级概述:
在将文本分解为子词(根据其模型)之前,分词器执行两个步骤:归一化和预分词。
src link: https://huggingface.co/learn/nlp-course/chapter6/4
Operating System: Ubuntu 22.04.4 LTS
参考文档
归一化
在规范化步骤中,会进行一些基本的清理工作,包括移除多余的空格、转换成小写字母以及去除音标。如果你对Unicode规范化(例如NFC或NFKC)有所了解,那么你可能会知道这是分词器可能会应用的一个过程。
🤗 Transformers 分词器的 backend_tokenizer
属性提供了对底层 🤗 Tokenizers 库分词器的访问。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
print(type(tokenizer.backend_tokenizer))
<class 'tokenizers.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章中看到的,基于单词的分词器可以简单地将原始文本在空白字符和标点符号处分割成单词。这些单词将是分词器在训练期间可以学习的子令牌的边界。
为了了解快速分词器如何执行预分词,我们可以使用分词器对象的预分词器属性中的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的另一个主要特点是可逆分词:由于对空格没有特殊处理,解码令牌只需将它们连接起来,并将_s替换为空格——这就得到了归一化文本。正如我们之前所看到的,BERT分词器会移除重复的空格,因此其分词是不可逆的。
算法概览
在接下来的部分中,我们将深入探讨三种主要的子词分词算法:BPE(被GPT-2和其他模型使用)、WordPiece(例如被BERT使用)和Unigram(被T5和其他模型使用)。在我们开始之前,这里有一个关于它们各自如何工作的快速概览。如果你还没有完全理解,不要犹豫,在阅读接下来的每个部分后回到这个表格。
Model | BPE | WordPiece | Unigram |
---|---|---|---|
Training | Starts from a small vocabulary and learns rules to merge tokens | Starts from a small vocabulary and learns rules to merge tokens | Starts from a large vocabulary and learns rules to remove tokens |
Training step | Merges the tokens corresponding to the most common pair | Merges the tokens corresponding to the pair with the best score based on the frequency of the pair, privileging pairs where each individual token is less frequent | Removes all the tokens in the vocabulary that will minimize the loss computed on the whole corpus |
Learns | Merge rules and a vocabulary | Just a vocabulary | A vocabulary with a score for each token |
Encoding | Splits a word into characters and applies the merges learned during training | Finds the longest subword starting from the beginning that is in the vocabulary, then does the same for the rest of the word | Finds the most likely split into tokens, using the scores learned during training |
现在让我们深入探讨BPE!
结语
第二百六十三篇博文写完,开心!!!!
今天,也是充满希望的一天。