1. 前言
nlp领域目前已经发展到一个非常高的层次了,这个层次不仅仅是模型和数据方面,还涌现出了很多非常巧妙的trick,这篇文章就记录一下关于tokenization方面的工作。
所谓的tokenization其实就是将文本切分成words或者subwords,然后转成ids以便模型处理。最初的nlp分词非常简单,对于英语类以空格分割的语言来说,简单的以空格进行分割就行,不过这样简单的处理还存在一些问题,比如说*“Don’t you love 🤗 Transformers? We sure do.”,如果简单分割,那么就会得到[“Don’t”, “you”, “love”, “🤗”, “Transformers?”, “We”, “sure”, “do.”],对于“Transformers?”* 或者 “do.”这样的分词结果,显然是不合适,所以还需要考虑一下符号的影响,这可以通过现在很多开源工具实现,比如spaCy或者Moses等等。不过很明显会有个问题,那就是你切分的越细,得到的vocabulary越大,对于存储空间的要求也越大,虽然大部分情况下我们的词典都不会特别大,但是总是存在意外情况,尤其是Bert这类对于语料越大效果越好的模型的出现,更是加剧了这种情况。如果切分成words会导致内存问题,那么一个想法就是做character级别的tokenization,对于英文来说只有26个,那么算上大小写也只有52个,这可以大大缓解过大的embedding matrix问题,但是,character级别需要模型去从序列顺序中学习word的表示,模型在这方面显然不如直接切分word来的直观,所以这类方法会有性能方面的损失。在这种情况下,混合了word和character的一些新方法应运而出,这类方法被称为subword。
2.subword tokenization
2.1 Byte Pair Encoding
这种算法其实很简单,从其名字基本可以了解到一些东西,最显眼的就是pair这个词。它的做法是每次使用频次最高的一个字节对来替换这两个字节。
算法流程:
- 准备足够大的训练语料
- 确定期望的subword词表大小
- 将单词拆分为字符序列并在末尾添加后缀“ </ w>”(这里不是必须,可以添加任意可以代表结束的字符),统计单词频率。 本阶段的subword的粒度是字符。 例如,“ low”的频率为5,那么我们将其改写为“ l o w </ w>”:5
- 统计每一个连续字节对的出现频率,选择最高频者合并成新的subword
- 重复第4步直到达到第2步设定的subword词表大小或下一个最高频的字节对出现频率为1
2.1.1 构建subword字典
有输入的word和其频次,首先将word分成character,并将subword词典初始化为character:
{
'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3}
Iter 1, 最高频连续字节对"e"和"s"出现了6+3=9次,合并成"es",将"es"加入subword词典。输出:
{
'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w es t </w>': 6, 'w i d es t </w>': 3}
Iter 2, 最高频连续字节对"es"和"t"出现了6+3=9次, 合并成"est",并将"est"加入subword词典。输出:
{
'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est </w>': 6, 'w i d est </w>': 3}
Iter 3, 以此类推,最高频连续字节对为"est"和"</ w>" 输出,同样加入subword词典:
{
'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3}
……
Iter n, 继续迭代直到达到预设的subword词表大小或下一个最高频的字节对出现频率为1。
2.1.2 编码
根据2.1.1得到了subword的字典,对单词就可以进行编码了,将字典按照字符长度递减排列,然后依次遍历词典中的subword,如果单词包含了该subword,那么就用该subword加入单词的分解集合中,对剩余的字符进行迭代。这里将词典首先排序,便可以保证最先加入集合的subword是最后生成的。若最后还有一些字符序列无法在字典中找到,则替换为特殊的subword,比如*< unk >*.
同样举个例子, 已有排好序的词典:
[“errrr</w>”, “tain</w>”, “moun”, “est</w>”, “high”, “the</w>”, “a</w>”]
对单词"mountain</ w>",遍历词典,首先看到“tain</ w>”出现在了单词中,加入该subword,对剩余的"moun"继续便利,下一个subword “moun”匹配,加入,那么便得到了单词的分解结果为:[“moun”, “tain</ w>”]。
2.1.3 解码
将tokens拼接在一起即可,注意"< /w>"代表句子结束。
# 编码序列
[“the<