Token 效率:同样的文本,不同 Tokenizer 编码后 token 数差多少?对训练成本的影响

你有没有发现一个怪现象:同样一个意思,用英文写,提示词能装下好几页;换成中文,三四千字就把上下文撑满了。我最早以为是中文表达太“密”,后来把 tokenizer 的输出打出来一看,差点以为是 bug——一句 10 个字的中文,被编码成了 20 多个 token。这跟模型聪不聪明没关系,纯粹是 tokenizer 在切分环节多做了一大笔“生意”。

AI technology illustration

Tokenizer 看起来只是“预处理”:把文字映射成数字而已。但它直接决定你的提示词能装多少内容、训练语料有多少 token、以及账单上的数字。今天我把这笔账算清楚。

Tokenizer 干的活:把文本切成模型数得清的“词块”

语言模型不读文字,它读整数。Tokenizer 负责把文字变成 id。“我爱自然语言处理”可能被切成这样:

[我][爱][自然][语言][处理]  // 5 个 token
[我爱][自然语言][处理] // 3 个 token

到底是按字切、按词切、还是按半个词切,取决于词表和合并规则。现在大模型最常用的 BPE(Byte Pair Encoding)思路很直白:先拆成最小的字符,统计相邻字符对的出现次数,把高频对合并成一个 token,反复合并,直到词表达到目标大小。

“自然语言”频繁出现时,过程是这样的:

[自][然][语][言] → [自然][语][言] → [自然][语言]

这套算法最早是搞数据压缩的,后来被 Sennrich 等人引入 NLP,处理神经机器翻译中的稀有词。大模型后来普遍使用字节级变体,中文这种没有空格的字符也能用字节兜底,不会出现 OOV。

BPE、WordPiece、Unigram、SentencePiece 是什么关系

算法/框架 核心标准 代表模型 一句话解释
BPE 相邻字符对出现频率 GPT、Llama、Qwen 谁出现得多,就把谁焊在一起
WordPiece 合并后语言模型似然增益 BERT 不仅看频率,还要看合并后是否更“像”词
Unigram 删掉哪些 token 让整句损失最小 ALBERT、部分 T5 从大词表出发,剪到最合适的大小
SentencePiece 预处理框架,内部跑 BPE/Unigram Llama、Gemma 把空格也当一个普通符号,适合中文、日文

注意 SentencePiece 不是算法,而是一套训练/预处理框架。它的特殊之处是把空格当作普通符号,不依赖语言确实有空格分隔,所以很适合中文、日文这种没有空格的语言。

差多少:同样语义,token 数能差两三倍

下面是一个具体例子。英文句 The quick brown fox jumps over the lazy dog. 和中文语义等价句 敏捷的棕色狐狸跳过懒狗。,用三类典型 tokenizer 编码,token 数大致如下。数字不是严格 benchmark,不同版本会有出入,重点看比例:

Tokenizer 类型 英文句 中文句
英文单语字节 BPE(类似 GPT-2) 10 约 22
多语言 SentencePiece(类似 Llama 2) 10 约 12
中文增强 BPE(类似 Qwen 系列) 10 约 9

同一句英文,三者差不多,因为空格分隔天然适合 BPE,高频英文单词几乎都变成了单个 token。同一句中文,差距就从约 22 降到约 9,达到两倍多。换成一篇 5000 字的中文文档,浪费的 token 是成千上万的。

为什么中文会被切成这么多 token

  • 预分词规则:英文 tokenizer 依赖空格把单词分开,再按词合并;中文没有空格,如果训练语料里中文占比低,每个汉字只能以单个字符或字节形式留下。
  • 词表语言覆盖:GPT-2 这类 tokenizer 的词汇表大半由英文子词构成,中文字符很少出现在合并路径上。
  • 训练语料分布:Qwen 这类 tokenizer 在中文语料上训练过,不仅常用汉字是 token,连“狐狸”“跳过”这种双字组合也常被合并成一个 token。

训练成本:token 少 20%,不是省 20%

如果用来训练模型,差距会被放大成真金白银。假设语料是 1 亿字符的中文文本,低效 tokenizer 可能切出 2 亿 token,高效 tokenizer 只要 9 千万。业内常用的估算公式是 总计算量 ≈ 6 × 模型参数量 × token 数,具体推导可参考 Chinchilla 论文。多出来的 1.1 亿 token,就是多出来一截训练账单。

更麻烦的是长文档。Transformer 的 attention 理论复杂度是 O(n²):一段本来 2000 token 的文档,被切碎后变成 4000 token,attention 层的计算量不是 2 倍,而是 4 倍。所以 tokenizer 效率对训练成本的影响经常被低估。

别把所有常用词都塞进词表

我以前为压缩 token 做过一件傻事:把词表调得很大,把中文常用词尽量变成单个 token。结果模型在新词上泛化很差。后来才想明白,子词粒度恰恰是模型能组合出新词的原因。“自然语言处理”是 1 个 token,不等于模型就理解它;保留“自然”“语言”这些子模块,反而能让没见过的新组合也被拼出来。

因此 token 效率不是唯一指标。词表越大,embedding 表和 softmax 输出层也越大,训练和推理参数都会变多;过度合并还会伤害泛化。选 tokenizer 是在“省 token”和“保持子词可组合性”之间做平衡。

FAQ:三个容易搞混的问题

API 能自己选 tokenizer 吗?

不能。模型权重和 tokenizer 是绑定的,计费也按它自己的 encode 结果来。OpenAI 的 官方说明也写得很清楚:按 token 计价。你唯一能做的是改写 prompt,减少冗余空格、标点和重复表达。

词表越大 token 一定越少吗?

不一定。词表增长到一定程度后会饱和,多出来的词频几乎为零,却增加参数和训练开销。更常见的做法是让 tokenizer 在目标语料上多训练几轮,更合理地分配合并。

换 tokenizer 能提升效率吗?

能,但已发布模型换 tokenizer 要重新训练 embedding 层,很不划算。对从零预训练的模型,则值得在训练前多测几个候选 tokenizer。

我的建议:把 tokenizer 当成一个训练超参来调

我在训练前一定会拿真实语料给至少三个 tokenizer 跑一遍,统计 token-per-byte、平均序列长度、P99 序列长度以及稀有 token 覆盖率。不要只看平均,还要看长尾样本会不会把 max sequence length 推高。

短期看,tokenizer 不会消失。像 Byte Latent Transformer、MambaByte 这类想跳过 tokenizer 的方向还在探索阶段,还没有成为主流。但至少在这个时代,tokenizer 效率就是训练成本的一部分。早花 10 分钟评估,后面能省一大笔钱。

想自己验证?用两行代码跑 tokenizer

用 Hugging Face 的 transformers 跑一下:

from transformers import AutoTokenizer
for name in ["gpt2", "Qwen/Qwen2.5-1.5B-Instruct"]:
tok = AutoTokenizer.from_pretrained(name)
for text in ["The quick brown fox jumps over the lazy dog.", "敏捷的棕色狐狸跳过懒狗。"]:
print(name, len(tok.encode(text)))

如果只关心 OpenAI 系,可以装 tiktoken。想了解更底层的切分机制,看 Hugging Face Tokenizers 文档

原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/750.html

(0)
上一篇 24分钟前
下一篇 9分钟前

相关推荐