📑 本页目录(点开跳转)
03 · Tokenizer 与上下文
⏱ 32 分钟 | ⭐ 小而实用,能直接省钱
🎯 一句话
模型不认识文字,只认识数字编号。Tokenizer 就是那本「字典」——它怎么切词,直接决定了你花多少钱、上下文能装多少、以及一些莫名其妙的 bug。
🧠 核心概念一:Token 不是「字」也不是「词」
英文 "unbelievable"
→ ["un", "believ", "able"] 3 个 token
英文 "hello world"
→ ["hello", " world"] 2 个 token(注意空格跟着后面的词!)
中文 "我喜欢机器学习"
→ ["我", "喜欢", "机器", "学习"] 4 个 token(好的分词器)
→ ["我","喜","欢","机","器","学","习"] 7 个(差的分词器)
代码 "def calculate_sum(a, b):"
→ ["def", " calculate", "_", "sum", "(", "a", ",", " b", "):"]
经验值(当量级感用,各模型不同):
| 内容 | 大致换算 |
|---|---|
| 英文 | 1 token ≈ 0.75 个单词 ≈ 4 个字符 |
| 中文 | 1 token ≈ 1–2 个汉字 |
| 代码 | token 密度高,符号多 |
| JSON | 很浪费——大量括号引号各占 token |
💰 直接的省钱推论:同样的信息, JSON > YAML > 紧凑文本(token 消耗从高到低)。 让模型输出结构化数据时,能用简洁格式就别用嵌套 JSON。
💰 一个能立刻省钱的对比
要表达同样 5 条记录:
嵌套 JSON(带缩进)
[{"name": "张三", "age": 28, "city": "北京"}, ...] ≈ 120 token
CSV / TSV
name,age,city
张三,28,北京 ≈ 45 token ⭐
→ 省 60%+,而且模型解析得一样好
⚠️ 但有个例外:如果你需要程序稳定解析输出,JSON 仍然值得那点 token—— 它有成熟的解析器和 schema 校验。省 token 不该以"输出解析不稳定"为代价。
🔑 实用判断:输入侧用紧凑格式(你写的),输出侧看需不需要程序解析。 输入侧的节省是白赚的,因为模型读什么格式都一样懂。
🧮 怎么估自己的 token 量
# 最靠谱的办法:用对应模型的官方 tokenizer 实测
# 没有的话,粗略估算:
def rough_tokens(text):
zh = sum('一' <= c <= '鿿' for c in text)
return int(zh * 0.7 + (len(text) - zh) / 4) # 中文≈0.7,其他≈4字符/token
⚠️ 别用"字符数 ÷ 4"估中文——那会低估一倍以上。
🧠 核心概念二:BPE —— 字典是怎么造出来的
BPE(Byte Pair Encoding) 的思路简单得出奇:
① 一开始每个字符都是一个 token
② 统计哪两个相邻 token 一起出现最频繁
③ 把这一对合并成一个新 token,加进字典
④ 重复几万次,直到字典达到目标大小(如 15 万)
结果:常见词变成 1 个 token,罕见词被拆成几块
「the」→ 1 个 「antidisestablishmentarianism」→ 7 个
💡 这解释了一个关键现象:
训练语料里出现多的语言,token 效率就高。 早期模型英文语料占绝大多数 → 中文被切得很碎 → 同样内容中文烧掉更多 token。 现在的模型改善很多,但英文仍普遍比中文省 token。
🐛 核心概念三:Tokenizer 导致的经典 bug
这些不是段子,是真实存在的现象:
| 现象 | 原因 |
|---|---|
| 数不清「strawberry 有几个 r」 | 模型看到的是 ["str","aw","berry"],根本看不到单个字母 |
| 算术出错,尤其大数 | 数字被切分方式不一致:1234 可能是 ["123","4"] |
| 反转字符串失败 | 同上,它没有「字符」这个概念 |
| 奇怪的 token 触发怪异行为 | 训练语料里的垃圾字符串(如某些论坛 ID)成了独立 token,但几乎没被训练过 |
🔑 实用推论:需要精确字符级操作(计数、反转、精确切分)时,别让模型硬算——给它一个工具(智能体教程第 7 章的工具设计)。
🧠 核心概念四:上下文窗口是怎么撑大的
上下文从 4K → 100 万,靠的不是硬堆,而是几项技术叠加:
① 位置编码可外推
RoPE(旋转位置编码)→ 通过调整"频率"让模型适应比训练时更长的序列
(NTK 缩放、YaRN 等外推方法)
② 注意力优化
FlashAttention → 不省计算量,但大幅省显存读写(IO 感知)
GQA/MQA → 多个 Q 头共享 K/V 头,KV Cache 直接省几倍
③ 长上下文继续训练
在长文档上专门再训一轮,让模型真的"会用"长上下文
⚠️ 但要记住第 2 章的结论:
窗口能装 ≠ 用得好。 支持 100 万 token 不代表塞满 100 万效果最好—— 注意力仍是 n²,腐烂仍然存在。「大海捞针」测试通过,不等于复杂推理也稳。
🎣 为什么"大海捞针"通过了还是不够
大海捞针(NIAH)测的是:
把一句话藏进 100 万 token 里,模型能不能找出来
→ 这是【单点检索】任务,相对容易 ⭐
真实任务往往需要:
· 同时用上分散在 5 个位置的信息(多跳)
· 分辨相似但不同的多条信息(干扰项)
· 对整个长文档做归纳(不是找一句)
⚠️ 这些任务的表现,往往在 20~30% 窗口占用后就开始下滑
🔑 实用结论:别把"支持 200K 上下文"当成"可以塞 200K"。 一个稳妥的经验:用到窗口的 30~50% 时就该考虑做检索/压缩了, 而不是等到塞满。 🔗 这就是《智能体工程教程》上下文工程那一章的全部动机。
📍 位置也会影响效果:"中间迷失"
把关键信息放在不同位置,模型的利用率不一样:
开头 ████████████ 好
中间 ████ 差 ⭐ ← "Lost in the Middle"
结尾 ███████████ 好
→ 重要的东西放【开头或结尾】,别埋在中间
💡 两条直接可用的提示词布局建议:
| 建议 | 理由 |
|---|---|
| 指令放最后再重复一遍 | 结尾位置注意力强,且离生成最近 |
| 不变的内容放最前面 | 既利用了开头位置优势,又能命中 Prefix Caching ⭐ |
🔗 和你已知的关系
左列有的来自日常用 LLM 的经验,有的在站内别的板块讲过——碰上过哪几条就从哪几条接进来,一条没碰过也不影响读这一章。
| 你可能已经知道的 | 这一章的补充 |
|---|---|
| 上下文有限、要精简(智能体第 4 章) | 现在你知道「限」在哪:token 数、KV Cache 显存、注意力 n² |
| token 计费 | 知道了为什么中文/JSON 更贵,怎么省 |
| 让模型别做精确计算,给工具 | 现在知道根因:它没有「字符」概念 |
📦 深挖的话要学什么
📍 每条后面标了它在哪: 📗 本库有 = 站内已经讲透,点进去就行;📙 半有 = 站内讲了一半,另一半得外找; 📕 要外找 = 站内没有,得去论文或别处。 (这份清单以前只列词不说去哪,指到的坑有些站内根本没挖过,按图索骥会扑空。)
- 算法:BPE / WordPiece / Unigram / SentencePiece 的区别与取舍 → 📙 半有:BPE 的合并流程本章讲了;Kaggle 05 · 预训练模型详解 补了 Byte-level BPE(以字节而不是字符为单位,天然覆盖全部 Unicode,RoBERTa 的词表因此是 50265)——这解释了本章"差的分词器把中文切成一个字一个 token"是怎么被绕过去的。但 WordPiece / Unigram / SentencePiece 三者的取舍站内是空的,要外找(SentencePiece 项目自带的说明是最省事的起点)
- 实操:用
tokenizers库训练自己的分词器(垂直领域、小语种能显著提效) → 📕 要外找:站内没有训练分词器的可跑代码。⚠️ 而这恰恰是本章下面「⚖️ 要不要深挖」表里唯一标了「值得学」的一项——想省那 20–40% token,本库帮不上,得去 HuggingFacetokenizers的官方文档 - 词表设计:词表大小 vs 序列长度的权衡、多语言词表的公平性问题 → 📕 要外找:站内只出现过孤立的词表大小数字(50265),没有「词表变大 → 序列变短 → 但 embedding 层和 softmax 变贵」这笔权衡账,也没有多语言公平性的量化方法
- 位置编码:绝对 → 相对 → RoPE → 外推方法(NTK/YaRN)的演进
→ 📗 本库有:02b · 现代 LLM 架构的四处改动 的 RoPE 一节 + 「长度外推:PI / NTK / YaRN 到底在动什么」。本章只说了「RoPE 通过调整频率适应更长序列」就停住了,那一章把「频率」是什么讲开了(钟表类比:高频维度像秒针管邻近关系、低频像时针管长距离),并说清三种外推动的是同一个东西;还给了实践里最常用的一招——把
rope_theta从 10000 改成 1000000 再在长数据上续训一小段 - 长上下文工程:FlashAttention 原理、GQA、环形注意力、稀疏注意力 → 📙 半有:前三项站内都有——FlashAttention 在 AI基础设施 08(为什么"不省计算只省 IO"反而快),GQA 在 AI基础设施 16 · KV-Cache(MHA/MQA/GQA/MLA 四种的存储形态对比),环形注意力在 AI基础设施 14 · 并行策略怎么组合(上下文并行,和 FlashAttention 同源:一个片上分块、一个跨卡分块,128K+ 训练的必需品)。只有稀疏/滑窗注意力站内是空的,要外找
- 评估:大海捞针(NIAH)、RULER 等长上下文基准的设计与局限 → 📙 半有:「NIAH 只测检索不测推理」这个结论本章和 智能体工程 04 · 上下文工程 都讲了(后者还给了上下文腐烂的三个架构性原因);评测集本身怎么造——抽样、多大才够、怎么查污染——在 数据这一关 13 · 评测集也是数据。但 RULER 这类基准具体怎么设计多跳题和干扰项,站内没有,要外找
需要的前置:第 2 章 Transformer 基础。
⚖️ 要不要深挖
| 你的情况 | 建议 |
|---|---|
| 做应用、关心成本 | ✅ 本章内容就够用,实用价值已经拿到 |
| 处理中文/小语种/垂直领域大批量文本 | ✅ 值得学「训练自己的分词器」,可能省 20–40% token |
| 要做长上下文优化 | ✅ 深挖位置编码和注意力变体 |
| 只用 API,量不大 | ⛔ 不用深挖 |
🔑 我的判断:这块知识密度小但实用性高,本章的内容已经覆盖 80% 的日常价值。 除非你有「大批量中文处理」或「长上下文优化」的具体需求,否则不必单独深挖—— 建议并入第 2 章 Transformer 一起学。
✅ 检查点
- 为什么中文通常比英文更费 token?
- 让模型输出结构化数据,JSON 和紧凑文本哪个省钱?什么情况下 JSON 那点 token 值得花?
- 估中文 token 量时,为什么不能用"字符数 ÷ 4"?
- 为什么模型数不清「strawberry 有几个 r」?该怎么解决?
- 上下文窗口撑大靠哪几项技术?
- 「大海捞针」测试通过为什么还不够?真实任务在多少窗口占用后开始下滑?
- 什么是"中间迷失"?它给出哪两条提示词布局建议?
👀 答案
- BPE 按语料频率合并,训练语料里英文占比高 → 英文常见词是 1 个 token,中文被切得更碎。
- 紧凑文本(CSV/TSV 比嵌套 JSON 能省 60%+)。但如果需要程序稳定解析输出,JSON 值得那点 token——省 token 不该以输出解析不稳定为代价。实用判断:输入侧用紧凑格式(白赚),输出侧看需不需要程序解析。
- 中文约 0.7 字符/token,用 ÷4 会低估一倍以上。
- 它看到的是子词块(如 str/aw/berry),没有"单个字符"的概念。解决:给它一个工具去算,别让它硬数。
- RoPE 位置编码可外推(+NTK/YaRN)、注意力优化(FlashAttention 省 IO、GQA/MQA 省 KV Cache)、长文档继续训练。
- 因为 NIAH 只测单点检索(藏一句话再找出来),相对容易。真实任务要多跳、要分辨干扰项、要对全文归纳——这些在 20~30% 窗口占用后就开始下滑。实用结论:用到 30~50% 就该做检索/压缩。
- 信息放在中间时利用率明显低于开头和结尾。两条建议:①指令放最后再重复一遍(结尾注意力强且离生成近)②不变的内容放最前面(既占位置优势,又能命中 Prefix Caching)。
🛑 可以停在这里
⚡ 走神救援
Token≠字≠词,是BPE按频率合并出的子词块。中文/JSON更费token——CSV比嵌套JSON省60%+,但需要程序稳定解析时JSON值得那点token;实用判断:输入侧用紧凑格式(白赚),输出侧看需不需要解析;⚠️估中文token别用"字符÷4",中文约0.7字符/token,会低估一倍。模型没有"字符"概念→数不清字母、算术出错→用工具解决。长上下文靠RoPE外推+FlashAttention(省IO)+GQA(省KV Cache)+长文继续训练,但能装≠用得好:⭐大海捞针只测单点检索,真实任务(多跳/有干扰项/全文归纳)在20~30%窗口占用后就开始下滑 → 用到30~50%就该做检索或压缩,别等塞满。⭐"中间迷失":信息放中间利用率明显低 → 两条布局建议:指令放最后再重复一遍、不变的内容放最前面(既占位置优势又命中Prefix Caching)。本章内容已够日常用,建议并入Transformer一起学。
下一节 👉 04-训练三阶段.md