tokenize:关于 NLP 中的 tokenize 总结

Tokenize

关于 tokenize 的总结,翻译自Summary of Tokenize

Subword tokenization

transformers 使用混合tokenization:Word-Level 和 Character-Level

Subword tokennizaiton算法基于这样一个原则,即经常使用的词不应该被分割成更小的子词,而稀有词应该被分解成有意义的子词。

对于BertTokenizer:

from transformers import BertTokenizertokenizer = BertTokenizer.from_pretrained("bert-base-uncased")print(tokenizer.tokenize("I have a new GPU!"))['i', 'have', 'a', 'new', 'gp', '##u', '!']

前边的词都存在字表中,GPU 没有存在常用字表中,所以被拆分成gp 和 ##u, “##”表示token的其余部分应连接到前一个token,与字母之间是相连的(用于解码或反转令牌化).

对于 XLNetTokenizer:

from transformers import XLNetTokenizertokenizer = XLNetTokenizer.from_pretrained("xlnet-base-cased")tokenizer.tokenize("Don't you love 🤗 Transformers? We sure do.")["▁Don", "'", "t", "▁you", "▁love", "▁", "🤗", "▁", "Transform", "ers", "?", "▁We", "▁sure", "▁do", "."]

可以看到 Transformers 被切分成 Transform 和 ers.

BPE(Byte-Pair Encoding)

BPE 依赖于将训练数据拆分为单词的 Pretokenization。Pretokenization 可以使用空格作为分隔符。例如在 GPT-2,Roberta 。更高级的 Pretokenization 可以使用基于规则的,例如 XLM,FlauBERT 。在大多数语言中使用Moses,或者使用 Spacy 和 ftfy , 计算训练语料库中每个单词的频率

在 pre-tokenization 之后,创建了一组独特的单词,并确定了每个单词在训练数据中出现的频率。接下来,BPE创建一个基本词汇表,该词汇表由唯一单词集中出现的所有符号组成,并学习合并规则,从基本词汇表的两个符号中形成一个新符号。直到词汇达到所需的词汇量为止。请注意,所需的词汇表大小是一个超参数,需要在训练标记器之前定义。

例如,假设在 pre-tokenization 之后,已经确定了以下一组单词,包括它们的频率:

