保姆级教程-BBPE分词算法

文章目录

  • [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 最核心的本质。

相关推荐
不会就选b1 小时前
算法日常・每日刷题--<优先级队列>2
数据结构·算法
hanlin031 小时前
刷题笔记:力扣第189题-轮转数组
笔记·算法·leetcode
琥珀色糖1 小时前
leetcode hot100题(持续更新)移动零(双指针)
算法·leetcode·职场和发展·双指针·移动零
AndrewHZ2 小时前
【LLM技术全景】RAG 从原理到实战——检索增强生成完整指南
人工智能·深度学习·算法·llm·检索增强·生成式模型·rag
LuminousCPP2 小时前
单链表专题(三)-刷题复盘篇:从快慢指针到环形链表 II 数学推导
数据结构·经验分享·笔记·学习·算法·链表
-dzk-2 小时前
【贪心算法】LC 121.买卖股票的最佳时机
算法·贪心算法
hanlin032 小时前
动态规划专练:力扣第718、1143题
算法·leetcode·动态规划
ChaoZiLL2 小时前
二叉树经典例题
数据结构·算法
不正经学生3 小时前
C语言字符串函数进阶:安全版函数 + 错误处理 + 子串查找
c语言·开发语言·c++·算法·面试