BPE的训练和解码范围都是一个词的范围。
###BPE训练过程:
首先将词分成一个一个的字符,然后在词的范围内统计字符对出现的次数,每次将次数最多的字符对保存起来,直到循环次数结束。
###BPE编码
解码过程,经过训练过程,会得到codec文件,codec文件中保存的就是训练过程的字符对,文件中最开始的是训练时最先保存的字符,即具有较高的优先级。
解码是也是按在词的范围中进行编码的,首先将词拆成一个一个的字符,然后按照训练得到的codec文件中的字符对来合并。
比如我们想编码:
aaabdaaabac
我们会发现这里的aa出现的词数最高(我们这里只看两个字符的频率),那么用这里没有的字符Z来替代aa:
ZabdZabac
Z=aa
此时,又发现ab出现的频率最高,那么同样的,Y来代替ab:
ZYdZYac
Y=ab
Z=aa
同样的,ZY出现的频率大,我们用X来替代ZY:
XdXac
X=ZY
Y=ab
Z=aa
最后,连续两个字符的频率都为1了,也就结束了。就是这么简单。
解码的时候,就按照相反的顺序更新替换即可。
transformer[1]的subtokenizer以及bpe算法。
其中subtokenizer分为形成词汇表与处理训练文本两个部分。
形成词汇表部分,首先通过空格为间隔切分原始文本并统计词语与词频数,其次根据上个步骤的统计形成初始的每个单词或单字词汇表。通过迭代一定的次数不断更新词汇表。那么如何更新呢,主要采用如下几个步骤:
1、遍历当前的文本的每个词语,将每个词语切分,从最长长度即end开始,向前遍历这个词语的每个位置,找到最长的在词典中的词,不断切割,直至该词切割结束,如我是小白将会被切分成我、是、小白。然后进一步更新新的词语与词频,如以我开始,则有我1,我是1,我是小白1,以是开始,是1,是小白1,以小白开始,则是小白1。
2、基于新的词语与词频,从最长的词语开始,如果词频大于一定的阈值,则加入新的词汇表,否则不加。并将与之相关的词频减少,如‘translate’加入词汇表,则对应的’t’,‘tr’…对应词频减少。一直遍历到最短的词语。
处理训练文本部分,将原文本按空格划分,对每一行的每个词语进一步切分,每次找出在词表中最长的子词。切分完后进行编码即可。
BPE算法基于论文[2],以及开源代码[3],该算法具体执行过程如下:
1、learn bpe
首先根据空格切分原训练语料,因此需要先分词,以统计词语与词频。将每个词语变成单个词如’word’变为{w,o,r,d</w>},根据词频进行排序,形成{(w,o,r,d):1}类似的词语词频字典。
将排序字典中每个key进行前后组后,统计词频,如{(w,o,r,d):1}则变为{wo:1,or:1,rd:1}。生成词语词频统计结果。
基于指定生成bpe的数目,每次选择频率出现最高的词语,将词语词频字典中对应的两个词合并成一个词,如‘a’’b’出现的合并为’ab’, 并将其写入bpe codes里。并将与之相关的统计结果进行更新,如‘a b c b’,如果ab合并,则对应的’b c’统计次数减少。最后将‘ab’的统计次数置为0。不断循环迭代,直至生成指定的bpe codes数。
2、apply bpe
此次apply bpe的作用是为了生成词汇表,如果词汇表已经生成,则忽略此步。
上一步生成的bpecodes默认以权重进行排序,权重大的在前面。
将原始语料每一行读入,以空格切分,形成每个词组。对词组中的每个词得到对应的pair,如word则形成[wo,or,rd]。每次在这个word pairs选择在bpe codes权重最大的pair,组合并形成新的word,如果最终pair数为1即没有合并的可能或者最大的pair也不再bpe_codes中,则停止循环。如{我爱祖国}则变为{我爱 祖国}。由于我和爱原词后面仍接着词,因此最终变为{我@@ 爱@@ 祖国}。以区别{祖国@,爱@,我},最终我和我@是不同的词汇。
3、将apply bpe的结果按空格划分形成词汇表,包含词语与词频。
4、将原语料进一步使用apply bpe,此次可以结合词汇表,可以设置一个阈值,不使用低频词,以减少低词频影响。因为过滤掉低词频,可能会出现oov问题,如出现oov问题,则将原词切分为更小的词。我觉得它和第二步有点重复,不知理解是否错误。
以上就是使用transformer过程中对子词切分一点总结。
[1] https://github.com/tensorflow/models
[2]Neural Machine Translation of Rare Words with Subword Units
[3]https://github.com/rsennrich/subword-nmt