Unigram Language Model 分词:跟 BPE 不一样的自底向上分词思路

如果你的印象里 NLP 的分词器只有 BPE 一种,那 Unigram 可能让你意外。它不靠合并、不靠贪心,而是把分词当成一道概率题来求解。我第一次看到这个思路时,有一种“原来还能这样”的感觉。

AI technology illustration

所谓“自底向上”,我理解是指在无监督语料上直接构建词表,不依赖外部知识。但注意,Unigram 的实际构建方向恰好和 BPE 相反:BPE 是从字符开始不断合并,Unigram 是从大词表开始不断剪枝。

在深度学习时代,我们不再手工分词,而是用子词(subword)解决开放词表问题。常见的有 BPE、WordPiece、Unigram 三种。BPE 和 WordPiece 本质是同一类——都是从小到大合并;只有 Unigram 比较另类。

BPE 是合并,Unigram 是剪枝

BPE(Byte Pair Encoding)的思路很简单:从字符表开始,每次统计相邻符号对的出现频率,把最高频的那一对合并成新符号,重复 N 次。词表就这么“合并”出来了。这个过程是自底向上的,也可以说是贪心的——只看眼下哪个 pair 最常出现。

Unigram 不这么做。它反过来,先准备一个巨大的候选词表(可能包含上万个子词),然后通过概率模型估计每个子词的贡献,把贡献最小的那些“剪掉”,重复剪到目标词表大小。

语言模型怎么用来分词?

Unigram 这个名词来自一元语言模型(unigram language model)。它给每个子词一个概率,假设一个词序列被切分成若干子词后,整个序列的似然等于所有子词概率的乘积。分词的目标,就是找到使这个乘积最大的切分。

举个例子,假如词表里有 “_我”、“_我们”、“_爱”……(下划线表示词首标记)。模型会对不同切分给出不同得分:

P("_我 爱 中国") = p(_我) × p(爱) × p(中国)
P("_我们 爱 中国") = p(_我们) × p(爱) × p(中国)

谁的概率乘积高,就选谁。这个“选最优”的过程用 Viterbi 算法完成——把每个子词看成一条路径,路径权重就是负对数概率,分词就是求最短路径。

Viterbi 到底怎么解?

假设句子有 n 个位置,每个位置 i 到 j(j>i)之间如果存在子词 w,就有一条边,权重为 -log p(w)。从位置 0 到 n 找长度最短的路径。标准的动态规划就可以,复杂度 O(n×L),L 是最大子词长度。这也是为什么 Unigram 分词可以做到实时。

词表不是拼出来的,是剪出来的

现在最关键的问题:那棵巨大的候选词表从哪来?怎么剪?Kudo 论文里给了一个迭代过程,我用自己的话说一遍:

  1. 先造一棵“森林”:把语料里出现的所有字符、所有频繁子串、甚至 BPE 生成的词表全部混进去,得到一个超大的种子词表。
  2. 用 EM 算法估计每个子词在当前语料上的最大似然概率。这一步可以看作给每个子词打分。
  3. 计算“剪掉某个子词会让总体似然损失多少”,这个损失就是它的价值指标。
  4. 按损失从小到大排序,删掉损失最小的那批子词,直到词表规模达到目标值。
  5. 回到第 2 步,重新估计概率,再剪。反复迭代,直到词表稳定成型。

这种做法的好处是,词表不再由“出现次数”这种局部统计决定,而是由它对整个语料的建模贡献决定。一个子词即使出现的多,如果它的存在不影响整体分词似然,照样可能被剪掉。

一张表说清 BPE 和 Unigram 的区别

维度 BPE Unigram
构建方向 字符开始,自底向上合并 大词表开始,自顶向下剪枝
词表来源 逐步合并高频 pair 按似然损失删除低贡献子词
分词方法 贪心最长匹配(按合并顺序) Viterbi 求最大概率切分
概率模型 无,纯统计频率 一元语言模型
支持采样 不能 可以(Subword Regularization)
典型实现 GPT、BERT 等 SentencePiece 默认

一个很直观的差别:BPE 的分词结果是确定的,同一个句子永远切成同一个序列;Unigram 的分词结果是概率性的,同一个句子可能有多种切分都有意义。

它还有一个 BPE 没有的绝活:采样

正因为 Unigram 给每个切分都赋予了概率,所以我们可以按这个概率分布去采样,而不是只挑概率最大的那个。这就是 Kudo 在同一篇论文里提出的 Subword Regularization。训练机器翻译或语言模型时,每次迭代用不同的分词结果喂给模型,相当于做了数据增强,模型的鲁棒性会好很多。BPE 做不到这一点,因为 BPE 只有一条确定的切分路径。

我在自己的项目里试过这个大招。同一句话,“机器学习人工智能”在 Unigram 下可能被切成“机器学习/人工智能”,也可能被切成“机器/学习/人工/智能”。训练时多样性就来了。SentencePiece 里只需要设置 nbest_sizealpha 两个参数:nbest_size 控制采样的候选数量,alpha 控制平滑度,值越大采样越随机。

spm.SentencePieceTrainer.train(input='corpus.txt', model_prefix='unigram', vocab_size=8000, model_type='unigram', character_coverage=0.9995)

我最初以为 Unigram 只是 BPE 的变体

说回我的误区。早期我对比各种 tokenizer,发现 BPE 的结果看起来更干净,Unigram 有时会切出一些奇怪的子词。我一度以为 Unigram 没用,直到理解了它的剪枝逻辑。后来当我试着用 SentencePiece 训练多语言模型时,Unigram 的优势才真正显示出来:它对原始文本的适配度很高,不需要像 BPE 那样预先处理空格。而且因为词表是概率剪出来的,即使词表缩得很小,它也能保持不错的分词质量。BPE 一旦词表缩到很小,就只能留下那几组高频合并,表达能力下降得很快。

Unigram 的边界在哪

没有万能的分词器。Unigram 也有自己的代价:训练过程比 BPE 慢得多,要反复跑 EM 和剪枝;种子词表的大小和构造方式对结果影响很大,需要调参。另外,Unigram 的一元独立性假设意味着它看不到更长的上下文,有些歧义切分它没法像 BPE 那样用“频率”来强行压住(虽然频率在这里也被建模成概率了)。

所以我的判断是:如果你需要采样型分词、多语言处理、或者对词表大小敏感,Unigram 是更优解;如果你的任务追求极致的确定性和训练速度,BPE 仍然很能打。两者不是谁取代谁的关系,而是在不同目标下的不同设计选择。

原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/784.html

(0)
上一篇 3小时前
下一篇 3小时前

相关推荐