("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)

因此,基本词汇是[“b”、“g”、“h”、“n”、“p”、“s”、“u”]。将所有单词拆分为基本词汇的符号,我们得到:

("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)

然后,BPE统计每个可能的符号对的频率,并选择最频繁出现的符号对。在上述示例中,“h”后跟“u”出现 10+5=15次 (10次出现 “hug” 时出现10次,5次出现“hugs”时出现5次)。然而,最常见的符号对是“u”,其次是“g”,总共出现10+5+5=20次。因此,tokenzier 学习的第一个合并规则是将所有“u”符号和一个“g”符号组合在一起。接下来,在词汇表中添加“ug”。然后,这组单词就变成了:

("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)

然后,BPE识别下一个最常见的符号对。它是“u”后跟“n”,出现16次。“u”、“n”合并为“un”,并添加到词汇表中。第二个最常见的符号对是“h”,其次是“ug”,出现15次。两个再次合并,可以在词汇表中添加“hug”。在这个阶段,词汇表是[“b”、“g”、“h”、“n”、“p”、“s”、“u”、“ug”、“un”、“hug”],我们的一组独特词汇表示为:

("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)

假设 Byte-Pair Encoding 训练将在此时停止,那么学习的合并规则将应用于新词(只要这些新词不包含不在基本词汇表中的符号)。例如,单词 “bug” 将被标记为 [“b”,“ug”],但 “mug” 将被标记为 [“”,“ug”],因为符号 “m” 不在基本词汇表中。 一般来说,诸如 “m” 之类的单个字母不会被 “” 符号取代,因为训练数据通常至少包含每个字母的一次出现,但对于表情符号等非常特殊的字符很可能会出现这种情况。

如前所述,词表大小(即基本词表+合并次数)是一个可供选择的超参数。例如,GPT的词汇量为40478,因为它们有478个基本字符,并且在40000次合并后选择停止训练。

Byte-level BPE

如果将所有unicode字符视为基本字符,则包含所有可能的基本字符的词表可能会非常大。为了获得更好的基本词汇表,GPT-2使用字节作为基本词汇表,这是一个巧妙的技巧,可以强制基本词汇表的大小为256,同时确保每个基本字符都包含在词汇表中。通过一些额外的规则来处理标点符号,GPT-2的 tokenizer可以标记每个文本,而不需要符号。GPT-2的词汇表大小为50257,对应于256字节的基本标记,一个特殊的文本结束标记和通过50000次合并学习的字符。

WordPiece(Character-Level BPE)

WordPiece 是用于 BERT、DistilBERT 和 Electra 的 subword tokenization 算法。WordPiece 首先初始化词汇表,以包含训练数据中的每个字符,并逐步学习给定数量的合并规则。与 BPE 相反,WordPiece 并没有选择最大频繁的符号对,而是选择一个一旦将训练数据添加到词汇表中,就可以最大化其可能性的字符对。

那么这到底意味着什么呢?参考前面的示例,最大化训练数据的可能性相当于找到字符对,其概率除以其第一个字符的概率,然后再除以其第二个字符的概率,在所有字符对中是最大的。只有当 “ug” 除以 “u”、“g” 的概率大于任何其他字符对时,“u” 后跟 “g” 才会合并。从直觉上看,WordPiece 与 BPE 略有不同,它通过合并两个字符来评估其损失,以确保其价值。

Unigram

与 BPE 或 WordPiece 不同,Unigram 将其基本词汇表初始化为大量字符,并逐步精简每个字符以获得较小的词汇表。例如,基本词汇表可以对应于所有预先标记的单词和最常见的子字符串。Unigram 不直接用于 transformers 中的任何模型,但它与 SentencePiece 一起使用。

在每个训练步骤中,Unigram 算法根据当前词汇表和 Unigram 语言模型定义训练数据的损失(通常定义为对数似然)。然后,对于词汇表中的每个符号,算法计算如果从词汇表中删除该字符,总体损失将增加多少。然后,Unigram 会删除丢失增量最低的字符中的p(p通常为10%或20% 。这些字符对训练数据的总体损失影响最小。重复这个过程,直到词汇达到所需的大小。Unigram 算法始终保留基本字符,以便任何单词都可以标记。

由于 Unigram 不基于合并规则(与BPE和WordPiece相反),该算法有几种在训练后标记新文本的方法。例如,如果一个经过培训的 Unigram 标记器展示了以下词汇:

["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"],

“hugs” 可以标记为[“hug”、“s”]、[“h”、“ug”、“s”]或[“h”、“u”、“g”、“s”]。那么选择哪一个呢?Unigram在保存词汇表的基础上保存训练语料库中每个 token 的概率,以便在训练后计算每个可能 tokenazition 的概率。该算法简单地选择了实践中最可能的标记化,但也提供了根据其概率对可能的 tokenazition 进行采样的可能性。

这些概率由标记器训练的损失定义。假设训练数据由单词
x 1 、 … 、 x N x_{1}、\dots、x_{N} x1​、…、xN​
组成 x 1 . , … , x N x_1.,…,x_N x1​.,…,xN​一个单词 x i x_{i} xi​的所有可能标记的集合我定义为 S ( x i ) S(x_{i}) S(xi​),则总损失定义为
L = − ∑ i = 1 N l o g ( ∑ x ∈ S ( x i ) p ( x ) ) L = -\sum_{i=1}^{N} log (\sum_{x \in S_(x_i)}p(x)) L=−i=1∑N​log(x∈S(​xi​)∑​p(x))

SentencePiece

迄今为止描述的所有 tokenization 算法都有相同的问题:假设输入文本使用空格分隔单词。然而,并非所有语言都使用空格来分隔单词。一种可能的解决方案是使用特定于语言的 Pre-tokenization ,例如:XLM使用 中文、日语、泰文 pre-tokenization ,为了解决这个问题 SentencePiece 将输入视为原始输入流,从而在要使用的字符集中包含空格。然后,它使用 BPE 或 unigram 算法来构造适当的词汇表。

例如,XLNetTokenizer 使用 SentencePiece,这也是为什么在前面的示例中 “▁“ 词汇表中包含字符。使用SentencePiece 进行解码非常容易,因为所有的标记都可以连接起来并 ”▁“ 被一个空格。

库中所有使用 SentencePiece 的 transformers 模型都将其与 unigram 结合使用。例如:ALBERT、XLNet、Marian、T5 。

总结

Tokenizer 的作用就是将文本映射成数字,以便于计算机处理。简单的来说,只需要通过空格分割就能获得英文的词,汉字的话就在字中间添加空格然后分割(中文BertTokenizer),对于中文来说这样会导致很多问题,因为中文的词表达的意思会更丰富,拆开之后会造成语义损失,后来百度的 ERNIE 对中文的做了改进。更高级一点的 Tokenizer 将统计学的方法运用到其中,以Subword 为基础构建词表,通过统计的方式,选择一种规则(见上文的描述),将词拆分成更小的单元,这样能够减少 UNK的出现,并且能够控制词表的大小。Unigram 与 sentencePiece 配合使用使得不使用空格作为分隔的语言也有了很好的处理方法。

相关推荐

相关文章