概述
Unigram 是 SentencePiece 中常用的一种子词分词算法,和 BPE、WordPiece 的"从字符合并"方向相反:它先构建一个很大的候选词表,然后反复删除对语料似然影响最小的 token,直到词表缩小到目标大小。
它的核心是一个 Unigram 语言模型:假设每个 token 独立,一个词的概率等于其某种分词方式下所有 token 概率的乘积。
训练时用 EM 思想不断更新 token 概率,并用 Viterbi 算法找每个词概率最大的分词路径。
Unigram 的核心逻辑
与 BPE / WordPiece 的区别
| 算法 | 方向 | 合并/删除依据 |
|---|---|---|
| BPE | 从小到大合并 | 相邻对频率最高 |
| WordPiece | 从小到大合并 | 似然增益 P(xy)P(x)P(y)\frac{P(xy)}{P(x)P(y)}P(x)P(y)P(xy) |
| Unigram | 从大到小删除 | 删除后语料 loss 增加最小 |
Unigram 的训练步骤:
- 初始化一个大词表:比如所有字符 + 高频子串,或先用 BPE 生成一个较大词表。
- E 步:用当前 token 概率,对语料中每个词找最优分词(Viterbi),统计 token 使用情况。
- M 步:根据统计结果重新估计每个 token 的概率,使语料似然最大化。
- 计算删除代价:对词表中每个 token,模拟删除它,重新分词并计算语料 loss 增量 ΔL。
- 剪枝:删除 ΔL 最小的前 p% 个 token(如 10%)。
- 重复,直到词表达到目标大小。单字符通常强制保留,保证任何词都能被分词。
数值例子:语料与初始词表
使用语料:
| 单词 | 频率 |
|---|---|
| hug | 10 |
| pug | 5 |
| pun | 12 |
| bun | 4 |
| hugs | 5 |
初始词表取所有出现的字符及部分高频子串:
text
["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
统计每个 token 在语料中的总频率:
| token | 频率 | 计算来源 |
|---|---|---|
| h | 15 | hug 10 + hugs 5 |
| u | 36 | hug 10 + pug 5 + pun 12 + bun 4 + hugs 5 |
| g | 20 | hug 10 + pug 5 + hugs 5 |
| hu | 15 | hug 10 + hugs 5 |
| ug | 20 | hug 10 + pug 5 + hugs 5 |
| p | 17 | pug 5 + pun 12 |
| pu | 17 | pug 5 + pun 12 |
| n | 16 | pun 12 + bun 4 |
| un | 16 | pun 12 + bun 4 |
| b | 4 | bun 4 |
| bu | 4 | bun 4 |
| s | 5 | hugs 5 |
| hug | 15 | hug 10 + hugs 5 |
| gs | 5 | hugs 5 |
| ugs | 5 | hugs 5 |
总频率:
15+36+20+15+20+17+17+16+16+4+4+5+15+5+5=210 15+36+20+15+20+17+17+16+16+4+4+5+15+5+5=210 15+36+20+15+20+17+17+16+16+4+4+5+15+5+5=210
因此每个 token 的概率是:
P(token)=freq(token)210 P(token) = \frac{\text{freq}(token)}{210} P(token)=210freq(token)
例如:
P(hug)=15210≈0.07143,P(ug)=20210≈0.09524 P(\text{hug}) = \frac{15}{210} \approx 0.07143, \quad P(\text{ug}) = \frac{20}{210} \approx 0.09524 P(hug)=21015≈0.07143,P(ug)=21020≈0.09524
对每个词找最优分词
Unigram 假设 token 独立,所以一种分词方式的概率是各 token 概率的乘积。我们枚举每个词的可能分词,选概率最大的。
"hug"
可能分词:
| 分词 | 概率 |
|---|---|
| h u g | 15210⋅36210⋅20210≈1.17×10−3\frac{15}{210} \cdot \frac{36}{210} \cdot \frac{20}{210} \approx 1.17 \times 10^{-3}21015⋅21036⋅21020≈1.17×10−3 |
| hu g | 15210⋅20210≈6.80×10−3\frac{15}{210} \cdot \frac{20}{210} \approx 6.80 \times 10^{-3}21015⋅21020≈6.80×10−3 |
| h ug | 15210⋅20210≈6.80×10−3\frac{15}{210} \cdot \frac{20}{210} \approx 6.80 \times 10^{-3}21015⋅21020≈6.80×10−3 |
| hug | 15210≈7.14×10−2\frac{15}{210} \approx 7.14 \times 10^{-2}21015≈7.14×10−2 |
最大是 hug ,概率:
P(hug)=15210=0.0714286 P(\text{hug}) = \frac{15}{210} = 0.0714286 P(hug)=21015=0.0714286
"pug"
| 分词 | 概率 |
|---|---|
| p u g | 17210⋅36210⋅20210≈1.32×10−3\frac{17}{210} \cdot \frac{36}{210} \cdot \frac{20}{210} \approx 1.32 \times 10^{-3}21017⋅21036⋅21020≈1.32×10−3 |
| pu g | 17210⋅20210≈7.71×10−3\frac{17}{210} \cdot \frac{20}{210} \approx 7.71 \times 10^{-3}21017⋅21020≈7.71×10−3 |
| p ug | 17210⋅20210≈7.71×10−3\frac{17}{210} \cdot \frac{20}{210} \approx 7.71 \times 10^{-3}21017⋅21020≈7.71×10−3 |
最大是 pu g 或 p ug,概率:
P(pug)=17210⋅20210=34044100≈0.0077098 P(\text{pug}) = \frac{17}{210} \cdot \frac{20}{210} = \frac{340}{44100} \approx 0.0077098 P(pug)=21017⋅21020=44100340≈0.0077098
"pun"
| 分词 | 概率 |
|---|---|
| p u n | 17210⋅36210⋅16210≈1.06×10−3\frac{17}{210} \cdot \frac{36}{210} \cdot \frac{16}{210} \approx 1.06 \times 10^{-3}21017⋅21036⋅21016≈1.06×10−3 |
| pu n | 17210⋅16210≈6.17×10−3\frac{17}{210} \cdot \frac{16}{210} \approx 6.17 \times 10^{-3}21017⋅21016≈6.17×10−3 |
| p un | 17210⋅16210≈6.17×10−3\frac{17}{210} \cdot \frac{16}{210} \approx 6.17 \times 10^{-3}21017⋅21016≈6.17×10−3 |
最大概率:
P(pun)=17210⋅16210=27244100≈0.0061678 P(\text{pun}) = \frac{17}{210} \cdot \frac{16}{210} = \frac{272}{44100} \approx 0.0061678 P(pun)=21017⋅21016=44100272≈0.0061678
"bun"
| 分词 | 概率 |
|---|---|
| b u n | 4210⋅36210⋅16210≈2.49×10−4\frac{4}{210} \cdot \frac{36}{210} \cdot \frac{16}{210} \approx 2.49 \times 10^{-4}2104⋅21036⋅21016≈2.49×10−4 |
| bu n | 4210⋅16210≈1.45×10−3\frac{4}{210} \cdot \frac{16}{210} \approx 1.45 \times 10^{-3}2104⋅21016≈1.45×10−3 |
| b un | 4210⋅16210≈1.45×10−3\frac{4}{210} \cdot \frac{16}{210} \approx 1.45 \times 10^{-3}2104⋅21016≈1.45×10−3 |
最大概率:
P(bun)=4210⋅16210=6444100≈0.0014512 P(\text{bun}) = \frac{4}{210} \cdot \frac{16}{210} = \frac{64}{44100} \approx 0.0014512 P(bun)=2104⋅21016=4410064≈0.0014512
"hugs"
| 分词 | 概率 |
|---|---|
| hug s | 15210⋅5210≈1.70×10−3\frac{15}{210} \cdot \frac{5}{210} \approx 1.70 \times 10^{-3}21015⋅2105≈1.70×10−3 |
| hu gs | 15210⋅5210≈1.70×10−3\frac{15}{210} \cdot \frac{5}{210} \approx 1.70 \times 10^{-3}21015⋅2105≈1.70×10−3 |
| h ug s | 15210⋅20210⋅5210≈1.62×10−4\frac{15}{210} \cdot \frac{20}{210} \cdot \frac{5}{210} \approx 1.62 \times 10^{-4}21015⋅21020⋅2105≈1.62×10−4 |
最大概率:
P(hugs)=15210⋅5210=7544100≈0.0017007 P(\text{hugs}) = \frac{15}{210} \cdot \frac{5}{210} = \frac{75}{44100} \approx 0.0017007 P(hugs)=21015⋅2105=4410075≈0.0017007
计算初始语料 Loss
Unigram 使用负对数似然:
Loss=−∑x∈Corpusfreq(x)⋅logP(x) \text{Loss} = - \sum_{x \in \text{Corpus}} \text{freq}(x) \cdot \log P(x) Loss=−x∈Corpus∑freq(x)⋅logP(x)
其中 P(x) 是该词最优分词的概率。
代入:
| 单词 | 频率 | 最优概率 | 贡献 |
|---|---|---|---|
| hug | 10 | 0.0714286 | 10⋅(−log0.0714286)=26.3910 \cdot (-\log 0.0714286) = 26.3910⋅(−log0.0714286)=26.39 |
| pug | 5 | 0.0077098 | 5⋅(−log0.0077098)=24.335 \cdot (-\log 0.0077098) = 24.335⋅(−log0.0077098)=24.33 |
| pun | 12 | 0.0061678 | 12⋅(−log0.0061678)=61.0612 \cdot (-\log 0.0061678) = 61.0612⋅(−log0.0061678)=61.06 |
| bun | 4 | 0.0014512 | 4⋅(−log0.0014512)=26.144 \cdot (-\log 0.0014512) = 26.144⋅(−log0.0014512)=26.14 |
| hugs | 5 | 0.0017007 | 5⋅(−log0.0017007)=31.885 \cdot (-\log 0.0017007) = 31.885⋅(−log0.0017007)=31.88 |
总 Loss:
26.39+24.33+61.06+26.14+31.88≈169.8
所以初始词表下的语料损失约为 169.8。
模拟删除 token,计算 Loss 增量
现在对词表中每个 token,假设删除它,看 loss 会增加多少。我们选几个有代表性的 token。
删除 "pu"
删除 "pu" 后,pug 和 pun 需要重新分词:
pug 原来:["pu", "g"],概率:
P(pu)P(g)=17210⋅20210 P(\text{pu})P(g) = \frac{17}{210} \cdot \frac{20}{210} P(pu)P(g)=21017⋅21020
删除 pu 后,可用 ["p", "ug"],概率:
P(p)P(ug)=17210⋅20210 P(p)P(ug) = \frac{17}{210} \cdot \frac{20}{210} P(p)P(ug)=21017⋅21020
完全相同。
pun 原来:["pu", "n"],概率:
P(pu)P(n)=17210⋅16210 P(\text{pu})P(n) = \frac{17}{210} \cdot \frac{16}{210} P(pu)P(n)=21017⋅21016
删除 pu 后,可用 ["p", "un"],概率:
P(p)P(un)=17210⋅16210 P(p)P(un) = \frac{17}{210} \cdot \frac{16}{210} P(p)P(un)=21017⋅21016
也完全相同。
因此删除 "pu" 后,所有词的最优概率不变,Loss 增量为:
ΔL(pu)=0 \Delta L(\text{pu}) = 0 ΔL(pu)=0
删除 "ug"
删除 "ug" 后:
- pug 原来 "pu", "g" 仍可用,概率不变。
- hugs 原来 "hug", "s" 仍可用,概率不变。
- 其他词不依赖 "ug"。
所以:
ΔL(ug)=0 \Delta L(ug) = 0 ΔL(ug)=0
小结
- 如果 ΔL(t)=0:删掉它,Loss 完全不变,说明这个 token 对模型没有贡献
- 如果 ΔL(t)>0:删掉它,Loss 变大,说明它还有用。
- ΔL(t) 越大,说明这个 token 越重要,越不应该删。
删除 "hug"
删除 "hug" 后,hug 和 hugs 会受影响。
"hug" 原来 ["hug"],概率:
P(hug)=15210=0.0714286 P(hug) = \frac{15}{210} = 0.0714286 P(hug)=21015=0.0714286
删除后最优变为 ["hu", "g"],概率:
P(hu)P(g)=15210⋅20210=30044100≈0.0068027 P(hu)P(g) = \frac{15}{210} \cdot \frac{20}{210} = \frac{300}{44100} \approx 0.0068027 P(hu)P(g)=21015⋅21020=44100300≈0.0068027
损失增加:
10⋅−log(0.0068027)−(−log(0.0714286))=10⋅log0.07142860.0068027≈10⋅2.35=23.5 10 \cdot -\\log(0.0068027) - (-\\log(0.0714286)) = 10 \cdot \log \frac{0.0714286}{0.0068027} \approx 10 \cdot 2.35 = 23.5 10⋅−log(0.0068027)−(−log(0.0714286))=10⋅log0.00680270.0714286≈10⋅2.35=23.5
"hugs" 原来 ["hug", "s"],概率:
P(hug)P(s)=15210⋅5210 P(hug)P(s) = \frac{15}{210} \cdot \frac{5}{210} P(hug)P(s)=21015⋅2105
删除后可用 ["hu", "gs"],概率:
P(hu)P(gs)=15210⋅5210 P(hu)P(gs) = \frac{15}{210} \cdot \frac{5}{210} P(hu)P(gs)=21015⋅2105
概率不变,损失不增加。
所以:ΔL(hug)≈23.5\Delta L(hug) \approx 23.5ΔL(hug)≈23.5
因为 23.5 远大于 0,所以 "hug" 不会被删除。
Unigram 剪枝的规则是:每一轮删除 ΔL 最小的前 p% 个 token。
所以在这一轮剪枝中:
"pu" 和 "ug" 是最佳删除候选(ΔL = 0)
"hug" 是最不该删除的候选之一(ΔL = 23.5)
剪枝与词表更新
算法每轮会删除 ΔL\Delta LΔL 最小的前 p% 个 token。
例如设 p=10%,初始词表 15 个 token,则删除 1 个。
由于 "pu" 和 "ug" 的 ΔL=0,它们是最佳删除候选。
假设第一轮删除 "ug",则新词表频率总和变为:
210−20=190 210−20=190 210−20=190
更新后的概率重新归一化:
P(token)=freq(token)190 P(token) = \frac{freq(token)}{190} P(token)=190freq(token)
例如:P(hug)=15190≈0.07895P(hug) = \frac{15}{190} \approx 0.07895P(hug)=19015≈0.07895
然后重新运行 EM,更新分词和概率,再次计算每个 token 的删除代价,继续删除 ΔL 最小的 token。
如此反复,直到词表缩小到目标大小。
最终分词:Viterbi 找最大概率路径
训练完成后,对新词分词时,用 Viterbi 算法在词表构成的图中找概率最大的路径。
例如词表中有 h, u, g, hu, ug, hug 等,对单词 unhug 分词:
- 从 u 开始,可能 u、un 等;
- 到 n,最佳是 un;
- 到 h,un + h;
- 到 u,un + hu;
- 到 g,un + hug。
最终得到:
text
["un", "hug"]
这就是概率最大的分词路径。
总结
Unigram 算法可以概括为:
-
从大词表开始,而不是从小合并。
-
用 Unigram 语言模型定义词的概率:
P(word)=maxsegmentation∏t∈segP(t) P(word) = \max_{segmentation} \prod_{t \in seg} P(t) P(word)=segmentationmaxt∈seg∏P(t)
实际常用 Viterbi 找最大概率路径。
-
用 EM 更新 token 概率:E 步分词统计,M 步重估概率。
-
计算删除每个 token 的 loss 增量:
ΔL(t)=Lnew−Lold \Delta L(t) = L_{new} - L_{old} ΔL(t)=Lnew−Lold
删除 ΔL 最小的 token。
- 重复剪枝,直到词表达到目标大小。
在上面的例子中,删除 "pu" 或 "ug" 不会增加语料 loss,因此它们会被优先删除;而删除 "hug" 会使 loss 增加约 23.5,因此会被保留。
通过这种"反向剪枝",Unigram 能自动保留对语料似然贡献最大的子词,最终得到一个紧凑且高效的分词词表。