2、常见Tokenization算法:BPE、WordPiece、SentencePiece
好,咱们直接进入正题。Tokenization(分词)是文本预处理最基础的一步,但选错算法后面全白搭。我自己踩过不少坑,比如拿BPE跑中文数据,出来的词片断全是单字,模型压根学不到语义。这一章就把三种主流算法掰开揉碎讲清楚:BPE、WordPiece、SentencePiece。你想想看,它们到底差在哪?各自用在什么场景?我先说结论:没有万能算法,只有合适的选择。
2.1 BPE — 字节对编码
BPE(Byte Pair Encoding)最早是数据压缩用的,2016年被Neural Machine Translation领域拿来搞分词。原理特别简单:反复合并频率最高的相邻字符对。举个例子:
初始词表: a, b, c, d, e, f, g, h, i, j
统计频次: ("a","b")=10, ("b","c")=8, ("c","d")=7 ...
第一次合并: "ab" 成为新符号,词表扩增
重复直到词表容量达到预设值
我最早在机器翻译项目里用BPE处理英文。跑出来效果不错,但遇到德语这种超长复合词,BPE会切得很碎。后来我发现,BPE擅长处理形态丰富的语言——因为它不关心语义,只看统计频次。说白了,BPE就是“无脑高频优先”。
2.2 WordPiece — 词片分割
WordPiece是Google搞的,用在BERT等模型里。思路和BPE类似,但合并依据不一样:BPE看的是原始频次,WordPiece看的是互信息——也就是两个符号同时出现的概率比单独出现的概率乘积大多少。公式长这样:
score(a, b) = P(a,b) / (P(a) * P(b))
分数越高说明a和b关系越紧密。WordPiece每次只合并得分最高的符号对。嗯,这里要注意:WordPiece有个特殊标记##,用来表示这不是单词开头。比如"tokenization"会被切成["", "##ization"]。我在做BERT微调时见过不少同学忘记处理##,导致序列对齐出错。
| 特性 | BPE | WordPiece |
|---|---|---|
| 合并依据 | 原始字符对频次 | 互信息(似然比) |
| 特殊标记 | 无 | ## 表示连续片断 |
| 典型应用 | , RoBERTa, T5 | BERT, DistilBERT |
| 中文适用性 | 差(需预分词) | 中等(配合词表) |
2.3 SentencePiece — 统一处理所有语言
SentencePiece是Google开源的,2018年发布。它最大的特点:把空格也当成普通字符处理,不再依赖预分词。你喂它原始字符串,它自己决定单词边界。这一点对中文、日文这类无空格分隔的语言来说,简直是救星。
SentencePiece有两种模式:
- Unigram语言模型: 用EM算法(期望最大化)逐步剪枝,保留概率最高的子词。
- BPE模式: 直接套用BPE逻辑,但空格被当作普通符。
我个人习惯优先用Unigram模式,因为它能生成多个候选序列,再选最优路径,比BPE的贪心合并更灵活。我记得在给东南亚小语种做机器翻译时,当地语言没有标准分词工具,SentencePiece直接上,效果出奇好。
# 训练SentencePiece模型(Python示例)
import sentencepiece as spm
spm.SentencePieceTrainer.train(
input='my_corpus.txt',
model_prefix='my_tokenizer',
vocab_size=32000,
character_coverage=1.0, # 覆盖所有字符
model_type='unigram', # 可选 bpe 或 unigram
unk_id=0, bos_id=1, eos_id=2, pad_id=3
)
2.4 三种算法对比与选型建议
我把三种算法放在一张表里,你可以直观对比:
| 维度 | BPE | WordPiece | SentencePiece |
|---|---|---|---|
| 是否依赖预分词 | 是 | 是 | 否(空格视为普通符) |
| 合并策略 | 贪心高频 | 互信息最大 | Unigram剪枝 / BPE |
| 输出可逆性 | 是(可还原) | 是 | 是(有损——空格丢失) |
| 典型模型 | , T5 | BERT, ALBERT | XLNet, T5(部分) |
| 中文友好度 | ★☆☆ | ★★☆ | ★★★ |
| 训练速度 | 快 | 中 | 较慢(Unigram需多轮) |
你看完表格可能会问:那我到底选哪个?我的原则很简单:
- 纯英文或拉丁语系: BPE够用,速度快,词表稳定。
- 多语言、中文、日语: 无脑上SentencePiece(Unigram模式)。
- 使用预训练BERT系列: 保持原始WordPiece词表,不要自己重新训练——除非你有大量领域数据。
2.5 核心知识图谱
下面这张SVG图帮你理清三种算法的演进关系和关键区别。你可以把它当作快速记忆卡。
2.6 落地实战中的注意事项
讲完理论,我们聊聊落地时的几个坑。我这些年踩过的雷不算少,分享出来你们少走弯路。
- 词表大小如何选? 太小导致UNK太多,太大导致模型参数量爆炸。我一般32K起步,看语料规模上浮。如果是通用语料,50K已经很稳定。
- 一定要做Byte Fallback: 细看SentencePiece或BPE的配置,开启byte_fallback选项。这样遇到生僻字符会转成UTF-8字节序列,而不是UNK。我在处理多语言语料时测试过,UNK率从5%降到0.1%以下。
- 一致性校验: 训练和推理必须用完全相同的tokenizer文件。我曾经手滑用了两个版本,结果线上模型编码混乱,排查了两天才发现问题。
- 中文的先决条件: 如果选BPE或WordPiece,务必先用预分词(如Jieba、LTP)切好。否则你会得到一堆单字。SentencePiece则不需要这一步,但要注意把“▁”(空格符)正确映射。