Token 怎么数:BPE 与隐形的 Token 税
从字符到子词的演进、BPE 的合并规则,以及中文为什么天生贵 2 倍;「给主人留下些什么吧」的诡异合并
本页解决的问题
先给结论「Token 怎么数:BPE 与隐形的 Token 税」要解决的关键问题是什么?
从字符到子词的演进、BPE 的合并规则,以及中文为什么天生贵 2 倍;「给主人留下些什么吧」的诡异合并
把成本看成形状,不是一个数字。 把一次请求拆成输入、输出、重试、工具和等待时间。使用量的形状,通常能告诉你是哪项设计选择最贵、哪里值得先改。
优化想象中的平均值之前,先测一次真实请求。
调用便宜了,却悄悄增加了重试、延迟或人工复核。
早期 NLP 走过两个极端。词级别分词语义明确,但英语几十万个单词加上词形变化(look / looks / looking / looked),词表直接爆炸,还处理不了没见过的新词(OOV 问题)。字符级别分词什么词都拆得开,但序列太长、单个字符信息量太低,推理效率极差。
现代大模型选择了中间路线——子词分词(Subword Tokenization):常见的词保留为完整 Token,不常见的词拆成有意义的片段。词表控制在 32k 到 200k 之间,既不爆炸,又什么都能表示。
GPT 系列、Llama 3 都用 BPE(Byte-Pair Encoding,字节对编码)。它的词表不是人工编的,而是从语料里「合并」出来的:
初始化:把语料库全部拆成最小单位(对 Unicode 文本先做 UTF-8 编码,以字节为单位)。
统计频次:数一数所有相邻字节对在语料库里出现的频率。
合并最频繁的一对:比如 "u" 和 "g" 经常相邻,就合并成新符号 "ug" 加入词表。
迭代:重复第 2、3 步,直到词表达到预设大小——Llama 2 是 32,000,GPT-4 的 cl100k_base 是 100,277。
推理时反过来查:常见词 "hug" 直接是一个 Token;生僻词 "bug" 拆成 ["b", "ug"] 两个。词表越大、你的文本越「常见」,Token 就越少,账单就越薄。这就是为什么 GPT-4 换用 10 万词表后,还专门把多层缩进的空格序列并成单一 Token——代码生成的效率直接翻了几倍。
主流模型的 Tokenizer 训练语料以英文为主,BPE 学到的合并规则高度偏向英语词汇。结果是:非拉丁语系(中文、日文、韩文)在 Token 化时面临严重的效率劣势,业界称之为「Token 税」。
| 语言 | 示例 | Token 数 | 比率 | 现象解释 |
|---|---|---|---|---|
| 英语 | Donald John Trump | 3 | ~0.75 Token/词 | 常见单词直接映射为 1 个 Token |
| 中文 | 唐纳德·约翰·特朗普 | 6 | ~1.5 – 2.5 Token/字 | 常见字是 1 个 Token,生僻字被拆成 2–3 个字节 Token |
| 韩语 | 도널드 존 트럼프 | 7 | ~1.5 – 3.0 Token/字 | 编码空间覆盖不足,经常回退到字节编码 |
表达同样的语义,中文用户要消耗比英文用户多 2 倍以上的 Token:既多付了 API 的钱,又变相缩短了上下文窗口。
点选一句话,对比它的中英文版本各要消耗多少 Token(按典型比率估算)。想看精确切分,去 OpenAI Tokenizer 亲手试。
英文有天然的护栏:BPE 合并前先按空格预分词,合并只发生在单词内部,Token 边界基本符合语言学直觉。中文没有空格,BPE 只能完全依赖统计共现频率来决定边界——一段文本(哪怕是一句毫无逻辑的广告语)在语料里重复出现千万次,BPE 就会把它整个合并成一个「不可分割的最小语义单元」。
最有名的例子是「给主人留下些什么吧」:这句网络博客时代的高频留言,在 GPT 的词表里被强行合并成了独立 Token,成了著名的「故障 Token」。这不是段子,是 BPE 统计逻辑的必然产物。
Token 是统计出来的子词,不是字符也不是单词。BPE 按语料频率迭代合并,词表大小是超参数。
中文天生要交 Token 税:同样语义比英文多花 2 倍以上,还变相缩短上下文窗口。估算预算时别按英文经验拍脑袋。
词表决定压缩率。cl100k_base 十万词表 + 空格合并优化,是 GPT-4 代码效率高的直接原因。选模型时 Tokenizer 效率也是成本参数。
内容来源:整理自作者团队内部分享《AI Token 降本增效策略分享》第一部分「Token 的构成」。BPE 细节可参考 OpenAI Tokenizer 与 tiktoken 开源库。想复习分词和词表的底层原理,回看大模型原理 · 词表与训练。
「从字符到子词:两个极端的平衡点」的完整成本怎么算
「早期 NLP 走过两个极端。」提醒我们,AI 成本不是单价乘一次调用这么简单。输入、输出、重试、工具调用、等待时间和人工收尾,会共同决定一次任务到底贵不贵。
先找出账单里不断重复的部分
「现代大模型选择了中间路线—— 子词分词(Subword Tokenization) :常见的词保留为完整 Token,不常见的词拆成有意义的片段。词表控制在 32k 到 200k 之间,既不爆炸,又什么都能表示」对应的关键变量通常是上下文是否每轮重复发送、输出是否过长、失败是否触发重试,以及每次调用是否真的带来有用结果。减少无效 Token 往往同时降低费用、延迟和并发压力。
便宜的单次调用可能换来更贵的全流程
以「词表决定压缩率。」为起点做一张小表:输入、输出、重试、工具和人工复核分别花了什么,再比较优化前后的质量,避免只看某一项价格。
从「从字符到子词:两个极端的平衡点」走到「BPE:按出现频率合并出来的词表」
「从字符到子词:两个极端的平衡点」先把问题落在「早期 NLP 走过两个极端。 词级别分词 语义明确,但英语几十万个单词加上词形变化(look / looks / looking / looked),词表直接爆炸,还处理不了没见过的新词(OOV 问题)。 字符级别分词 什么词都拆得开,但序列太长、单个字符信息量太低,推理效率极差」上;到了「BPE:按出现频率合并出来的词表」,讨论继续推进到「GPT 系列、Llama 3 都用 BPE(Byte-Pair Encoding,字节对编码) 。它的词表不是人工编的,而是从语料里「合并」出来的」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。
把这条判断带到下一个场景
分析成本时,先画出完整交互,再找每轮重复的输入、无效输出和失败重试;单次调用便宜,并不代表整个任务便宜。
- 「从字符到子词:两个极端的平衡点」:早期 NLP 走过两个极端。 词级别分词 语义明确,但英语几十万个单词加上词形变化(look / looks / looking / looked),词表直接爆炸,还处理不了没见过的新词(OOV 问题)。 字符级别分词 什么词都拆得开,但序列太长、单个字符信息量太低,推理效率极差
- 「BPE:按出现频率合并出来的词表」:GPT 系列、Llama 3 都用 BPE(Byte-Pair Encoding,字节对编码) 。它的词表不是人工编的,而是从语料里「合并」出来的
- 「最后的要点」:推理时反过来查:常见词 "hug" 直接是一个 Token;生僻词 "bug" 拆成 ["b", "ug"] 两个。 词表越大、你的文本越「常见」,Token 就越少,账单就越薄。 这就是为什么 GPT-4 换用 10 万词表后,还专门把多层缩进的空格序列并成单一 Token——代码生成的效率直接翻了几倍
最后的「最后的要点」把讨论落到「推理时反过来查:常见词 "hug" 直接是一个 Token;生僻词 "bug" 拆成 ["b", "ug"] 两个。 词表越大、你的文本越「常见」,Token 就越少,账单就越薄。 这就是为什么 GPT-4 换用 10 万词表后,还专门把多层缩进的空格序列并成单一 Token——代码生成的效率直接翻了几倍」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。
我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。
读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。
如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。
还没有这篇文章的讨论。