跳至主要內容

BPE分词器

最后的开神-wkyc大约 5 分钟Token分词器强化学习

BPE分词器

字节对编码(Byte Pair Encoder,BPE),又称 digram coding 双字母组合编码,是一种数据压缩算法,用来在固定大小的词表中实现可变⻓度的子词。该算法简单有效,因而目前它是最流行的方法。

BPE 首先将词分成单个字符,然后依次用另一个字符替换频率最高的一对字符 ,直到循环次数结束。

1 分词算法

(1)准备语料库,确定期望的 merge 词表大小等参数。

(2)统计每个单词出现的频率。

(3)将语料库中所有单词拆分为单个字符,用所有单个字符建立最初的词典,并统计每个字符的频率。挑出频次最高的符号对 ,将新字符加入词表,然后将语料中所有该字符对融合(merge)。

注:新字符依然可以参与后续的 merge,有点类似哈夫曼树,BPE 实际上就是一种贪心算法 。

分词器示意图
图1.1 字节对算法流程

(4)重复遍历 2 和 3 操作,直到词表中单词数达到设定量或下一个最高频数为 1 ,如果已经达到设定量,其余的词汇直接丢弃。

2 一个示例

比如我们想编码:

aaabdaaabac aaabdaaabac

我们会发现这里的aa出现的词频最高(我们这里只看两个字符的频率),那么用这里没有的字符Z来替代aa:

ZabdZabac ZabdZabac

Z=aa Z=aa

此时,又发现ab出现的频率最高,那么同样的,Y来代替ab:

ZYdZYac ZYdZYac

Y=ab Y=ab

Z=aa Z=aa

同样的,ZY出现的频率大,我们用X来替代ZY:

XdXac XdXac

X=ZY X=ZY

Y=ab Y=ab

Z=aa Z=aa

最后,连续两个字符的频率都为1了,算法也就结束了。

3 GPT2tokenizer

GPT2tokenizer同时也是gpt3的tokenizer,它的词汇表由256个单字节符号+50000个merge词+1个<|endoftext|>组成。

我们需要知道,词汇表是一个键为字节串值为token_id的字典,编码的过程和构造merge词表的过程相差无几,唯一的区别是结束的条件不同,而解码的过程则就是编码的反向过程。

尽管词汇表里面已经包含所有的merge词,但是GPT2tokenizer还是需要一个merges.txt来记录所有对merge词对,从下面算法流程就能明白原因了。

3.1 训练

训练的步骤与前面所提到的BPE原始步骤基本一致,除了一个在GPT2论文中提到的一个额外限制。由于dog有很多变体“dog.”、“dog!”出现的频率非常高,但是它对语言建模而言是次优的,因此官方制定了一条限制——不能跨符号类别进行merge操作。在加入这个限制的BPE算法下GPT2tokenizer诞生了。

3.2 编码

(1)把所有字符通过utf-8规则转换成字节串。

(2)扫描所有2-gram,检索merges.txt,选择优先级最高的词对(在merges.txt中位置越靠前优先级越高),进行merge操作。

(3)循环第2步,直到某一轮扫描,所有2-gram都不是merge词对为止。

(4)对这个经过merge操作的新串,使用词汇表映射到token_id。

3.3 解码

(1)对所有token_id列表,使用键值互换的反向词汇表映射到一个字节串列表。

(2)合并这个字节串列表为一个字节串。

(3)使用utf-8规则将字节串解码为人类可以理解的自然语言字符串。

下面举例说明一下,解码的步骤。

首先下面是utf-8从字节解码到字符的规则。

(1)0xxxxxxx(0-7) 单独成字符

(2)10xxxxxx(8-B) 作为后缀字节

(3)110xxxxx(C-D) 有一个后缀字节

(4)1110xxxx(E) 有两个后缀字节

(5)1111xxxx(F) 有三个后缀字节

下面演示了从输入token序列[4399, 2572, 3461]到字符串的完整过程。

(1)[4399, 2572, 3461]

(2)[[2325, 168], [201, 234], [102, 129]]

(3)[[[101, 104], 168], [201, 234], [102, 129]]

(4)[101, 104, 168, 201, 234, 102, 129]

(5)\xc2\xa1\x65\xe6\x93\x84\x42

(6)[\xc2\xa1, \x65, \xe6\x93\x84, \x42]

(7)你a他4

大概过程就是token返回到字节,再根据字节高四位来唯一编码,比如\xc2高四位是c,那后面就有一位字节和他一起编码到字符。

3.4 总结

词汇表中有大量的英文单词,但也有很多光看词汇表看不出来是哪国语言的奇异符号,其实把它们通过utf-8规则解码到字符串我们才能发现,词汇表是包括了一些汉字,日文假名和其他国的一些高频词汇的。至于不在词汇表的字词,只能通过词汇表上的字节或字节串来“碎片”地表示了,这也就是BPE分词器解决OOV问题的一种思路。至于为什么英文单词那么多,因为BPE算法训练tokenizer的语料库以英文语料库为主。

值得注意的是,词汇表中“cat”前有没有空格是不算作同一个token的。其中有空格代表一个英文单词或者是一个英文单词前缀,而没有空格则代表了cat作为英文单词的中间片段或者后缀。