文章目录
- [1. 为什么还要有 BBPE?](#1. 为什么还要有 BBPE?)
- [2. byte 到底是什么?](#2. byte 到底是什么?)
- [3. 英文字母是怎么变成 byte 的?](#3. 英文字母是怎么变成 byte 的?)
- [4. 但中文就不一样了](#4. 但中文就不一样了)
- [5. 用"乐高积木"理解 BBPE](#5. 用“乐高积木”理解 BBPE)
- [6. 再进一步:它甚至可以把多个汉字粘起来](#6. 再进一步:它甚至可以把多个汉字粘起来)
- [7. BBPE 到底怎么训练?](#7. BBPE 到底怎么训练?)
- [8. 那 BBPE 最牛的地方是什么?](#8. 那 BBPE 最牛的地方是什么?)
- [9. 这是 BBPE 最大的工程意义](#9. 这是 BBPE 最大的工程意义)
- [10. 用一个极端例子理解](#10. 用一个极端例子理解)
- [11. 那为什么罕见字符特别"费 Token"?](#11. 那为什么罕见字符特别“费 Token”?)
- [12. BBPE 并不真正"懂中文"](#12. BBPE 并不真正“懂中文”)
- [13. BPE 和 BBPE 到底差在哪里?](#13. BPE 和 BBPE 到底差在哪里?)
- [14. 为什么名字这么奇怪?](#14. 为什么名字这么奇怪?)
- [15. BBPE 的两个阶段也要分清](#15. BBPE 的两个阶段也要分清)
- [16. 为什么 GPT 一类模型特别适合这种方案?](#16. 为什么 GPT 一类模型特别适合这种方案?)
- [17. 但 BBPE 也不是完美的](#17. 但 BBPE 也不是完美的)
- [18. 最终检验](#18. 最终检验)
可以。BBPE 通常指 Byte-level BPE(Byte-level Byte Pair Encoding,字节级 BPE)。
如果刚才的 BPE 你已经理解了,那么 BBPE 实际上只改了一个最关键的地方:
BPE:从"字符/子词"等基本单位开始合并。
BBPE:强制从最底层的 byte(字节)开始合并。
用费曼学习法,可以把 BBPE 理解成:
先把世界上所有文字都拆成计算机最原始的字节积木,再把经常一起出现的字节积木逐渐粘成更大的 Token。
1. 为什么还要有 BBPE?
假设我们做一个普通字符级 BPE。
英语:
text
hello
可以很自然地拆成:
text
h e l l o
中文:
text
你好
拆成:
text
你 好
看起来也没问题。
但是世界上字符实在太多:
text
中文
English
日本語
한국어
é
ü
①
🚀
😂
𝕏
罕见 Unicode 字符
如果你的 tokenizer 基础词表想直接覆盖所有 Unicode 字符,就会很麻烦。
于是 BBPE 想了一个办法:
别管这是中文、英语还是 Emoji。计算机最终不都是 byte 吗?
2. byte 到底是什么?
你可以暂时把 byte 理解成:
计算机保存文本时最基础的一小块数字。
一个 byte 有:
text
0 ~ 255
总共只有:
text
256 种
这就非常漂亮了。
因为无论全世界有多少种文字,UTF-8 最终都可以表示成这 256 种 byte 的组合。
所以 BBPE 的最初始词表理论上只需要:
256 个 byte。
3. 英文字母是怎么变成 byte 的?
比如:
text
hello
UTF-8 编码下:
text
h → 68(hex)
e → 65
l → 6C
l → 6C
o → 6F
于是计算机底层看到的其实可以理解成:
text
68 65 6C 6C 6F
BBPE 刚开始就把它看成:
text
[h] [e] [l] [l] [o]
这里恰好一个英文字母对应一个 byte,所以看起来和字符 BPE 差不多。
4. 但中文就不一样了
例如:
text
我
UTF-8 中对应三个字节:
text
E6 88 91
所以普通人的视角:
text
我
是 1 个汉字。
但 BBPE 最底层看到的是:
text
[E6] [88] [91]
3 个 byte。
这就是 BBPE 最核心的地方。
5. 用"乐高积木"理解 BBPE
假设 BBPE 一开始只有最小积木:
text
[E6] [88] [91]
它根本不知道:
这三个东西代表汉字"我"。
它只知道:
"咦,这几个 byte 好像经常连续出现。"
如果训练语料中"我"出现几亿次,那么:
text
E6 + 88
可能经常连在一起。
于是合并:
text
[E6 88]
之后又发现:
text
[E6 88] + [91]
经常一起出现。
继续合并:
text
[E6 88 91]
于是最终:
text
我
完整的 UTF-8 字节序列可能对应成一个 Token。
注意:
BBPE 从来没人告诉它"我"是一个汉字。
它只是根据 byte 的共现频率自己把三个 byte 粘起来了。
6. 再进一步:它甚至可以把多个汉字粘起来
假设训练数据里面:
text
人工智能
出现了几千万次。
一开始 BBPE 看到的是一长串 UTF-8 bytes。
经过大量 merge 以后,它可能逐渐产生:
text
人
工
人工
智能
人工智能
这些越来越大的 Token。
所以最终:
text
人工智能
可能切成:
text
人工智能
一个 Token。
也可能:
text
人工 | 智能
两个 Token。
或者:
text
人工 | 智 | 能
具体取决于训练语料和词表大小。
7. BBPE 到底怎么训练?
假设为了教学,我们拿:
text
hello
hello
help
先转为 byte。
因为全是 ASCII,可以直接简写:
text
h e l l o
h e l l o
h e l p
BBPE 开始统计:
text
h + e
e + l
l + l
l + o
l + p
发现:
text
h + e
出现最多。
于是:
text
h + e → he
语料变成:
text
he l l o
he l l o
he l p
继续统计。
可能发现:
text
he + l
频率最高。
于是:
text
he + l → hel
变成:
text
hel l o
hel l o
hel p
继续:
text
hel + l → hell
再:
text
hell + o → hello
于是词表里慢慢长出了:
text
he
hel
hell
hello
这跟普通 BPE 完全一样。
唯一关键区别:最开始的原子单位是 byte。
8. 那 BBPE 最牛的地方是什么?
假设用户突然输入一个训练集几乎从来没出现过的 Emoji:
text
🫠
普通字符级 tokenizer 如果基础词表里根本没有这个字符,就可能遇到 OOV:
text
<UNK>
也就是:
我不认识。
而 BBPE 不怕。
因为:
text
🫠
一定存在 UTF-8 编码。
只要存在 UTF-8 编码,它一定能拆成:
text
byte1 byte2 byte3 byte4 ...
而这每一个 byte 都属于:
text
0~255
BBPE 的基础词表全部覆盖。
所以:
任何合法 UTF-8 字符串,理论上 BBPE 都可以编码。
哪怕它最后被拆得非常碎。
9. 这是 BBPE 最大的工程意义
你可以问:
"为什么不直接给所有 Unicode 字符建词表?"
因为 Unicode 字符很多,而且还不断扩展。
但是 byte 永远只有:
text
256 种
于是:
text
任意文本
↓ UTF-8
字节序列
↓
只需要 256 个基础符号
↓
BPE 不断合并高频序列
↓
得到几万 / 十几万个 Token
因此 BBPE 同时解决了:
覆盖能力 和压缩效率。
10. 用一个极端例子理解
假设训练语料里:
text
中华人民共和国
出现了非常非常多次。
BBPE 最开始:
text
中 → 3 bytes
华 → 3 bytes
人 → 3 bytes
民 → 3 bytes
共 → 3 bytes
和 → 3 bytes
国 → 3 bytes
一共大约:
text
21 bytes
最原始状态可能相当于 21 个单位。
但是随着训练:
text
中
华
中华
人民
共和国
中华人民共和国
这些频繁出现的 byte 序列可能逐渐被 merge。
最终甚至可能:
text
中华人民共和国
成为很少几个 Token。
所以 BBPE 本质上是在做:
对自然语言中的高频 byte 序列进行统计压缩。
11. 那为什么罕见字符特别"费 Token"?
现在应该很好理解了。
例如模型训练数据里:
text
the
出现几百亿次。
BBPE 会非常积极地把:
text
t + h + e
融合。
最后:
text
the
可能就是一个 Token。
但你突然输入一个非常罕见的 Unicode 字符:
text
𰻞
训练语料里几乎没见过。
它对应的多个 byte 没有机会形成高优先级 merge。
于是:
text
𰻞
可能需要多个 Token。
因此:
同样是一个"字符",Token 成本可能完全不同。
12. BBPE 并不真正"懂中文"
这是一个非常容易产生的误解。
假设它学出来:
text
人工智能
一个 Token。
你不能说:
BBPE 理解了"人工智能"这个词的含义。
没有。
它只是发现:
text
这串 byte 出现得特别频繁。
于是压缩成一个符号。
就像压缩软件发现:
text
011011001101100...
经常出现,于是给它一个快捷编号。
所以:
Tokenizer 学的是字符串统计结构,不是语义。
真正学习:
text
人工智能是什么意思
的是后面的 Transformer 参数。
13. BPE 和 BBPE 到底差在哪里?
这一张表基本就能记住:
| BPE | BBPE | |
|---|---|---|
| 全称 | Byte Pair Encoding | Byte-level BPE |
| 最初单位 | 通常是字符/符号等 | byte |
| 基础集合 | 依实现而异 | 256 bytes |
| 中文"我"初始状态 | 可能直接是 我 |
E6 88 91 |
| 遇到罕见字符 | 取决于基础词表 | 一定可以拆成 bytes |
| OOV 问题 | 可能需要额外处理 | 基本可以天然避免 |
| 最终 Token | 子词/词/字符串片段 | 高频 byte 序列 |
所以千万不要把:
Byte Pair Encoding
里面的 Byte,
和:
Byte-level BPE
里面的 Byte-level,
混为一谈。
这是名字非常容易误导人的地方。
14. 为什么名字这么奇怪?
历史上的 Byte Pair Encoding 本来就是一种数据压缩算法。
NLP 后来借用了它的:
"不断合并最高频 pair"
这个思想。
但是 NLP 版 BPE 的最底层单位并不一定真的是 byte。
后来又出现:
Byte-level BPE
意思就是:
"这次真的把 byte 作为最底层单位。"
所以:
text
BPE
重点是:
text
pair merge 算法
而:
text
BBPE
重点是:
text
从 byte 开始做 BPE
15. BBPE 的两个阶段也要分清
训练 tokenizer 时:
text
大量文本
↓
UTF-8 bytes
↓
统计相邻 byte/token pair
↓
不断 merge
↓
Vocabulary + Merge Rules
比如学出了:
text
t + h → th
th + e → the
E6 + 88 → ...
...
以后真正使用 tokenizer 的时候,不会重新训练。
用户输入:
text
hello
Tokenizer 根据已经学好的 merge rules:
text
bytes
↓
执行 merge
↓
hello
↓
Token ID
最后可能得到:
text
[15339]
具体数字取决于 tokenizer。
模型真正看到的是 ID,而不是:
text
hello
这几个字母。
16. 为什么 GPT 一类模型特别适合这种方案?
因为互联网训练数据极其混乱:
text
普通英语
中文
日语
Emoji
URL
JSON
Python
Java
HTML
LaTeX
用户名
奇怪 Unicode
拼写错误
例如:
java
Map<String, List<UserDTO>>
或者:
text
https://example.com/api/v1?id=123
如果 tokenizer 严重依赖"自然语言单词",面对这些东西会很难受。
BBPE 完全不在乎:
你到底是 Java、中文还是 Emoji。
它只看:
text
byte byte byte byte byte...
所以它非常适合通用大语言模型这种复杂输入。
17. 但 BBPE 也不是完美的
它最大的缺点之一就是:
对训练数据中比较少见的语言/字符,Token 效率可能很差。
例如一种语言在训练集中极少出现。
它的 byte 序列很少被 merge。
于是同样一句话:
text
英语:10 tokens
另一种语言可能:
text
20~40 tokens
这就意味着:
text
更占上下文长度
+
推理成本更高
+
有效信息密度更低
所以 tokenizer 的训练语料分布非常重要。
18. 最终检验
假设现在一个完全不懂 AI 的人问你:
什么是 BBPE?
如果你能这样回答,就算真的理解了:
BBPE 是 Byte-level BPE。它先把所有文本按照 UTF-8 转换成字节,因此最基础只需要 256 种 byte,就能够表示中文、英文、Emoji 等任何文本。然后它和普通 BPE 一样,不断统计训练语料中最常出现的相邻 byte 或已有 Token,把它们合并成更大的 Token。于是常见字符串会被压缩成很少的 Token,而罕见字符串即使从没见过,也仍然能够退化成 byte 来表示,因此基本不存在传统意义上的未知字符问题。
最后再压缩成一句:
BPE 是"不断粘高频积木",BBPE 则规定"最开始那套积木一定是 256 种 byte"。
这就是 BBPE 最核心的本质。