"2 年大模型开发经验,熟悉 GPT、Claude、Qwen 等主流模型 API 调用与应用开发"
看到这份简历,我问了个简单的问题:一个中文字在 GPT-4 里占几个 token?
候选人愣了几秒,说"应该是 1 个吧"。
错了。而且不只是数字错了,是对整个分词机制没有概念。Tokenizer 是大模型的第一道工序——文本进来先过分词器,切成 token,才能送进模型。分词切得好不好,直接影响模型理解能力、推理速度和 API 成本。
很多人天天调 API,却从没想过模型是怎么"看"文字的。今天这场面试,就把 Tokenizer 的核心问题讲透。
面试官:"大模型用的分词算法叫 BPE,你能讲讲它的原理吗?"
候选人:"BPE...就是把文本切成子词吧?按词频合并?大概就是这样。"
正解:
BPE 不需要任何语言学规则,纯靠统计频率就能学会怎么切词——这是它最反直觉的地方。
BPE(Byte Pair Encoding)最早是 1994 年 Philip Gage 提出的数据压缩算法,2016 年被 Sennrich 等人引入 NLP,现在 GPT、Llama、Claude 等主流模型全在用。
核心思路只有一句话:找到语料里出现频率最高的相邻 token 对,合并成一个新 token,不断重复。
具体流程:
Step 1:初始化词表。 把所有文本拆到最小单位。以 Byte-level BPE 为例,基础词表就是 256 个字节值。任何文字——中文、emoji、特殊符号——都能用字节表示,不会出现 OOV(未登录词)。
Step 2:统计频率。 遍历语料,数所有相邻 token 对出现了多少次。假设 "l" 和 "o" 紧挨着出现了 5000 次,是最高频的一对。
Step 3:合并。 把 "l" + "o" 合并成新 token "lo",加入词表。语料里所有 "l""o" 相邻的位置都替换成 "lo"。
Step 4:重复。 重新统计频率,继续合并。下一轮可能发现 "lo" 和 "w" 频率最高,合并成 "low"。BPE 就是这样层层叠加,从字节逐步构建出子词、词根、甚至完整单词。
合并持续到词表大小达到预设值为止。 GPT-2 的词表是 50,257,GPT-4 约 100,000,Llama 3 是 128,256。
推理时怎么切词? 不再看频率,而是按训练时学到的合并规则,按顺序依次执行。给定一段新文本,先拆成字节,然后按合并规则一步步合并,最终得到 token 序列。
一个直观的例子:BPE 训练时从来没见过 "lowest" 这个词,但它学到了 "low" 和 "est" 两个 token——分别是词根和后缀。所以 "lowest" 会被切成 ["low", "est"],模型依然能理解。这就是 BPE 处理未知词的方式,不需要整词匹配,靠子词组合就能覆盖。
要点速记
- BPE 核心:统计最高频的相邻 token 对 → 合并 → 重复
- 基础词表 256 个字节,不会出现 OOV
- 推理时按训练好的合并规则顺序执行,不看频率
- 主流词表大小:GPT-4 约 100K,Llama 3 为 128K
面试官:"Llama 2 的词表是 32K,Llama 3 直接扩到 128K,Gemini 更是到了 256K。词表越大越好吗?"
候选人:"词表大了,能表示的词就多了,应该效果更好吧?"
正解:
词表大小不是越大越好,而是和模型参数量存在幂律关系——模型越大,最优词表也越大。
NeurIPS 2024 有篇重要论文 Scaling Laws with Vocabulary(来源:Sea AI Lab & HKU),专门研究了这个问题。核心结论:大多数 LLM 的词表都太小了。以 Llama 2-70B 为例,最优词表应该是 216K 以上,但实际只有 32K——差了 7 倍。
词表大小影响什么?
压缩效率。
Embedding 层参数量。
多语言支持。
经验法则:
实验数据佐证: 同样的计算预算(2.3e21 FLOPs),把词表从 32K 扩到 43K,ARC-Challenge 准确率从 29.1 提升到 32.0,差了近 3 个百分点(来源:Scaling Laws with Vocabulary 论文)。
词表太大的代价: 超过 100K 之后边际收益递减。从 8K 到 32K 的 perplexity 下降很明显,从 100K 到 200K 改善就很小了,但 Embedding 层参数和内存占用却在线性增长。
前沿进展: SuperBPE(COLM 2025,来自华盛顿大学 + NVIDIA + AI2)提出两阶段 BPE——先学子词,再学跨词边界的超级词(如 "by accident"、"of course" 这样的固定搭配)。同样 200K 词表,SuperBPE 比标准 BPE 少 33% 的 token,在 30 个 benchmark 上平均提升 4.0%,MMLU 提升 8.2%。Moondream AI 已经在产品中采用了 SuperBPE,序列长度平均缩短 21%。
要点速记
- 词表大小和模型参数量呈幂律关系,大模型配大词表
- Llama 2 的 32K 词表对 70B 模型来说太小,最优值约 216K
- 词表扩大 → token 更少 → 推理更快,但 Embedding 层参数也增加
- SuperBPE(2025)同词表大小下减少 33% token,MMLU +8.2%
面试官:"同样的内容,中文调 GPT-4 的 API 费用是英文的好几倍,你知道为什么吗?"
候选人:"可能是...中文比较复杂?字数多?"
(面试官内心 OS):天天在调 API,连钱怎么花出去的都没想过...
正解:
不是中文复杂,而是 GPT-4 的分词器对中文不友好——同样的语义信息,中文被切成了更多的 token。
BPE 分词的合并规则是从训练语料里学来的。GPT-4 的训练数据以英文为主,英文字母组合频率高,能学到大量高效的合并规则("tion"、"ing"、"the" 都是单个 token)。中文在训练数据中占比低,汉字之间的组合频率不够高,很多常见词没法合并成单独 token。
具体数据对比:
同样 1000 字的内容,中文在 GPT-4 上比英文多出约 40% 的 token。换算成 API 费用,同一段文本的中文版本比英文版贵 30-50%。有人实测:同样一段文本,中文调 GPT API 花 30 美分,翻译成英文只要 10 美分。
跨语言不公平问题:
这不是某一个模型的问题,而是英文主导的 BPE 分词器的通病。一篇 2023 年的研究发现(来源:arxiv 2305.15425),同一段文本翻译成不同语言后,token 数量最多能差 15 倍。低资源语言(泰语、阿拉伯语等)被过度碎片化,序列变长,不仅推理慢、API 贵,还会因为上下文窗口被浪费而影响模型表现。
中文分词还有一个更隐蔽的坑:语义切割错误。
MIT Press 2025 年发表的研究发现,GPT-4、GPT-4o 和 Llama 3 在处理中文时,会被 token 边界误导。两个毫不相关的汉字,如果碰巧被切成了相同的开头 token,模型会错误地认为它们语义相似。LLaMA 系列还会错误合并不存在的词——把"的事"当成一个词,而正确的切分应该是"事物"。这类问题在日常聊天里不明显,但在情感分析、法律文本等需要精确语义理解的场景里,影响很大。
省钱的工程策略:
中文业务优先选中文优化模型。
混合语言策略。
推理语言切换。
要点速记
- GPT-4 中"机器学习"占 4 个 token,Qwen 里只占 2 个
- 同样内容中文比英文多 40% token,API 费用差 30-50%
- 中文业务优先用 Qwen/DeepSeek,token 效率提升 40-50%
- 隐蔽坑:token 边界会影响模型对中文语义的理解
面试官:"你知道 tiktoken 和 SentencePiece 的区别吗?什么时候用哪个?"
候选人:"tiktoken 是 OpenAI 的,SentencePiece 是 Google 的...其他区别不太清楚。"
正解:
两者都实现了 BPE,但实现方式不同,适用场景也不同。选错了不影响能不能跑,但影响效率和多语言效果。
核心区别在于处理顺序:
tiktoken 的流程:文本 → 正则预分词(按空格、标点切开)→ UTF-8 编码成字节 → 在字节上做 BPE 合并。它先用正则把文本切成粗粒度的块,然后在每个块内部做字节级 BPE,合并不能跨越块的边界。
SentencePiece 的流程:文本 → 直接在 Unicode 码点上做 BPE 合并,不需要预分词。空格被当成普通字符(用 ▁ 符号表示),和文字一视同仁地参与合并。
这个区别带来的实际影响:
多语言处理。
BPE 合并结果不同。
速度。
tiktoken 有一个关键限制:只支持推理,不支持训练。 想训练自己的分词器,得用 SentencePiece 或 HuggingFace Tokenizers 库。
各模型的选择:
Llama 3 从 SentencePiece 切换到 tiktoken 是个标志性事件。Meta 同时把词表从 32K 扩到 128K,编码效率和推理速度都有提升。
训练自己分词器的注意事项:
训练分词器不需要海量数据——几百万 token 就够了,因为分词器只需要学 token 出现频率,不需要理解语义。关键配置:
byte_fallback=True,否则遇到罕见字符会报 unknown token要点速记
- tiktoken:字节级 BPE,快(3-6 倍),但只能推理不能训练
- SentencePiece:码点级 BPE,多语言友好,支持训练 + 推理
- Llama 3 从 SentencePiece 换成 tiktoken,词表同时扩到 128K
- 自训练分词器:几百万 token 即可,必须设 byte_fallback=True
面试官:"在生产环境里,分词器有哪些容易被忽略的坑?"
候选人:"嗯...可能是特殊字符处理?Unicode 编码问题?"
正解:
分词器的坑比想象中多,很多问题表现出来像模型的 bug,实际上是分词器的锅。
2023 年有人发现,让 ChatGPT 复述 "SolidGoldMagikarp" 这个词,它回复的却是 "distribute"。这不是模型幻觉,而是 Glitch Token。
原因:SolidGoldMagikarp 是一个 Reddit 用户名,在分词器的训练语料(网页文本)里出现频率够高,被 BPE 合并成了单独的 token。但在模型的训练数据里,这个 token 几乎没出现过,Embedding 向量基本是随机值。模型遇到这个 token 就"懵"了。
Glitch Token 的典型症状(来源:ACM 2024 论文):
排查和防御:
让 GPT-4 算 1234 + 5678,有时会算错。问题不在 Transformer 的计算能力,在分词。
"480" 可能是一个 token,"481" 却被切成 "4" + "81"。数字的 token 切分是不规则的,模型无法稳定地对齐数位,自然算不准。GPT-4 的词表里甚至有 "693" 这样的三位数 token,但 "694" 可能就要拆成两个。
Llama 系列的做法是把所有数字逐位拆分——"1234" 切成 "1" "2" "3" "4",只需要 0-9 十个 token。这种方式让模型的算术能力明显提升,但代价是数字序列变长。DeepSeek V2 也采用了同样的策略。
工程建议:
这个坑更隐蔽。你写了一个 prompt 以 http: 结尾,期望模型补全 //www.example.com。但模型输出的是一个空格开头的奇怪 URL。
原因:训练时 :// 是一个完整的 token(GPT-2 里是 token 1358)。但 prompt 在 : 处截断了,模型看到的是单独的 : token(token 27),这和训练时的分布不一致,生成就跑偏了。
约 70% 的常见 token 都存在这个问题——它们是更长 token 的前缀,当 prompt 恰好在这里截断时,就会产生 token 边界偏差(来源:Guidance 文档)。
解决方案:Token Healing,由微软 Guidance 库(20K+ GitHub Stars)提出。核心思路:生成时回退一个 token,把 prompt 的最后一个 token 作为前缀约束,让生成的第一个 token 自然接上。
python
# Guidance 库示例
import guidance
model = guidance.models.Transformers("meta-llama/Llama-3-8B")
# Token healing 自动处理边界问题
result = model + "http:" + guidance.gen(max_tokens=20)Token Healing 目前主要在本地推理时有效(Guidance 支持 LlamaCpp 和 Transformers 后端),调 API 时无法直接控制。规避方式:prompt 尾部不要截断在单词或符号的中间,保持完整的 token 边界。
要点速记
- Glitch Token:词表里有但模型没训练过的 token,约 10% 词表是残留 token
- 数字切分不规则导致算术错误,Llama 用逐位切分方案缓解
- 70% 的常见 token 存在边界偏差风险,Guidance 的 Token Healing 可修复
- API 调用时注意 prompt 尾部不要截断在 token 中间
这场面试暴露了一个典型问题:很多人天天用大模型 API,对 token 这个概念的理解却停留在"计费单位"。
Token 不只是计费单位,它是模型看世界的最小粒度。分词器怎么切,决定了模型能不能理解你的输入、能不能准确生成、以及你要为此付多少钱。
建议:
---
分词器是大模型最被低估的组件。做应用的人忽略它,做训练的人也经常直接用现成的。但从 SuperBPE 到 LiteToken,学术界一直在挖掘这个方向的潜力——同样的模型参数,换个更好的分词器就能提升 4-8% 的 benchmark 成绩。
面试时能把分词器讲清楚的候选人不多,但讲得清楚的,通常对模型的理解也不会差。