在深入探讨Transformer模型中常用的三种子词分词算法(Byte-Pair Encoding [BPE]、WordPiece和Unigram)之前,我们先来看看每个分词器对文本进行预处理的步骤。这是分词流程的高级概述:
在将文本分割成子词(根据模型需求)之前,分词器会执行两个步骤:归一化和预分词。
Normalization
归一化步骤包括一些通用的清理工作,如删除不必要的空格、小写转换以及移除重音符号。如果你了解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
模型加载一个分词器,将同样的例子传递给它。你能看到cased和uncased版本的分词器之间有什么主要区别吗?
Pre-tokenization
如接下来的章节所述,分词器不能直接对原始文本进行训练。相反,我们首先需要将文本分割成小片段,如单词。这就是预分词步骤的作用。如我们在第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分词器只在空格上进行分割,不考虑标点。此外,它默认在句子开头添加了一个空格(在“你好”之前),并忽略了“是”和“你”之间的双空格。
现在我们已经了解了一些不同分词器处理文本的方式,我们可以开始探索这些算法本身了。首先,我们快速了解一下广泛适用的SentencePiece;接下来的三个部分,我们将分别探讨三种主要的子词分割算法:BPE(GPT-2和其他模型使用)、WordPiece(例如BERT使用的)和Unigram(T5和其他模型使用的)。在开始之前,这里有一个快速概述,如果你在阅读完每个部分后仍然不明白,可以随时返回这里。
SentencePiece
SentencePiece 是一种用于文本预处理的分词算法,适用于接下来三个部分中我们将要看到的任何模型。它将文本视为Unicode字符的序列,并用特殊字符▁
替换空格。当与Unigram算法(参见第7节)结合使用时,甚至不需要预分词步骤,这对于不使用空格的语种(如中文或日语)非常有用。
SentencePiece的主要特点是可逆分词:由于不特别处理空格,解码令牌只需简单地将它们连接起来,然后将_
替换为空格,这将得到规范化文本。如前所述,BERT分词器会移除重复的空格,因此其分词不可逆。
Algorithm overview
接下来,我们将深入研究三种主要的子词分词算法:BPE(GPT-2和其他模型使用)、WordPiece(如BERT所用)和Unigram(T5和其他模型使用)。在开始之前,这里简要概述它们的工作原理。如果在阅读完每个后续部分后仍然不明白,可以随时返回此表。
模型 | BPE | WordPiece | Unigram |
---|---|---|---|
训练 | 从一个小词汇表开始,学习合并令牌的规则 | 从一个小词汇表开始,学习合并令牌的规则 | 从一个大词汇表开始,学习移除令牌的规则 |
训练步骤 | 合并最常见对的令牌 | 合并频率最高的对的令牌,优先考虑每个令牌频率较低的对,以提高整体频率 | 移除词汇表中所有最小化整个语料库损失的令牌 |
学习内容 | 合并规则和词汇表 | 只有词汇表 | 每个令牌的分数的词汇表 |
编码 | 将单词分割成字符,然后应用训练中学到的合并规则 | 从开头开始查找最长的在词汇表中的子词,然后对剩余部分做同样的操作 | 找到最可能的分割成令牌的方式,使用训练中学到的分数 |
现在我们开始深入了解BPE!