一句话理解
WordPiece 是一种"把文字切成子词"的算法——切得比字大、比词小,让模型既能处理已知词,也能猜未知词。
但要注意:WordPiece 对英文及拉丁语系(法语、西班牙语、德语等)发挥核心作用,但对纯中文来说,它退化为逐字切分——子词优势主要体现在有空格分隔词边界的语言上。
方法 1:按"词"切分(整词分词)
词表:{我, 是, 一, 个, 好, 学生, ...}
问题:
├── 中文词边界不清("南京市长江大桥"怎么切?)
├── 词表爆炸(中文几十万个词,全收录不现实)
└── 未登录词(OOV):词表里没有的词 → [UNK] → 丢失信息
方法 2:按"字"切分(逐字分词)
每个字都是一个 token
优点:简单,没有未登录词
缺点:
├── 丢失了"词"的信息("海洋"和"海"+"洋"语义不同)
└── 序列太长,计算量大
WordPiece 的切法:介于"字"和"词"之间
常见词 → 整词保留: "海洋" → ["海洋"]
次常见 → 切成子词: "海洋监测" → ["海洋", "监测"]
罕见词 → 逐字切分: "龘" → ["龘"](词表里没有就拆成字)
好处:
├── 常见词保持完整(语义不丢失)
├── 罕见词可以拆分(不会 [UNK])
├── 词表大小可控(2-3 万个词条就够)
└── 兼顾了"字"的灵活和"词"的语义
WordPiece 和 BPE(Byte Pair Encoding)是最主流的两种子词算法,结构相似,但合并策略不同:
BPE(GPT 系列用的):
合并标准 = 出现频率最高
"play" + "##ing" 一起出现了 10000 次 → 合并!
类比:谁加班时间最长,谁升职
WordPiece(BERT 用的):
合并标准 = 统计关联度最高(PMI)
"play" + "##ing" 一起出现的次数 >> 各自独立出现的预期 → 合并!
类比:谁贡献最大,谁升职(不只看工时,还看产出)
PMI 衡量:两个 token "粘在一起"的程度,是否超出随机预期
score(A, B) = P(AB) / (P(A) × P(B))
P(AB) = AB 实际一起出现的概率
P(A) × P(B) = 如果 A 和 B 完全独立,它们碰巧在一起的预期概率
score > 1 → 超出预期,值得合并(有统计关联)
score = 1 → 刚好等于预期,合不合都行
score < 1 → 低于预期,不该合并
举例:
"的" 和 "了" 都是高频字,经常一起出现
→ 但它们各自太常见了,一起出现是"碰巧"
→ PMI 不高 → WordPiece 不会优先合并
"un" 和 "##able" 不常出现,但几乎总是一起出现
→ 关联度极高 → WordPiece 优先合并
## 前缀:词边界标记WordPiece 用 ## 区分"词的开头"和"词的延续":
没有 ## → 可以独立出现在词的开头
有 ## → 只能接在前一个 token 后面
例子:
"play" → 独立词,可以开头
"##ing" → 后缀,只能接在后面:play + ##ing = playing
"un" → 前缀,可以开头
"##der" → 延续:un + ##der = understand 的一部分
为什么需要 ##?
没有 ## 的话:
"ing" 出现在词表 → 模型不知道它是独立词还是后缀
→ 无法判断词边界
有了 ## 之后:
"ing" → 独立词
"##ing" → 一定是后缀
→ 模型清楚知道词的边界在哪里
还原原文:
分词结果:["play", "##ing", "a", "game"]
还原规则:
## 开头 → 直接拼到前一个 token 后面
没有 ## → 前面加空格
还原过程:play → playing → playing a → playing a game
第 1 步:初始化
把所有词拆成单个字符
"unbelievable" → ["u", "##n", "##b", "##e", "##l", "##i", "##e", "##v", "##a", "##b", "##l", "##e"]
词表 = 所有出现过的字符
第 2 步:迭代合并
计算所有相邻 token 对的 PMI 分数
选分数最高的那对 → 合并成一个新 token
更新语料中所有出现的地方
重复,直到词表达到目标大小(如 21,128)
第 3 步:输出词表
常见词 → 已经完整合并("海洋")
常见子词 → 部分合并("##ing")
罕见字 → 还是单个字符(兜底)
假设语料中有这些词:
Word Count
low 5
lower 2
newest 6
wider 3
初始化后:
low → [l, ##o, ##w]
lower → [l, ##o, ##w, ##e, ##r]
newest → [n, ##e, ##w, ##e, ##s, ##t]
wider → [w, ##i, ##d, ##e, ##r]
计算 PMI 分数:
(##e, ##r) 出现 5 次(lower×2 + wider×3)
score = 4.46 ← 高!因为 ##r 不常见,但总跟在 ##e 后面
(##e, ##w) 出现 6 次(newest×6)
score = 2.08 ← 低一些,因为 ##w 太常见了
WordPiece 选择合并 (##e, ##r) → ##er
因为 PMI 更高(关联度更强)
BPE 会选择合并 (##e, ##w)
因为出现次数更多(6 > 5)
用户输入:"海洋监测技术"
第 1 步:贪心匹配
从左到右,每次找词表里能匹配的最长子词
"海" → 词表里有 → 匹配
"洋" → 词表里有 → 匹配
"监" → 词表里有 → 匹配
"测" → 词表里有 → 匹配
"技" → 词表里有 → 匹配
"术" → 词表里有 → 匹配
第 2 步:处理未知
如果遇到词表里没有的字 → 逐字切分(兜底)
永远不会出现 [UNK](除非是完全没见过的字符)
第 3 步:输出
["海", "洋", "监", "测", "技", "术"]
→ 对应 ID → 送入模型
英文有空格分隔单词 → 天然知道词边界
"unbelievable" → ["un", "##believ", "##able"]
↑前缀 ↑词根延续 ↑后缀
WordPiece 在英文上效果很好:
常见词完整保留("the", "is")
复杂词合理拆分("un + ##believ + ##able")
中文没有空格 → 需要特殊处理
bert-base-chinese 的策略:
中文字逐字切分(每个字前后加空格)
→ 然后对每个字查词表
"海洋监测" → ["海", "洋", "监", "测"]
中文的 WordPiece 实际上退化为"逐字切分"
因为中文字本身就是最小的语义单元
## 前缀在中文里用得少(多数中文字独立出现)
对纯中文句子,WordPiece 的子词优势完全消失:
输入:"海洋监测技术"
WordPiece 切法:["海", "洋", "监", "测", "技", "术"] ← 6 个单字
逐字切分结果: ["海", "洋", "监", "测", "技", "术"] ← 完全一样!
原因:
├── normalizer 的 handle_chinese_chars 在中文字前后加空格
├── 预分词阶段就把中文字切开了
├── WordPiece 没有机会合并两个中文字
└── 词表中多字中文词条数 = 0(实证数据)
结论:
对纯中文 → WordPiece ≈ 逐字切分(没有子词优势)
对纯英文 → WordPiece 充分发挥(un + ##believ + ##able)
对中英混合 → WordPiece 在英文部分发挥作用,中文部分仍逐字
场景 WordPiece 的作用
─────────────────────────────────────────────────────────
纯英文句子 核心作用(子词切分,保留词根语义)
法语/西班牙语/德语等拉丁语系 核心作用(与英文类似,有空格分隔词边界)
纯中文句子 几乎没有(退化为逐字切分)
中英混合句子 部分作用(英文部分子词切分,中文部分逐字)
中日混合句子 部分作用(日文部分可能子词切分)
为什么拉丁语系效果好?
├── 有空格分隔单词 → 预分词能识别词边界
├── 词根+词缀结构明显 → WordPiece 擅长切分(un+##believ+##able)
└── 共享拉丁字母 → 词表利用率高
实际业务中(如 HXXedge 系统):
用户输入几乎全是中文 → WordPiece 基本不发挥子词优势
分词本质上就是"逐字切分 + 查词表转 ID"
| 对比 | WordPiece | BPE | SentencePiece |
|---|---|---|---|
| 代表模型 | BERT | GPT、LLaMA | T5、多语言模型 |
| 合并标准 | PMI(统计关联度) | 频率(出现次数) | 似然(语言模型概率) |
| 词边界 | ## 前缀 |
空格/特殊标记 | 内置处理 |
| 输入单位 | 字符 | 字节/字符 | Unicode 字符 |
| 中文处理 | 逐字切分 | 逐字/子词 | 直接处理(无需预分词) |
| 优点 | 语义关联好 | 简单快速 | 通用性强 |
bert-base-chinese 使用 WordPiece 分词
词表大小:21,128 个词条
中文:逐字切分
英文:子词切分(play + ##ing)
分词文件:
tokenizer.json ← 完整词表 + 分词规则(21,000+ 行)
tokenizer_config.json ← 分词配置参数(23 行)
推理引擎:
tokenizers 库(Rust 实现)加载 tokenizer.json
→ 把文字转成 input_ids
→ 送入 ONNX 模型推理
WordPiece = 一种"聪明地切词"的算法
核心思想:
不是按频率切,而是按"统计关联度"切
关联度高的子词合并(如 un + ##able)
关联度低的保持分开
好处:
├── 词表小(2-3 万词条)
├── 没有未登录词(最差逐字切分)
└── 保留语义(常见词完整,罕见词拆分)
重要事实:
├── 对英文/拉丁语系 → WordPiece 充分发挥子词优势(核心作用)
├── 对纯中文 → WordPiece 退化为逐字切分(子词优势消失)
└── 对中英混合 → 仅在英文部分发挥作用
BERT 选择 WordPiece → 21,128 词条 → 中文逐字切分 → 高效理解中文
📝 创建:2026-09-11
🔄 标签:WordPiece、分词算法、子词、BERT、Tokenization、PMI