【分词:BPE算法】白板手推吃透LLM基石原理!
https://www.bilibili.com/video/BV1v9dSYLE6U?spm_id_from=333.788.videopod.sections&vd_source=173edcc8f6052bd44ad224e1284119c3

GBT模型第一步:分词 完整清晰笔记
一、为什么要做分词
1. 问题背景
- 牛津词典统计英文单词约60万个
- 数量庞大的核心原因:派生词极多
- 动词变形:
go+es/ing会成为独立词条 - 前缀/后缀:
like+前缀un变成unlike
- 动词变形:
2. 分词的核心目的
- 提取单词的词根/子词,大幅压缩词汇表达空间
- 若只保留词根,英文词汇量可缩减至17-22万个
3. 额外关键好处
- 解决未登录词(OOV)问题:新出现的单词可以通过已有子词组合生成
- 例如:新词
bus可以用b+us两个已有子词表示
二、BPE字节对编码算法详解
1. 算法核心思想
通过统计语料中相邻字符/子词的出现频率,将高频相邻对不断合并为新的子词,最终构建出兼顾压缩率和泛化能力的词汇表。
2. 完整实现步骤(基于6个单词原例)
原例语料:go、goes、fit、unfit、undo、unzip
步骤1:初始词典构建
将所有单词按单个字母拆分:
| 原始单词 | 拆分后的字符序列 | token数量 |
|---|---|---|
| go | g o | 2 |
| goes | g o e s | 4 |
| fit | f i t | 3 |
| unfit | u n f i t | 5 |
| undo | u n d o | 4 |
| unzip | u n z i p | 5 |
| 总计 | - | 21 |
初始词典(共12个元素):g、o、e、s、f、i、t、u、n、d、z、p
步骤2:第一次迭代(合并最高频相邻对)
统计所有相邻字符对的出现频率:
| 相邻字符对 | 出现次数 | 出现位置 |
|---|---|---|
| u n | 3次 | unfit、undo、unzip |
| g o | 2次 | go、goes |
| f i | 2次 | fit、unfit |
| i t | 2次 | fit、unfit |
| 其他 | 1次 | 略 |
最高频对:un(出现3次),将其合并为新元素加入词典。
合并后的序列及token数量:
| 原始单词 | 合并后的序列 | token数量 |
|---|---|---|
| go | g o | 2 |
| goes | g o e s | 4 |
| fit | f i t | 3 |
| unfit | un f i t | 4 |
| undo | un d o | 3 |
| unzip | un z i p | 4 |
| 总计 | - | 20 |
更新后的词典(共13个元素):
初始12个 + un
步骤3:第二次迭代(合并次高频相邻对)
重新统计所有相邻元素的频率,最高频对为go(出现2次),将其合并为新元素加入词典。
合并后的序列及token数量:
| 原始单词 | 合并后的序列 | token数量 |
|---|---|---|
| go | go | 1 |
| goes | go e s | 3 |
| fit | f i t | 3 |
| unfit | un f i t | 4 |
| undo | un d o | 3 |
| unzip | un z i p | 4 |
| 总计 | - | 18 |
更新后的词典(共14个元素):
之前13个 + go
步骤4:第三次迭代(合并第三高频相邻对)
再次统计所有相邻元素的频率,最高频对为fi(出现2次),将其合并为新元素加入词典。
合并后的序列及token数量:
| 原始单词 | 合并后的序列 | token数量 |
|---|---|---|
| go | go | 1 |
| goes | go e s | 3 |
| fit | fi t | 2 |
| unfit | un fi t | 3 |
| undo | un d o | 3 |
| unzip | un z i p | 4 |
| 总计 | - | 16 |
最终词典(共15个元素):
之前14个 + fi
步骤5:停止条件
- BPE是一个无限迭代的过程,理论上可以一直合并到所有单词都成为独立元素
- 停止条件的唯一作用是控制词汇表的最终规模
- 本例中我们预设目标是新增3个词汇,因此在第3次迭代后停止
三、三个核心概念的精确定义与计算
1. 序列长度L
定义:一段文本被分词后,得到的子词(token)的总个数。
计算方法:将所有分词后的子词一个一个数出来。
本例中的值:
- 极端1(全拆成字符):L=21
- 中间状态(第3次迭代):L=16
- 极端2(全合并成单词):L=6
2. 自注意力计算量
定义:Transformer自注意力层需要执行的点积运算总次数。
为什么是L²:
- 自注意力的核心工作:对于输入的每一个token,都要和输入的**所有token(包括它自己)**计算一次相似度
- 相似度通过点积计算
- 因此,L个token需要计算L×L=L²次点积
本例中的值:
- 极端1:21×21=441次
- 中间状态:16×16=256次
- 极端2:6×6=36次
3. 嵌入层参数量
定义:模型嵌入层中需要学习的参数总个数。
为什么是V×d:
- 嵌入层本质是一个查找表,将词典中的每个子词转换为一个固定长度的数字向量
- V:词汇表大小(子词的个数)
- d:每个子词对应的向量维度(每个向量有多少个数字)
- 总参数量 = 子词个数 × 每个子词的向量维度 = V×d
本例中的值(统一设定d=100):
- 极端1:12×100=1200个参数
- 中间状态:15×100=1500个参数
- 极端2:24×100=2400个参数
四、为什么中间状态是最好的?(核心重点)
我们将三个方案的所有关键指标放在一起对比:
| 方案 | 词汇表大小V | 序列长度L | 自注意力计算量L² | 嵌入层参数量V×100 | 能否处理未登录词 |
|---|---|---|---|---|---|
| 极端1(全拆成单个字符) | 12 | 21 | 441 | 1200 | 能 |
| 中间状态(第3次迭代) | 15 | 16 | 256 | 1500 | 能 |
| 极端2(全合并成完整单词) | 24 | 6 | 36 | 2400 | 不能 |
1. 计算量对比:中间状态比极端1快42%
- 极端1计算量:441
- 中间状态计算量:256
- 减少的计算量:441-256=185
- 减少比例:185÷441≈42%
这意味着,使用中间状态的分词结果,模型的运行速度会比极端1快接近一半。
2. 参数量对比:中间状态只比极端1多25%
- 极端1参数量:1200
- 中间状态参数量:1500
- 增加的参数量:1500-1200=300
- 增加比例:300÷1200=25%
这意味着,我们只付出了25%的参数增加代价,就换来了42%的计算速度提升,性价比极高。
3. 与极端2对比:中间状态参数量少37.5%,且能处理未登录词
- 极端2参数量:2400
- 中间状态参数量:1500
- 减少的参数量:2400-1500=900
- 减少比例:900÷2400=37.5%
更重要的是,极端2无法处理未登录词。如果出现一个不在训练语料中的新单词(如unhappy),极端2只能用一个统一的<unk>标记代替,导致模型完全无法理解;而中间状态可以将unhappy拆成un+happy两个已有子词,模型仍然能够理解其含义。
4. 黄金平衡点的本质
BPE的停止条件本质上是在**"计算速度"和"模型大小"之间做的一个工程权衡**:
- 词汇表越小,模型越小,但序列越长,计算越慢
- 词汇表越大,序列越短,计算越快,但模型越大
- 中间状态找到了一个最优的折中:用很小的模型增加,换来了很大的计算速度提升,同时还保留了处理未登录词的能力
五、分词器的调用:贪婪匹配策略
1. 核心原则
从左到右扫描文本,每次从当前位置开始,尝试匹配词典中最长的可能子词。
2. 完整执行过程(以unfit为例)
词典:g、o、e、s、f、i、t、u、n、d、z、p、un、go、fi
-
当前位置:第1个字符(u)
- 尝试匹配最长子串
unfit(4个字符)→ 不在词典中 - 缩短为
unf(3个字符)→ 不在词典中 - 缩短为
un(2个字符)→ 在词典中 - 第一个分词结果:
un,当前位置移动到第3个字符(f)
- 尝试匹配最长子串
-
当前位置:第3个字符(f)
- 尝试匹配最长子串
fit(3个字符)→ 不在词典中 - 缩短为
fi(2个字符)→ 在词典中 - 第二个分词结果:
fi,当前位置移动到第5个字符(t)
- 尝试匹配最长子串
-
当前位置:第5个字符(t)
- 匹配
t(1个字符)→ 在词典中 - 第三个分词结果:
t,扫描结束
- 匹配
最终分词结果:un、fi、t
六、分词后的向量表示
1. 词典序号分配
为最终词典中的每个元素分配唯一序号:
| 序号 | 元素 | 序号 | 元素 | 序号 | 元素 |
|---|---|---|---|---|---|
| 1 | g | 6 | i | 11 | z |
| 2 | o | 7 | t | 12 | p |
| 3 | e | 8 | u | 13 | un |
| 4 | s | 9 | n | 14 | go |
| 5 | f | 10 | d | 15 | fi |
2. One-hot编码
每个元素对应一个15维向量,对应序号位置为1,其余为0:
un(序号13)→ [0,0,0,0,0,0,0,0,0,0,0,0,1,0,0]fi(序号15)→ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]t(序号7)→ [0,0,0,0,0,0,1,0,0,0,0,0,0,0,0]
3. 单词的矩阵表示
单词unfit被分词为3个元素,因此它的向量表示是一个3×15的矩阵:
[
[0,0,0,0,0,0,0,0,0,0,0,0,1,0,0], # un
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], # fi
[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0] # t
]
七、总结
- 分词是GBT模型的第一步,核心目的是压缩词汇表并解决未登录词问题
- BPE算法通过不断合并高频相邻对来构建词汇表,是一个自底向上的过程
- BPE的停止条件是在计算速度和模型大小之间的权衡,中间状态是最优选择
- 分词器使用贪婪匹配策略,每次优先匹配最长的子词
- 分词后的子词通过one-hot编码转换为向量,供模型后续处理
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)