网上对比 WordPiece 和 SentencePiece 的文章大多列一张"支持语言、是否开源"的表格,读完还是不清楚两者到底在哪一层较劲。实际它们根本不在同一层:WordPiece 是一个合并准则(用什么公式决定合并哪一对),SentencePiece 是一个分词框架(从归一化到解码的完整系统,内部可以装 BPE 或 Unigram 两种模型)。把这一点钉死,剩下的细节都有了归属。
WordPiece:把 BPE 的频率换成似然比
WordPiece 最早出现在 2012 年 Schuster 和 Nakajima 的日韩语语音搜索论文里,动机是切出比"字"更粗、比"词"更细的单元。BERT 把它带进了 NLP 主流。它和 BPE 的训练框架完全一样:初始化小词表,迭代地合并候选对,直到词表达标。唯一的区别在选哪一对。
推导一下它的打分公式。假设语料已经按当前词表切好,用一个 unigram 语言模型描述语料:$P(s)=\prod_i p(x_i)$,其中 $p(x)=\mathrm{freq}(x)/N$。现在考虑把相邻片段 $a,b$ 合并成 $ab$。语料中每处出现 ab 的地方,似然从 $p(a)\,p(b)$ 变成 $p(ab)$,单次出现的似然比是
$$ \frac{p(ab)}{p(a)\,p(b)} \propto \frac{\mathrm{freq}(ab)\cdot N}{\mathrm{freq}(a)\,\mathrm{freq}(b)}. $$
对整个语料取对数后,合并收益正比于
$$ \text{score}(a,b) = \frac{\mathrm{freq}(ab)}{\mathrm{freq}(a)\,\mathrm{freq}(b)}, $$
这就是 WordPiece 实现里那行除法。和 BPE 的 $\mathrm{freq}(ab)$ 相比,分母多除了两个成分各自的频率。这个差别有明确的直觉:两个各自超高频的片段相邻出现,可能只是偶然,不代表它们"粘"。t 和 h 都极其常见,它们高频相邻里有相当部分是 t 结尾的词后面正好接 th 开头的词;把分母换成两者的边缘频率后,这类"假粘连"会被压下去,反而是两个成分各自罕见、却稳定相邻的对(比如 ##ing 里的部分组合)得分突出。BERT 的词表里 ##ing、##ed、##tion 这类词缀条目占比很高,正是这个打分准则的痕迹——它是往语素方向偏的。
词表条目上的 ## 前缀是 WordPiece 的另一个记号设计:## 开头表示该 piece 不是词首,编码时用它来还原词边界。这个信息在解码时是冗余的(拼接即可),但在分析词表构成时很好用。
SentencePiece:语言无关、无损、端到端
SentencePiece(Kudo & Richardson, 2018)的贡献不在合并公式,而在它给分词定的三条工程纪律,每一条都直接踩在传统方案的痛处上。
第一,语言无关。 传统预切分正则隐含"空格是词边界"的假设,对中文、日文、泰文这种不(完全)用空格分词的语言直接失效。SentencePiece 的做法是把空格也变成一个普通字符,用 Unicode 区块元素符 ▁(U+2581)表示:输入前先把空格替换成 ▁,分词器在字符流上工作,不需要任何关于"词"的先验。编码结果里 ▁ 出现的位置就是原始空格的位置。
第二,可逆。 decode(encode(x)) == x 被当成硬性约束。这要求归一化(默认 NFKC,全半角、兼容字符统一)也纳入系统内部而不是丢给用户预处理,同时连续空格的折叠、▁ 与原字符的冲突都有明确定义。为什么值得?训练语料预处理通常是多道流水线的叠加,任何一步不可逆,模型学到的就是"被污染的分布",排查起来无比痛苦。把可逆性做成系统级保证,等于把整类 bug 在源头消灭。
第三,端到端训练。 归一化器、预分词器、模型(--model_type=bpe 或 unigram)、后处理器、解码器是一条串起来的管线,直接从原始语料训出完整分词器。LLaMA 系列用 SentencePiece 的 BPE,T5 和 ALBERT 用它的 Unigram——同一个框架,两种模型,正好印证了"SentencePiece 是容器"这一定位。
一个容易踩的坑:SentencePiece BPE 不是 byte-level BPE
SentencePiece 的 BPE 模式默认在 Unicode 字符序列上训练(外加 ▁),不是 GPT-2 那种 UTF-8 字节级 BPE。遇到词表外的字符,没有 byte fallback 的模型只能输出 <unk> 或报错——对多语言、emoji、生僻字场景这是个实打实的坑。打开 byte_fallback=True 后,模型会退而把未知字符按 UTF-8 字节拆成 <0xNN> 序列(XLM-R 就是这么做的),鲁棒性才追平字节级方案。
对比一下三个容易混淆的组合:BERT 的 WordPiece 从字符起步、英文正则预切分、无字节兜底,生僻字直接 unk;GPT-2 的 byte-level BPE 从字节起步,天然无 OOV,但多字节字符膨胀严重;SentencePiece + byte fallback 居中,框架统一、可逆、有兜底,代价是多一步 ▁ 与字节模式的适配。选型时先把"输入里会不会出现词表外的 Unicode"这个问题问清楚,答案直接决定能不能用不带 fallback 的方案。
小结
WordPiece 回答的是"下一对合并谁":用似然比 $p(ab)/(p(a)p(b))$ 替代 BPE 的裸频率,偏好真正粘连的子串,词表因此更偏语素。SentencePiece 回答的是"分词系统该怎么组织":空白编码、归一化内置、可逆解码、模型可插拔。一个是公式,一个是架构——下次再看到"某模型用 WordPiece 还是 SentencePiece"的讨论,先看它问的是哪一层。