目录
subword产生背景
- word-level模型导致严重的OOV,而character-level模型粒度又太小
- 过大的词典会带来两个问题:
- 稀疏问题: 某些词汇出现的频率很低,得不到充分的训练
- 计算量问题: 词典过大,也就意味着embedding过程的计算量会变大
- 将词划分成字词的形式,能够大大降低词典的大小。同时,未知词汇能以subword组合的形式表示出来,也能提升词典的表达能力。
- 例如:
- 训练集的词汇: old older oldest smart smarter smartest
- word-level 词典: old older oldest smart smarter smartest 长度为6
- subword-level 词典: old smart er est 长度为4
- 例如:
subword主流算法
- 分类依据:这几种算法的处理流程跟语言学没有太大的关系,单纯是统计学的解决思路
BPE
- 全称:byte pair encoding
- 算法实现:
- 核心组成:
- learn_bpe + apply_bep + get_vocab
- learn_bpe的流程:
- 准备足够大的训练语料
- 确定期望的subword词表大小
- 将单词拆分为字符序列并在末尾添加后缀“ </ w>”,统计单词频率。 本阶段的subword的粒度是字符。 例如,“ low”的频率为5,那么我们将其改写为“ l o w </ w>”:5
- 停止符"</w>"的意义在于表示subword是词后缀
- 统计每一个连续字节对的出现频率,选择最高频者合并成新的subword
- 重复第4步直到达到第2步设定的subword词表大小或下一个最高频的字节对出现频率为1
- apply_bpe及其逆过程:
- 在之前的算法中,我们已经得到了subword的词表,对该词表按照子词长度由大到小排序。编码时,对于每个单词,遍历排好序的子词词表寻找是否有token是当前单词的子字符串,如果有,则该token是表示单词的tokens之一。
- 我们从最长的token迭代到最短的token,尝试将每个单词中的子字符串替换为token。 最终,我们将迭代所有tokens,并将所有子字符串替换为tokens。 如果仍然有子字符串没被替换但所有token都已迭代完毕,则将剩余的子词替换为特殊token,如<unk>。
wordpiece
- 和BPE的核心区别:
- wordpiece是按token间的互信息来进行合并;
- BPE是按照token一同出现的频率来合并的。
- 学习步骤:(和BPE差不多)
- 准备语料,分解成最小单元,比如英文中26个字母加上各种符号,作为原始词表
- 利用上述语料训练语言模型
- 从所有可能的subword单元中选择加入语言模型后能最大程度地增加训练数据概率的单元作为新的单元
- 重复上步骤,直至词表大小达到设定值或概率增量低于某一阈值
unigram language model
- 以最大化句子的likelihood为目标来直接构建整个词表
- 由于词表较大,罗列各种组合的概率过于低效,所以需要用Viterbi算法
- 在给定词表的情况下:
- 计算每个token对应的概率;
- 找到一个句子最好的分词方式
- 没有词表的情况:
- 首先初始化一个很大的词表
- 可以用所有长度为1的token加上高频出现的ngram来作为起始词表。
- 重复以下步骤直到词表数量减少到预先设定的阈值:
- 保持词表不变,用EM算法来求解每个token的概率
- 对于每一个token,计算如果把这个token从词表中移除而导致的likelihood减少值,作为这个token的loss
- 按loss从大到小排序,保留前n%(原文中为80%)的token。
- 在给定词表的情况下:
bert 的分词
- BERT 源码中 tokenization.py 就是预处理进行分词的程序,主要有两个分词器:BasicTokenizer 和 WordpieceTokenizer,另外一个 FullTokenizer 是这两个的结合。
- 先进行 BasicTokenizer 得到一个分得比较粗的 token 列表,然后再对每个 token 进行一次 WordpieceTokenizer,得到最终的分词结果。
- 对于中文来说,一句话概括:BERT 采取的是「分字」,即每一个汉字都切开。
- BasicTokenizer
- BasicTokenizer(以下简称 BT)是一个初步的分词器。对于一个待分词字符串,流程大致就是转成 unicode -> 去除各种奇怪字符 -> 处理中文 -> 空格分词 -> 去除多余字符和标点分词 -> 再次空格分词,结束
- WordpieceTokenizer
- 按照从左到右的顺序,将一个词拆分成多个子词,每个子词尽可能长。 greedy longest-match-first algorithm,贪婪最长优先匹配算法。