🏠 总目录📚 本教程 03 · Tokenizer 与上下文
📑 本页目录(点开跳转)

03 · Tokenizer 与上下文

32 分钟 | ⭐ 小而实用,能直接省钱


🎯 一句话

模型不认识文字,只认识数字编号。Tokenizer 就是那本「字典」——它怎么切词,直接决定了你花多少钱、上下文能装多少、以及一些莫名其妙的 bug

维度位置(第几个 token)→低维(上面)变化快,高维(下面)变化慢每个位置拿到一串独一无二的「条纹指纹」不同频率的正弦/余弦叠在一起,像二进制计数器的各个位⭐ 为什么需要它:注意力本身是【和顺序无关】的 —— 打乱输入,结果完全一样所以位置信息必须显式加进去,否则模型分不清「猫追老鼠」和「老鼠追猫」
不同频率的正弦叠在一起,每个位置拿到一串独一无二的「条纹指纹」。⭐ 为什么需要它:注意力本身和顺序无关 —— 打乱输入结果完全一样。位置信息必须显式加进去,否则模型分不清「猫追老鼠」和「老鼠追猫」。
国王王后男人女人巴黎法国东京日本同一个方向同一个方向「首都」词向量里,「关系」变成了空间中的【方向】国王 − 男人 + 女人 ≈ 王后⭐ 没人教它「性别」这个概念 —— 它是从「谁和谁常一起出现」里自己长出来的
「关系」在向量空间里变成了方向:国王→王后 和 男人→女人 是同一个方向。⭐ 没人教它「性别」这个概念 —— 它是从「谁和谁常一起出现」里自己长出来的。

🧠 核心概念一:Token 不是「字」也不是「词」

hellohello常见词 → 1 个 tokenunhappinessunhappiness少见词 → 拆成词根词缀Antidisestab...Antidisestab生僻词 → 碎成很多片苹果好吃中文常常一字一 token —— 所以同样字数更费额度Token 不是「字」也不是「词」,是 BPE 统计出来的高频片段⭐ 越常见的东西占的 token 越少 —— 字典是「按出现频率合并」造出来的⚠️ 这解释了两个经典现象:模型数不清单词里的字母(它看不到字母),以及中文/代码的 token 消耗比英文散文高得多
越常见的东西占的 token 越少 —— 字典是按出现频率合并造出来的。⚠️ 这解释了两个经典现象:模型数不清单词里的字母(它根本看不到字母),以及中文和代码的 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 更贵,怎么省
让模型别做精确计算,给工具 现在知道根因:它没有「字符」概念

📦 深挖的话要学什么

📍 每条后面标了它在哪: 📗 本库有 = 站内已经讲透,点进去就行;📙 半有 = 站内讲了一半,另一半得外找; 📕 要外找 = 站内没有,得去论文或别处。 (这份清单以前只列词不说去哪,指到的坑有些站内根本没挖过,按图索骥会扑空。)

需要的前置:第 2 章 Transformer 基础。


⚖️ 要不要深挖

你的情况 建议
做应用、关心成本 本章内容就够用,实用价值已经拿到
处理中文/小语种/垂直领域大批量文本 ✅ 值得学「训练自己的分词器」,可能省 20–40% token
要做长上下文优化 ✅ 深挖位置编码和注意力变体
只用 API,量不大 ⛔ 不用深挖

🔑 我的判断:这块知识密度小但实用性高,本章的内容已经覆盖 80% 的日常价值。 除非你有「大批量中文处理」或「长上下文优化」的具体需求,否则不必单独深挖—— 建议并入第 2 章 Transformer 一起学


✅ 检查点

  1. 为什么中文通常比英文更费 token?
  2. 让模型输出结构化数据,JSON 和紧凑文本哪个省钱?什么情况下 JSON 那点 token 值得花?
  3. 估中文 token 量时,为什么不能用"字符数 ÷ 4"?
  4. 为什么模型数不清「strawberry 有几个 r」?该怎么解决?
  5. 上下文窗口撑大靠哪几项技术?
  6. 「大海捞针」测试通过为什么还不够?真实任务在多少窗口占用后开始下滑?
  7. 什么是"中间迷失"?它给出哪两条提示词布局建议?
👀 答案
  1. BPE 按语料频率合并,训练语料里英文占比高 → 英文常见词是 1 个 token,中文被切得更碎。
  2. 紧凑文本(CSV/TSV 比嵌套 JSON 能省 60%+)。但如果需要程序稳定解析输出,JSON 值得那点 token——省 token 不该以输出解析不稳定为代价。实用判断:输入侧用紧凑格式(白赚),输出侧看需不需要程序解析
  3. 中文约 0.7 字符/token,用 ÷4 会低估一倍以上
  4. 它看到的是子词块(如 str/aw/berry),没有"单个字符"的概念。解决:给它一个工具去算,别让它硬数。
  5. RoPE 位置编码可外推(+NTK/YaRN)、注意力优化(FlashAttention 省 IO、GQA/MQA 省 KV Cache)、长文档继续训练
  6. 因为 NIAH 只测单点检索(藏一句话再找出来),相对容易。真实任务要多跳、要分辨干扰项、要对全文归纳——这些在 20~30% 窗口占用后就开始下滑。实用结论:用到 30~50% 就该做检索/压缩
  7. 信息放在中间时利用率明显低于开头和结尾。两条建议:①指令放最后再重复一遍(结尾注意力强且离生成近)②不变的内容放最前面(既占位置优势,又能命中 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

打卡记录保存在你的浏览器里,首页能看到总进度