技术博客

WordPiece 与 SentencePiece 的差异:似然合并准则与语言无关分词框架

第 34 / 147 篇 · 2026-06-29 · ai · tokenizerWordPieceSentencePiece

网上对比 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 是容器"这一定位。

WordPiece 改打分公式,SentencePiece 改系统架构 WordPiece(BERT 系):换一个合并打分公式 正则预切分 依赖英文词假设 似然打分合并 freq(ab) / (freq(a)·freq(b)) 词表带 ## 前缀 标记词中续接位置 打分与 BPE 的差别: 除以两个成分的频率, 惩罚"假粘连" SentencePiece(LLaMA / T5 系):重写整个分词系统 NFKC 归一化 全半角字符统一 ▁ 编码空白 连续空格折叠 BPE / Unigram 模型可二选一 可逆解码 还原原始字符串 一句话:WordPiece 是准则,SentencePiece 是容器;LLaMA 用的其实是装在容器里的 BPE

一个容易踩的坑: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"的讨论,先看它问的是哪一层。