有笨蛋嘛?
Graph Neural Networks 炼丹中...
睡大觉中
前方高能反应!
BUG 修复进度 99%
到底在干嘛?
GROMACS 跑起来了吗?
Tailwind CSS 拯救前端
在干嘛呢?
MD 模拟什么时候才能出图啊
写算法中
有笨蛋嘛?
BUG 修复进度 99%
睡大觉中
前方高能反应!
Evernightの宝藏之地
首页项目归档照片墙音乐说说友链关于
封面

BPE 分词算法原理讲解:大模型背后的词表构建基石

写作时间:2026-05-19
约 3 分钟 · 1175 字

词表危机:LLM 如何认识世界?

在把人类语言喂给 Transformer 等深度学习模型之前,我们必须先将其切分成离散的“词元”(Token)。这个切分过程往往面临两难:

如果按词切分(Word-level),比如把 machine learning 变成 [machine, learning],随着语料丰富,词汇表会膨胀到天文数字大小。而且在遇到网络造词、罕见拼写变形时,模型只能用 <UNK> (未知) 符号糊弄过去,丢失极大的信息。

如果退而求其次按字符切分(Character-level),比如拆成 [m, a, c, h, i, n, e, ...],虽然词表变小了且没有 <UNK> 问题,但每个句子的序列长度会暴增。这直接会导致 Transformer 昂贵的注意力机制算力爆炸,且单个字母也很难承载复杂的语义。

那么,有没有一种两全其美的办法,既能把常见词当成整体处理,又能把生僻词拆成合理的词根?BPE(字节对编码) 应运而生。


BPE 是如何炼成的?

BPE(Byte Pair Encoding)原本是用于数据压缩的古老算法。其核心思路非常纯粹:将文本中最高频出现的相邻符号对,合并成一个新的大符号。

通过在语料库上持续进行“频率统计 →\to→ 贪心合并”,模型可以自动学出一套包含不同粒度的子词(Subword)词表。

模拟训练:构造一个迷你词表

假设我们手头只有以下 4 个单词及其词频,为了清楚边界,我们给每个词结尾加一个特殊标记 </w>:

  • low</w> (出现 5 次)
  • lower</w> (出现 2 次)
  • newest</w> (出现 6 次)
  • widest</w> (出现 3 次)

Step 1:拆成原子字符 一开始,我们把所有词打碎,此时的基础词表只有拆开的单字符:l, o, w, e, r, n, s, t, i, d, </w>。

Step 2:寻找最高频组合 扫描这批打碎的语料,统计相邻两两字符的出现频率。 我们注意到 (e, s) 相邻的频率非常高:在 newest 中出现了 6 次,在 widest 中出现了 3 次,总计 9次。 由于它是最高频对,我们将 e 和 s 合并为新词元 es,并将其郑重写入我们的词汇表中。

Step 3:不断迭代 此时数据变成了 n e w es t </w> 等。我们继续扫描,发现 (es, t) 又紧挨着出现了 9 次。 继续合并:我们得到新词元 est。

之后 (est, </w>) 会被合并为 est</w>,因为英文里 -est 作为后缀经常出现在词尾。再往后,l 和 o 会合并为 lo,接着 lo 和 w 合并为 low。

只要我们将这个循环跑下去,设定一个目标词表大小(比如 32,000 或 64,000),算法就会在拼满后自动停止。最终生成的词汇库里,既有基础的单字符,也会包含大量高频的前缀、后缀和常用单词。


推理阶段的切词艺术

词表炼成之后,对于任意一段全新的用户输入,BPE 是如何进行拆解的呢?

规则其实是死板而严格的:绝对服从训练时生成的合并记录,依次尝试将字符组合起来。

举个例子,用户输入了一个模型从未在训练语料里见过的生造词 lowest。

  1. BPE 先把它切得粉碎:l o w e s t </w>
  2. 根据训练时学到的优先级,首先套用 (e, s) →\to→ es 的规则,接着套用 (es, t) →\to→ est 的规则。
  3. 前半部分套用 (l, o) 和 (lo, w) 的规则,得到 low。

奇迹发生了——新造词 lowest 被切成了 low 和 est 两个 Subword。模型虽然没背过这个词,但顺理成章地理解了这是“低”的“最高级”,不仅完全避免了抛出 <UNK> 的尴尬,还精准洞察了它的词根逻辑。


进化版:BBPE(Byte-Level BPE)

传统的 BPE 一般建立在 Unicode 字符上。然而,世界上的语言、符号极度繁杂,各种稀奇古怪的 emoji 层出不穷,光是把单字符穷举出来就已经是一个庞大的基座了。

后来,OpenAI 在设计 GPT-2 时另辟蹊径:任何文本,在计算机底层不全是由 UTF-8 编码的字节(Byte)拼接而成的吗?

既然 1 个字节只有 256 种可能的取值,那就直接把这 256 个基础字节作为最底层的原子字符!这就诞生了更加通用的 BBPE(Byte-Level BPE)。

在这种架构下:

  • 永远告别 OOV(未登录词):哪怕遇到一段乱码或者罕见的外星文,最后也一定兜底退化成这 256 种字节基础单元的拼接。
  • 词表空间极大释放:把原本因为处理各国冷门文字而浪费的槽位省出来,让词表更加紧凑纯粹。现代绝大多数万亿参数的巨兽模型,都在沿用这项革命性的改进设计。
上一篇从 Hexo 到 Nuxt:博客迁移全记录下一篇LSH算法原理讲解:从文本相似到大规模候选检索

评论

正在加载评论…

正在检查登录状态…

avatar

Evernight

记录学习、代码与生活的片刻。

RECOMMENDED

RLHF算法原理讲解:让大模型对齐人类偏好

2026-05-21

LSH算法原理讲解:从文本相似到大规模候选检索

2026-05-19

从 Hexo 到 Nuxt:博客迁移全记录

2026-05-03

Table of Contents

© 2026 Evernight✦把片刻留给永夜

基于 XingHuiSama · Evernight 修改CC BY-NC 4.0