WordPiece 分词算法

Google BERT 的核心分词技术


简介

WordPiece 是 Google 在 2016 年为了解决神经机器翻译问题提出的分词算法,后来因为成为了 BERT 的默认分词方案而彻底走红。自此之后,很多基于 BERT 的 Transformer 模型都复用了这种方法,比如 DistilBERT、MobileBERT、Funnel Transformers 和 MPNET。


初识 WordPiece

如果说 BPE 是单纯的统计学家(看数量),那么 WordPiece 就像是一个概率学家(看概率)。

核心思想

WordPiece 的核心目标:最大化训练数据的似然概率

与 BPE 的贪心合并策略不同,WordPiece 采用概率模型来选择如何合并子词。


WordPiece vs BPE:核心区别

维度 BPE WordPiece
合并逻辑 统计最频繁的字节对 最大化训练数据似然概率
选择标准 频率最高 最大化似然增量(PMI)
子词标记 词首加 Ġ (GPT 风格) 非词首加 ##
分词策略 贪心匹配 最长匹配
应用模型 GPT 系列, Llama, RoBERTa BERT 系列, DistilBERT, Electra
算法本质 纯粹的数据压缩 统计语言建模

核心公式:似然增量

WordPiece 的合并得分计算

在选择合并 c₁c₂ 生成 c₁c₂ 时,WordPiece 衡量的是:

1
score(c₁, c₂) = log P(c₁c₂) - [log P(c₁) + log P(c₂)]

其中:

  • P(c₁c₂) = 合并后 token 的频率 / 总频率
  • P(c₁) = c₁ 的频率 / 总频率
  • P(c₂) = c₂ 的频率 / 总频率

公式背后的逻辑

如果 c₁c₂ 经常在一起出现(分子大),且它们很少单独出现(分母小),分数就会很高。

直观例子

假设语料库里有这些词汇关系:

词汇组合 关系特征
“深度” + “学习” 经常成对出现,有意义
“的” + “学” 虽然 “的” 出现很多,但组合无意义

虽然 “的” 这个字出现次数极多,但 “的” 和 “学” 组合在一起的概率并不比它们随机碰上的概率高多少。而 “深度” 和 “学习” 一旦出现,往往是成对的,所以算法会优先合并 “深度学习”。

信息论视角:点互信息 (PMI)

合并得分公式可以重写为:

1
score(c₁, c₂) = log [P(c₁c₂) / (P(c₁) × P(c₂))]

这正是 点互信息(Pointwise Mutual Information, PMI)

PMI 越高,说明 c₁ 和 c₂ 越倾向于一起出现,而不是偶然相遇。

1
2
3
4
5
PMI(x, y) = log [联合概率 / (边际概率之积)]

高 PMI → x 和 y 有强烈的关联
低 PMI → x 和 y 几乎独立
负 PMI → x 和 y 互斥

为什么 BERT 选择了 WordPiece?

WordPiece 这种”互信息”式的合并方式,能够更有效地把有意义的词根和前缀提取出来。

示例: 对于 unhappy

算法 行为 结果
BPE 只因 “un” 后面经常跟 “h” 就合并 可能合并为 unh
WordPiece 判断 “un” 作为否定前缀的独立性 un + ##happy

WordPiece 能更敏锐地察觉到 unhappy 之间的独立性,而不仅仅是因为它们凑巧在一起出现得多。


WordPiece 算法实战演示

“光听不练永远无法掌握精髓”,让我们通过一个实际的案例来拆解 WordPiece 的核心逻辑。


1. 初始化:预分词与频次统计

假设我们的语料库有以下单词及其频次:

1
{"hug": 10, "pug": 5, "pun": 12, "bun": 4}

基础词表

1
h, u, g, p, n, b

添加 ## 标记

WordPiece 有个特殊规则:非词首的子词要加 ##

拆解结果:

原词 频次 拆解后
hug 10 h, ##u, ##g
pug 5 p, ##u, ##g
pun 12 p, ##u, ##n
bun 4 b, ##u, ##n

