目录
极简方法——空格分词(Space)
通过空格进行分词是最简单的分词方法,但是仅对英文而言,对于中文则可以使用逐字拆分进行分词
实现代码如下:
s = "very long corpus..."
words = s.split(" ") # Split over space
vocabulary = dict(enumerate(set(words))) # Map storing the word to it's corresponding id
优劣之处:如果词汇量少,这种方法可能效果不错,因为词典将存储给定语料中的每个单词(或特殊标记)。但是,像“cat”和“cats”这样的单词变体不会共享id(即标识符),即使它们的语义非常接近。
为了避免上述分词问题,如今的预训练语言模型的字典都采用了下图的子词方法进行分词和id化,子词/子标记扩展了上面的分词策略,进一步将一个单词分解为从数据中学习到的语言逻辑的子成分。
举例而言,如“cat”和“cats”这两个单词,cats的分词结果为 [cat,##s]。其中前缀**“##”**表示初始输入的子标记。这种分词方法可以在英语语料库中提取子标记,如“##ing”、“##ed”等。
优劣之处:这种利用“片段”组成的子标记分词方法总体上减少了词汇表的大小,并可以做到一定程度的词汇相似度,如“cat”和“cats”将有一定的联系。但不足的是,由于一个token将可能被分解为多个子token,模型的输入序列长度将会增加,产生输入序列长度的非线性复杂度的问题。
在所有的分词算法中,HuggingFace团队强调了基于Transformer的SOTA模型中所使用的分词算法:
基于给定语料的BPE实现代码如下:
命令行:pip install tokenizers
from tokenizers import Tokenizer
from tokenizers.decoders import ByteLevel as ByteLevelDecoder
from tokenizers.models import BPE
from tokenizers.normalizers import Lowercase, NFKC, Sequence
from tokenizers.pre_tokenizers import ByteLevel
from tokenizers.trainers import BpeTrainer
'''BPE'''
tokenizer = Tokenizer(BPE())
tokenizer.normalizer = Sequence([
NFKC(),
Lowercase()
])
tokenizer.pre_tokenizer = ByteLevel()
tokenizer.decoder = ByteLevelDecoder()
trainer = BpeTrainer(vocab_size=25000, show_progress=True, initial_alphabet=ByteLevel.alphabet())
tokenizer.train(trainer, ["big.txt"])
print("Trained vocab size: {}".format(tokenizer.get_vocab_size()))
tokenizer.model.save('.')
tokenizer.model = BPE('vocab.json', 'merges.txt')
encoding = tokenizer.encode("This is a simple input to be tokenized")
print("Encoded string: {}".format(encoding.tokens))
decoded = tokenizer.decode(encoding.ids)
print("Decoded string: {}".format(decoded))
实验效果如下
补充:即使基于中文的预训练模型,大部分我们看到的分词结果都是逐字进行分词,但对于中文也是有子标记的,如下图的bert_base_chinese的词汇表中的一部分中文子标记。
最后再来让我们学习一下SOTA的分词方法:BPE分词算法
BPE,(byte pair encoder)字节对编码,主要目的是为了数据压缩
简单例子解释:
训练过程:
注:整个训练过程以两个字符(或两个字符组)为拆分或组合的基本单位
首先,根据语料进行以字符为单位的拆分,然后按照字符对进行组合,并对所有组合的结果根据出现的频率进行排序,形成一份codec文件,排在第一位的是出现频率最高的子词,如下图的"e </w>",其中表示这个e是作为单词结尾的字符
之后,以where单词为例,具体过程如下图所示,按照两个字符进行拆分,然后查找上述的codec文件,逐对合并,优先合并出现频率靠前的字符对,如下图的85 319 9 15 1表示在该字符对在codec文件中的频率排名。
图片来源及参考: