WordPiece 与 Unigram LM 都把子词切分当作语言模型问题:前者用似然增益替换 BPE 的频次排序来挑选合并对,后者干脆放弃合并、直接建模子词概率并用 Viterbi 求全局最优切分。

两条路线的分水岭

BPE 算法原理 的训练是自底向上增量的:从字符出发,每轮把出现次数最多的相邻对合并进词表。WordPiece 保留了这个骨架,只换掉打分函数;Unigram LM 则彻底换了范式——它自顶向下减量,先给一个过大的种子词表,再逐步剪掉”删掉它损失最小”的子词。

由此产生一个关键后果:BPE 与 WordPiece 的词表是有序的操作序列(merge 规则或匹配优先级),Unigram 的词表是带概率的无序集合。前者切分由局部贪心决定,后者由全局搜索决定。

WordPiece:用似然增益挑选合并对

训练目标

每轮考察所有相邻子词对 ,选择使 unigram 语言模型似然提升最大的一对。似然增益近似为

这正是点互信息(pointwise mutual information)的指数形式。相比 BPE 的 ,分母惩罚了本身就高频的组件:th 这类到处都出现的组合不会仅因为频次高就被优先合并,而 ##tion、##ing 这类构成高度专一的后缀会被提前收进词表。实践中 WordPiece 词表更贴近形态学边界,BPE 词表更贴近字符串统计。

编码:最长匹配与 前缀

WordPiece 的推理不重放 merge 序列(这是最常见的误解)。BERT 原始实现对每个预分词后的词做从左到右的最长匹配(MaxMatch):

def wordpiece_tokenize(word, vocab, unk="[UNK]"):
    """BERT 风格最长匹配:每步取词表中最长的前缀,词中子词带 ## 前缀。"""
    tokens, start = [], 0
    while start < len(word):
        end, cur = len(word), None
        while end > start:
            piece = word[start:end]
            if start > 0:
                piece = "##" + piece       # 非词首必须带续接标记
            if piece in vocab:
                cur = piece
                break
            end -= 1
        if cur is None:                     # 任一位置失配 → 整词判为 UNK
            return [unk]
        tokens.append(cur)
        start = end
    return tokens
 
vocab = {"un", "##aff", "##able", "##a", "##ff"}
print(wordpiece_tokenize("unaffable", vocab))   # ['un', '##aff', '##able']
print(wordpiece_tokenize("unaffably", vocab))   # ['[UNK]']  —— 尾部 'bly' 失配,整词丢弃

## 前缀让 play 与 ##play 成为两个不同 token,从而在无空格信息的 token 序列里保留词边界;解码时去掉 ## 直接拼接即可还原。代价是词首/词中同形子词占两份词表配额。

BERT 系的工程约定

  • 词表规模:英文 uncased BERT 为 30522,multilingual BERT 为 119547。
  • 覆盖失败的粒度是整词 UNK,不是字符级兜底——这与 BBPE 字节级分词 的零 OOV 保证形成鲜明对比,也是 WordPiece 在 Unicode 噪声文本上退化的根源。
  • 中文在预处理阶段被逐字加空格,等价于强制单字切分,词表里基本没有多字中文词,详见 中文与多语言分词。
  • [CLS]、[SEP]、[MASK] 等保留 token 手工占位,机制见 特殊 Token 与 Chat Template。

Unigram LM:先假设概率,再反推词表

概率化切分假设

给定词表 和概率 ,一个切分序列 的概率按独立同分布假设分解:

切分即搜索:在原句所有合法切分集合 中取

注意长度惩罚是隐式的——子词越多,累加的负对数越多,模型天然偏好用少量长子词覆盖,除非短子词概率显著更高。

Viterbi 解码

切分候选数随句长指数增长,但目标函数在字符格(lattice)上可分解,动态规划 即可求全局最优:

import math
 
# 子词 -> log 概率(真实场景由 EM 训练得到)
vocab = {"你": -3.2, "好": -3.5, "你好": -2.1, "世": -4.0, "界": -4.1, "世界": -2.4}
 
def viterbi(text, vocab, max_len=8):
    n = len(text)
    best = [-math.inf] * (n + 1)   # best[i]: text[:i] 的最优 log 概率
    back = [0] * (n + 1)           # back[i]: 最优切分中末个子词的起点
    best[0] = 0.0
    for i in range(1, n + 1):
        for j in range(max(0, i - max_len), i):
            piece = text[j:i]
            if piece in vocab and best[j] > -math.inf:
                s = best[j] + vocab[piece]
                if s > best[i]:
                    best[i], back[i] = s, j
    tokens, i = [], n
    while i > 0:                   # 回溯
        tokens.append(text[back[i]:i])
        i = back[i]
    return tokens[::-1], best[n]
 
print(viterbi("你好世界", vocab))   # (['你好', '世界'], -4.5)

单字符 token 必须常驻词表,否则格中可能出现断点导致无解。

减量式训练

  1. 种子词表:用 suffix array 或 BPE 取足量高频 substring,规模通常是目标词表的数倍。

  2. E 步:固定词表,用前向-后向算法在 lattice 上求各子词的期望出现次数,更新 。

  3. M 步:对每个子词计算 ——将其移出词表后整个语料边际似然的下降量:

  4. 剪枝:按 loss 降序保留 top (常取 80%),单字符豁免;回到第 2 步直到达到目标 vocab size。

因为剪枝准则是”对全语料似然的边际贡献”,Unigram 对目标词表大小的变化更鲁棒——同一份种子跑到 32k 和 64k,得到的是嵌套式的取舍结果,而非 BPE 那样受 merge 顺序牵连的路径依赖。这一点在 词表设计与裁剪 中做裁剪实验时尤其重要。

subword regularization

Unigram 天然支持从 中按 采样,或输出 n-best 切分。训练时每个 epoch 换一种切分,相当于对 tokenization 边界做数据增强,在低资源翻译上稳定提升 1 BLEU 以上。BPE 侧的对应物是 BPE-dropout(以概率随机跳过 merge),但它只能扰动而无法给出切分概率。

与 BPE 的本质差异

维度BPEWordPieceUnigram LM
词表构建方向增量合并增量合并减量剪枝
选择准则 最大似然增益 / PMI 最大删除后似然损失最小
词表载荷有序 merge 规则子词集合 + 匹配优先级子词集合 +
编码算法重放 merge(贪心)最长匹配(贪心)Viterbi(全局最优)
一句话多切分仅 BPE-dropout 扰动不支持原生采样 / n-best
词边界标记</w> 或 Ġ## 续接前缀▁ 前导空格符
OOV 行为回落字符整词 [UNK]回落字符
代表模型GPT-2、RoBERTaBERT、DistilBERT、ELECTRAT5、ALBERT、XLNet、mBART

关键是”贪心 vs 全局”:BPE 和 WordPiece 一旦在某个位置做出合并/匹配决策就不可回退,可能被局部高分片段带偏;Unigram 用 的可加性换来了动态规划的全局最优,代价是编码比查表匹配慢一个量级,且必须携带浮点概率。

选型

  • 复现 BERT 系或在其权重上继续训练,只能用 WordPiece——## 约定与 embedding 表绑定。
  • 从零训中文/多语言模型,优先 Unigram:无需依赖空格预分词,配合 SentencePiece 的 --model_type=unigram 直接吃原始文本,词表在形态丰富语言上压缩率更好。
  • 需要 tokenization 层面的数据增强(低资源、噪声输入鲁棒性),Unigram 的采样切分是唯一开箱可用的选项。
  • 追求最高编码吞吐、或要与现成 GPT 词表对齐,仍然选 BPE 系。三者在 Hugging Face Tokenizers 实操 中共用同一套 normalizer / pre-tokenizer 管线,替换 model 组件即可横向对比。