💡 ## 的作用: 标记子词是否为单词的延续部分

  • ## 前缀:单词开头
  • ## 前缀:单词中间或结尾

2. 统计相邻对的频率

从上面的拆解,我们可以统计相邻 token 对的频率:

相邻对统计表

相邻对 出现词 频次计算 总频次
##u, ##g hug, pug 10 + 5 15
##u, ##n pun, bun 12 + 4 16
h, ##u hug 10 10
p, ##u pug, pun 5 + 12 17
b, ##u bun 4 4

同时统计单个 token 的频次:

Token 频次计算 总频次
##u 10 + 5 + 12 + 4 31
##g 10 + 5 15
##n 12 + 4 16
h 10 10
p 5 + 12 17
b 4 4

总频次 = 10 + 5 + 12 + 4 = 31


3. 计算合并得分

现在我们来计算候选对的得分(使用 PMI 公式):

候选对 1:##u + ##g

1
2
3
4
5
6
7
P(##u, ##g) = 15 / 31 ≈ 0.484
P(##u) = 31 / 31 = 1.0
P(##g) = 15 / 31 ≈ 0.484

score = log(0.484) - log(1.0) - log(0.484)
≈ -0.726 - 0 - (-0.726)
= 0

候选对 2:##u + ##n

1
2
3
4
5
6
7
P(##u, ##n) = 16 / 31 ≈ 0.516
P(##u) = 31 / 31 = 1.0
P(##n) = 16 / 31 ≈ 0.516

score = log(0.516) - log(1.0) - log(0.516)
≈ -0.662 - 0 - (-0.662)
= 0

候选对 3:p + ##u

1
2
3
4
5
6
7
P(p, ##u) = 17 / 31 ≈ 0.548
P(p) = 17 / 31 ≈ 0.548
P(##u) = 31 / 31 = 1.0

score = log(0.548) - log(0.548) - log(1.0)
≈ -0.603 - (-0.603) - 0
= -0.603

选择得分最高的对进行合并。


迭代机制

WordPiece 会持续迭代,每一轮都执行以下标准化步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌─────────────────────────────────────────────────────────┐
│ WordPiece 迭代流程 │
├─────────────────────────────────────────────────────────┤
│ 1. 统计 (Count) │
│ 统计所有相邻 token 对的出现频率 │
│ │
│ 2. 计算得分 (Score) │
│ score = log P(pair) - log P(c₁) - log P(c₂) │
│ 选择得分最高的对子 │
│ │
│ 3. 合并 (Merge) │
│ 将选定的对子合并为新的 token │
│ 例如:##u + ##g → ##ug │
│ │
│ 4. 更新 (Update) │
│ • 将新 token 加入词表 │
│ • 更新语料库中的 token 表示 │
└─────────────────────────────────────────────────────────┘

停止条件

算法会反复执行上述迭代过程,直到触发以下任一条件:

条件 说明
达到预设词表上限 例如设置 vocab_size = 30000
达到迭代次数限制 手动指定合并操作执行 N 次
得分低于阈值 当最高得分低于某个阈值(如 1e-5)时停止

分词算法:Longest Word First

与训练过程不同,WordPiece 在对新句子进行分词时,采用的是从左到右的最长匹配策略

算法步骤

1
2
3
4
5
6
7
8
输入:单词 "unaffable"

1. 从左到右,寻找词表中能匹配的最长前缀
2. 匹配到 "un"
3. 剩下的部分是 "affable"
4. 在词表里找以 "##" 开头的最长匹配
5. 匹配到 "##affable"
6. 最终结果:["un", "##affable"]

举例说明

假设词表里有 ["un", "##affable", "##able"]

输入:unaffable

步骤 匹配 剩余 说明
1 un affable 最长前缀匹配
2 ##affable "" 最长 ## 前缀匹配
结果 ["un", "##affable"] ✅ 成功

处理未知词

如果中间有一段在词表中找不到,整个词就会被标记为 [UNK]

⚠️ 注意: WordPiece 实际使用的是最长匹配策略,而非训练时的概率最大化,这是为了分词效率。


WordPiece 与 BPE 分词对比

相同输入,不同输出

输入单词:unhappily

BPE 分词(贪心匹配)

1
2
3
4
5
6
7
8
9
10
词表:[u, un, unh, h, ha, hap, happ, happi, happily, ##ly, ##y]

过程:
1. 最长匹配前缀:unh
2. 剩余:appily
3. 最长匹配前缀:happ
4. 剩余:ily
5. 匹配:##ily

结果:unh happ ##ily

WordPiece 分词(最长匹配)

1
2
3
4
5
6
7
8
词表:[un, unh, happily, happy, ##h, ##app, ##ily, ##ly]

过程:
1. 最长匹配前缀:un
2. 剩余:happily
3. 匹配:##happily

结果:un ##happily

关键区别总结

算法 训练策略 分词策略 特点
BPE 最高频合并 贪心最长匹配 简单高效,局部最优
WordPiece 最高 PMI 合并 最长匹配 语义感知,训练时考虑统计依赖

WordPiece 的优缺点

优点

✅ 优势 说明
语义感知 基于 PMI 的合并能保留有意义的词根和前缀
稳定性好 在 BERT 等模型上表现优异
处理 OOV 通过子词组合解决未知词问题
减少无效合并 不会因为高频但无意义的字符对而合并

缺点

❌ 劣势 说明
计算复杂 每次合并需要计算所有候选对的得分
训练成本高 需要多轮迭代统计和概率计算
依赖预训练 需要大规模语料库训练词表
不透明 相比 BPE 的直观频率统计,概率模型更难理解

BERT 中的实际应用

BERT Base 词表配置

参数
词表大小 30,000
特殊标记 [PAD], [UNK], [CLS], [SEP], `

分词示例

输入文本

1
WordPiece is a tokenization algorithm

BERT 分词结果

1
["Word", "##Piece", "is", "a", "token", "##ization", "algorithm"]

Token ID 序列

1
[10234, 15968, 2003, 1037, 19204, 10631, 14213, 17953]

注意事项

⚠️ WordPiece 倾向于:

  • 将罕见词拆分为更小的子词
  • 保留常见完整词
  • 使用 ## 标记子词延续

三种分词算法全对比

特性 BPE WordPiece Unigram
训练方向 自底向上(字符→词) 自底向上 自顶向下(词→字符)
选择标准 频率 似然概率 (PMI) 损失降低
分词策略 贪心匹配 最长匹配 最短路径 (Viterbi)
典型应用 GPT 系列, Llama BERT 系列 T5, ALBERT
计算复杂度

当前趋势

主流大模型的选择

💡 现状: 目前主流的大模型(如 Llama 3Mistral)多回归到了 BPE(尤其是 Byte-level BPE)。

为什么回归 BPE?

原因 说明
多语言支持 Byte-level BPE 能处理任何语言的字符
鲁棒性 处理特殊符号、emoji 等更加稳定
简洁高效 实现简单,训练和推理速度快
社区生态 HuggingFace 等工具链对 BPE 支持更好

WordPiece 的定位

虽然 BPE 更流行,但 WordPiece 仍然是 BERT 系列(及其变体)的核心技术,在理解语义和保持词根完整性方面仍有独特优势。


参考链接

  1. https://arxiv.org/abs/1609.08144 - Japanese and Korean Voice Search (WordPiece 原始论文)
  2. https://arxiv.org/abs/1810.04805 - BERT: Pre-training of Deep Bidirectional Transformers
  3. https://huggingface.co/learn/llm-course/zh-CN/chapter6/6 - HuggingFace LLM Course

WordPiece 的本质

它不只是分词算法,更是连接统计语言模型与深度神经网络的桥梁。

用概率的眼光看语言,让模型真正理解”词”的意义。