一、为什么要分词
考虑一个简单的句子Hello world,不分词直接拆成字符:H、e、l、l、o、空格、w、o、r、l、d共11个字符:
- 语义完全丢失
- 一个复杂的句子或片段拆分出来的字母序列非常长
考虑直接空格分词:
- 中文没有空格,多语言不支持
Token和Tokenization语义相近,但词表里是两个毫不相关的词,再来一个Tokenizer就不认识了- 词表巨大(OOV)
概括一下:
- 语言的开放性:语种、语法、专业词汇、人名、地名、网络梗、……
- 模型的泛化能力:理解词与词的关系,提高学习效率
- 计算效率:Transformer的自注意力机制计算复杂度为O(N^2)
在语义、泛化能力、效率之间达到平衡:$$\text{有效性} \propto \frac{\text{语义保持}}{( \text{词表大小} + \text{序列长度} )}$$
二、BPE(Byte Pair Encoding)
把最常见的字节对合并为一个新符号以减少长度
步骤:
- 把训练语料的每个词看作由字符组成的序列,通常在每个词尾加一个特殊结束符(比如
</w>)以保存词边界信息。 - 统计整个语料中所有相邻符号对(pair,例如
('l','o')或('o','w'))的出现频率。 - 找到出现频率最高的符号对,把它们合并成一个新符号(比如把
'l'+'o'合并成'lo')。 - 在语料中用新符号替换所有该对出现位置。
- 重复步骤 2–4,直到达到预定的合并次数或词表大小。
训练结果是一系列“合并操作(merge ops)”或最终词表。用于推理时,对新词按同样策略进行合并(贪心地应用已学合并),得到子词序列。
举个🌰:
- 给定一组语料:
low
lower
lowest
- 作为结尾保留边界,初始化
low</w> -> l o w </w>
lower</w> -> l o w e r </w>
lowest</w> -> l o w e s t </w>
- 统计初始相邻对频率
- low:(l,o) (o,w) (w, )
- lower:(l,o) (o,w) (w,e) (e,r) (r, )
- lowest:(l,o) (o,w) (w,e) (e,s) (s, t) (t, )
频率表:
| pair | 计数 |
|---|---|
| (l,o) | 3 |
| (o,w) | 3 |
| (w,e) | 2 |
| (w,) | 1 |
| (e,r) | 1 |
| (r,) | 1 |
| (e,s) | 1 |
| (s,t) | 1 |
| (t,) | 1 |
频率最高的是(l,o) (o,w),先合并(l,o),也可以定义不同的优先级选取不同的结果
- 合并(l,o) -> lo
lo w </w>
lo w e r </w>
lo w e s t </w>
- 更新频率表
| pair | 计数 |
|---|---|
| (lo,w) | 3 |
| (w, e) | 2 |
| (w,) | 1 |
| (e,r) | 1 |
| (e,s) | 1 |
| (s,t) | 1 |
| (t,) | 1 |
| (r,) | 1 |
频率最高的是(lo,w)
- 再合并(lo, w) -> low
low </w>
low e r </w>
low e s t </w>
| pair | 计数 |
|---|---|
| (low, e) | 2 |
| (low, ) | 1 |
| (e,r) | 1 |
| (e,s) | 1 |
| (s,t) | 1 |
| (t,) | 1 |
| (r,) | 1 |
除主项之外,其余项的频率都是1,没有再继续合并的必要,算法终止
- 最终合并结果:
{'l','o','w','e','r','s','t','</w>', 'lo', 'low', 'lowe'}
编码一个输入:lower
初始:l o w e r </w>
贪心匹配:
- l + o -> lo =>
lo w e r </w> - lo + w -> low =>
low e r </w> - low + e -> lowe =>
lowe r </w>
最终tokens:
["lowe", "r", "</w>"]
三、WordPiece
评价函数
基于BPE,在如何选择新的字词上做概率计算,每次选择加入词表的子词,都是为了最大化训练语料在当前词表下的似然(或最大减少负对数似然),说人话就是:优先合并那些组合在一起后,看起来更像一个整体的、有意义的 Token的字符对,而不是仅仅是高频的随机组合,进而减小token数量
数学表达:$$\text{Score}(\text{A}, \text{B}) = \log\left( \frac{P(\text{A}, \text{B})}{P(\text{A}) \cdot P(\text{B})} \right)$$
其中,P(*)是 Token 在训练语料中出现的概率
- P(A)·P(B) : 表示如果A和B是两个独立事件(即它们随机相邻),它们同时出现的概率。
- P(A,B): 表示A和B 实际相邻出现的概率(作为整体)
那么如果Score越大,说明 $$P(\text{A}, \text{B}){}$$比 $$P(\text{A}) \cdot P(\text{B})$$大得多,就说明A和B相邻不是一个偶然事件,以整体的形式出现更有意义
再举例🌰
- 还是这组语料
low</w> -> l o w </w>
lower</w> -> l o w e r </w>
lowest</w> -> l o w e s t </w>
初始词表为:{ l, o, w, e, r, s, t, </w> }
- 统计相邻对
- (l,o): 3
- (o,w): 3
- (w,e): 2
- 其它都是 1
如果把 lo(l+o)作为新 token,会对训练语料的似然增益有多大贡献?同理评估ow(o+w)、we,这里选择(l,o) → lo,因为英语里lo的组合更具语义,胜过ow组合
- 再评估
- (lo, w): 3
- (w, e): 2
- 其余 1
(lo,w) → low在三个单词中都能减少token数量,显著提升似然
- 再次评估
- (low, e): 2
- others 1
得到词表:{ l, o, w, e, r, s, t, </w>, lo, low, lowe }
编码一个输入:lowest
按最新词表用 longest-match-first:
- 从左到右,先试最长匹配:
lowe存在 → 输出lowe - 余下
s t </w>,st</w>不在表;尝试s→ 存在 → 输出s - 剩
t </w>→ 输出t</w>或t+</w>取决实现
最终得到tokens:
["lowe", "s", "t", "</w>"]
问题:
- 多语言怎么办?
- 没空格怎么办?
四、SentencePiece
基本思想:大词表优化
- 给定一组候选 token,生成初始的候选词表
- 不断移除不重要的 token
- 最后剩下最能表达语料的 token
特殊处理:把空格映射为_,这样能对所有语言生效
最后举例🌰
预处理后:
_我爱你
_我爱学习
_我爱北京
- 初始大词表
// 基本字符
▁, 我, 爱, 你, 学, 习, 北, 京
// 所有可能的连续子串
▁我, ▁我爱, ▁我爱你
▁我爱学, ▁我爱学习
▁我爱北, ▁我爱北京
爱你, 爱学, 爱学习, 爱北, 爱北京
学习, 北京
…
- 对语料分词,以“_我爱学习”为例
// 可能的分词
▁我 + 爱 + 学习
▁我爱 + 学习
▁ + 我 + 爱 + 学 + 习
同理,列出每段语料可能的分词情况
- 计算每个token对语料的贡献得分
-
定性分析
token 使用频率(想象即可) ▁我 很高 爱 很高 学习 高 北京 高 你 一般 我 偶尔(因为多数用了“▁我”) ▁我爱学习 几乎不用 ▁我爱北京 几乎不用 ▁我爱你 几乎不用 -
实际的定量计算是一套关于后验概率和概率期望值的复杂计算
- 去除贡献得分低的token
// 第一轮计算后,精简的词表
▁, ▁我, 爱, 你, 学习, 北京, 北, 京
-
重复步骤2-4,重新分词,计算新token的贡献得分
token 被使用次数 ▁我 很高 爱 很高 学习 高 北京 高 你 中 北 少 京 少 -
得到最终词表
{▁, ▁我, 爱, 你, 学习, 北京}
总结
- 分词的原因:语义、泛化能力、效率之间达到平衡
- 三种分词算法:BPE、WordPiece、SentencePiece
| 特性 / 算法 | BPE(Byte Pair Encoding) | WordPiece | SentencePiece(Unigram/BPE) |
|---|---|---|---|
| 核心思想 | 频繁 token 对合并 | 最大似然概率合并(基于语言模型) | 用概率模型选最优 token 序列 |
| 输入处理 | 只能处理空格分好词的文本 | 同 BPE(依赖空格) | 不依赖空格,直接基于字符序列 |
| 空格处理方式 | 需要预先空格分词 | 需要预先空格分词 | 将空格视为「▁」符号,直接处理原始文本 |
| 词表构建方式 | 贪心:频率最高的 pair | 贪心 + 概率:最大化 likelihood | 基于概率模型:删除低贡献 token |
| 优点 | 简单、高效 | 拆分结果更自然 | 最灵活,最通用,不依赖空格 |
| 缺点 | 不考虑上下文概率 | 仍需空格分词 | 训练复杂度高一点 |
| 典型应用 | GPT、LLaMA 早期模型 | BERT | GPT-4、T5、现代模型 |