🔬 Task 1 · 手搓一个分词器

作者:Winnie | 2026-04-16 | 阅读时间约 10 分钟
📌 先说结论(给没空读完的人)
- 🧠 分词器是模型的"眼睛" —— 它决定模型眼里的一段话是字、是词、还是子词碎片
- 🏆 BPE = Byte Pair Encoding,GPT-4 / Llama 3 / DeepSeek 都在用的主流方案
- 💡 核心思想一句话:统计哪两个挨在一起的片段最常见,合并它——重复 N 次
- ✅ 我跑通了:25 次合并,三个测试句子全部 encode → decode 可逆
🎬 为什么想搞这个
报名了 Datawhale 的 diy-llm 课程——斯坦福 CS336 的中文共建版。
一共 15 章 + 6 个大作业,主线是从 0 手搓一个 LLM:
- 🔧 基础件(分词器 / Transformer / Optimizer)
- 🚀 规模化(MoE / 分布式 / Scaling Laws)
- 🎯 对齐(SFT / GRPO)
这是我打算长期啃的一个系列,和之前《推理王国》共读同级——都是学习类,不急着出结论,慢慢啃。
Task 1 是分词器。占坑先,慢慢补。
🗺️ 这篇讲什么(思维导图)
在真开始讲之前,先铺张地图让你知道我要带你走到哪:
🔬 分词器(Task 1)
│
├── 🧐 它在干嘛
│ └── 文字 → 数字序列(模型看到的世界)
│
├── 🍡 四种切法
│ ├── 字符级 粒度细 · 序列长
│ ├── 字节级 GPT-2 风
│ ├── 词级 OOV 严重
│ └── BPE 🏆 自适应 · 当前主流
│
├── 🔨 BPE 算法
│ ├── 拆字符 + </w> 词尾标记
│ ├── 统计相邻 pair 频次
│ ├── 合并最高频的 pair
│ └── 重复 N 次
│
├── 🧪 实战跑通
│ ├── 25 次合并
│ ├── 编解码可逆 ✅
│ └── 罕见字自动退回字符级
│
└── 🪞 连回 Harness Engineering
└── 热路径合并,冷路径退化(同构)(后面会迭代成真正可交互的 Markmap,先占坑)
🧠 分词器到底在干嘛
打个不严谨的比方:
模型看到的不是"你好世界",而是一串数字
[1234, 5678, 9012]。
从文字到数字的那一步翻译,就是分词器干的。而"怎么切"这件事,直接决定:
- 🪟 同一段话要用几个 token(上下文窗口够不够用)
- 🧩 罕见词、emoji、专业术语能不能表示(OOV 问题)
- 🎯 模型学到的语义单元合不合理(理解效率)
🍡 四种切法,各有代价
| 切法 | 粒度 | 词表 | 序列长 | 代表模型 |
|---|---|---|---|---|
| 字符级 | 细 | 100–5k | 非常长 | Char-RNN |
| 字节级 | 更细 | ~256 | 很长 | GPT-2 |
| 词级 | 粗 | >100k | 短 | Word2Vec |
| 🏆 BPE | 自适应 | 30k–100k | 适中 | GPT-4 / Llama 3 |
BPE 赢在哪 ——
常见词合成一大块(省 token),罕见词自动退回字符(零 OOV)。
一个会看菜下饭的分词器 🍽️
🔨 BPE 算法本体(30 行能写完)
初始化:每个词拆成字符 + 一个词尾标记 </w>
循环 N 次:
1️⃣ 数一数:所有相邻字符对里,哪对最常见?
2️⃣ 合并它:把所有这对出现的地方,粘成一个
3️⃣ 记下来:这条合并规则存进 merges 表这就是全部。简单到让人怀疑"真就这?"
为什么要那个 </w> 🤔 ——
标记词尾,防止 the 的 e 和下一个词的 v 错误合并成 ev。小细节,但没它编解码就不可逆了。
🧪 实战:用 DeepSeek 风格正则训了一版
语料(故意塞了重复):
["这只猫🐈很可爱",
"the quick brown fox jumps over the lazy 🐕🦺",
"the the the quick quick brown fox",
"猫猫猫很可爱很可爱"]训练 25 次合并后,看它学到了啥 👀:
1. (' ', '</w>') ← 空格 + 词尾先合
2. ('t', 'h') → 'th'
3. ('th', 'e') → 'the'
5. ('很', '可') → '很可'
6. ('很可', '爱') → '很可爱'
22. ('这', '只') → '这只'
23. ('这只', '猫') → '这只猫'看懂了吗:重复出现的 the / quick / brown / 这只猫 / 很可爱 都被合成了整块——这就是 BPE 的"自适应"。
编解码测试 3 个句子:
| 输入 | 编码结果 | 还原 |
|---|---|---|
"the quick fox" | ['the</w>', 'quick</w>', 'fox</w>'] | ✅ |
"这只猫很可爱" | ['这只猫', '很可爱</w>'] | ✅ |
"敏捷的棕色狐狸🦊" | 退回字符级(语料未覆盖) | ✅ |
第三个最有意思 💎 ——
它没学过"敏捷"这个组合,但它没崩,而是退回字符级照样给你切好。这就是"无 OOV"的工程美感。
🪞 连回我在搞的 Harness Engineering
⚠️ 这一段是 N=1 的直觉,不是方法论,只是一个让我停下来想了一会儿的联想。
BPE 的核心逻辑:热路径合并,冷路径退化。
- 🔥 常见搭配 → 合成一个大 token(节省预算)
- ❄️ 罕见搭配 → 退回字符(保证覆盖)
我在搞的 Harness Engineering 的编排逻辑:热路径合并,冷路径独立。
- 🔥 常见协作 → 固化成 skill / 合并成一个 Agent 调用
- ❄️ 罕见任务 → 临时拉一个新 Agent
同构 🎯。
都是"在有限预算下做自适应压缩"——只是一个压字数,一个压调用。
📋 打卡清单
- [x] 课程仓库 clone 到本地
- [x] 独立虚拟环境(
.venv)搭好 - [x] 第 1 章工具
wandb装好(占坑用) - [x] 第 2 章理论过一遍(1321 行,捡重点看)
- [x] BPE 代码跑通(25 merges + 3 case 可逆)
- [x] 打卡笔记发博客 ← 就是这篇
- [ ] 完整作业 Notebook(慢慢补)
- [ ] DeepSeek tokenizer 拆解(慢慢补)
- [ ] 四种分词器对比实测(慢慢补)
🌱 慢慢补
这个坑我会慢慢填——每个 Task 一篇,和《推理王国》并列成两条学习线。
目标:学完这 6 个作业,能手搓一个极简 LLM 从 0 到 1。
不为证明什么,就是想亲手摸一遍底层 🔧
Winnie · 2026-04-16
