在 GPT 大语言模型中,tokenizer 是连接原始文本和模型理解的桥梁。本文将为你系统性地解析 GPT tokenizer 的工作原理,并通过大量实例和工具演示,帮助你直观理解 token 的生成过程、奇特现象以及如何在实际开发中应用。内容源自开源技术社区,由 Django 联合创始人 Simon Willison 撰写。
1. tokenizer 是什么?为什么重要?
在 GPT 模型中,tokenization(词元化) 指的是将用户输入的文本分割成 token(词元) 的过程。这是模型理解输入文本的词义、句法和语义的关键预处理步骤,直接影响模型输出的质量和连贯性。
tokenizer(词元生成器) 是执行这一操作的工具或组件。它负责将原始文本转换成模型可以处理的数字形式(即 token ID 序列),为 GPT 的生成与推理提供基础能力。
2. 直观理解 token:使用可视化工具
OpenAI 提供了一个官方 tokenizer 供探索。作者 Simon Willison 则构建了一个更直观的 Observable notebook 工具,可以在其中进行文本与 token 的相互转换,并搜索整个 token 表。
该工具界面如下:

以这段混合语言的文本为例:
The dog eats the apples El perro come las manzanas 片仮名
在该工具中,这段文本被切分为 21 个整数 token:
- 5 个 token 对应英文文本
- 8 个 token 对应西班牙文本
- 6 个 token(每个字符 2 个)对应三个日文字符
- 2 个换行符也各自对应一个整数 token
小提示: 该工具基于 GPT-2 的 tokenizer 构建,主要作为教育工具使用。GPT-3 及更高版本(如 GPT-4)的 tokenizer 与其存在些许差异。
2.1 常见英语单词的 token
大多数常见的英语单词都分配了一个唯一的 token。在示例中:
- “The”:464
- “ dog”:3290(注意前面的空格)
- “ eats”:25365
- “ the”:262
- “ apples”:22514
重要观察: 字母的大小写非常重要!以单词 “the” 为例,大写字母 T 的 “The” 对应的 token 是 464,而小写字母 t 开头且包含前导空格的 “ the” 对应的 token 是 262。许多单词的 token 都包含了前导空格,这样无需为每个空格字符使用一个额外 token,编码效率更高。
2.2 对其他语言(西班牙语)的切分
西班牙语 “El perro come las manzanas” 的编码如下:
- “El”:9527
- “ per”:583
- “ro”:305
- “ come”:1282
- “ las”:39990
- “ man”:582
- “zan”:15201
- “as”:292
从以上案例可以看出对英语的明显偏向。因为 “man” 是一个英语单词,其 token ID 较低(582)。而 “zan” 虽然不是一个独立的英语单词,但由于它是常见的字符序列,也拥有自己的 token(15201)。
2.3 对日语字符的切分
某些语言(如日语)的单个字符可能被编码为多个 token:
- 片:31965, 229
- 仮:20015, 106
- 名:28938, 235
3. 奇特的“故障 token”
“故障 token”(glitch tokens)是 tokenizer 中一类令人着迷的特殊 token。其中一个有趣的例子是 token 23282,对应字符串 “da vidjl”。

Scale AI 的 prompt 工程师 Riley Goodside 指出了与该 token 相关的奇怪行为:

为什么会发生这种情况?
- token 23282 可能与 Reddit 上的用户 “da vidjl123” 有关。该用户是 /r/counting 子论坛的活跃用户,经常发布递增数字,已发布过 163,000 多次此类帖子。
- /r/counting 子论坛的数据最终被用于训练 GPT-2 的 tokenizer。由于该用户出现数十万次,最终获得了属于自己的 token。
Hacker News 用户 @londons_explore 给出了最佳解释:
这些故障 token 都位于 token 嵌入空间的中心附近。模型在区分这些 token 和其他位于嵌入空间中心附近的 token 时存在困难,因此当被要求“重复”这些 token 时,模型会选择错误的 token。这种情况发生的原因是,这些 token 在互联网上出现了很多次,但本身并不难以预测(因此在训练过程中,梯度几乎变为零,嵌入向量会衰减到零,这是某些优化器在归一化权重时的操作)。
了解更多: 在 LessWrong 上搜索 “SolidGoldMagikarp (plus, prompt generation)” 可以找到对这种现象的详细说明。
4. 使用 tiktoken 进行 token 计数
OpenAI 的模型都有 token 限制。在将文本传递给 API 之前,经常需要计算字符串中的 token 数量,以确保不超出限制。
一个典型的技术用例是 检索增强生成(Retrieval Augmented Generation, RAG):通过对文档语料库执行搜索来回答用户问题,提取最相关的内容作为上下文包含在 prompt 中。成功实现这种模式的关键是在 token 限制内包含尽可能多的相关上下文,因此准确计算 token 数量至关重要。
OpenAI 提供了名为 tiktoken 的 Python 库来实现这一功能:
tiktoken 目前包含五种不同的切分方案:r50k_base、p50k_base、p50k_edit、cl100k_base 和 gpt2。
其中 cl100k_base 是最相关的,它是 GPT-4 和当前 ChatGPT 使用的经济型 gpt-3.5-turbo 模型的 tokenizer。text-da vinci-003 则使用 p50k_base。完整的模型与 tokenizer 映射可以在 tiktoken/model.py 的 MODEL_TO_ENCODING 词典中找到。
4.1 tiktoken 代码示例
以下是如何使用 tiktoken 的代码示例:
import tiktoken
encoding = tiktoken.encoding_for_model("gpt-4")
# or "gpt-3.5-turbo" or "text-da vinci-003"
tokens = encoding.encode("Here is some text")
token_count = len(tokens)
现在 token 将是一个包含四个整数 token ID 的数组——在该例中是 [8586, 374, 1063, 1495]。
使用 .decode() 方法可以将整数 ID 数组转换回文本:
text = encoding.decode(tokens)
# 'Here is some text'
提示: 第一次调用 encoding_for_model() 时,编码数据会通过 HTTP 从 openaipublic.blob.core.windows.net 的 Azure Blob 存储桶获取。这些数据会被缓存在临时目录中。如果机器重启,缓存将被清除。可以通过设置 TIKTOKEN_CACHE_DIR 环境变量来指定持久化的缓存目录,避免重复下载。
5. 命令行工具 ttok
ttok 是 tiktoken 的命令行封装工具,主要提供两个关键功能:
- 计算 token 数量:计算输入文本中的 token 数
- 文本截断:将文本截断为指定数量的 token
5.1 使用示例
计算 token 数量:
# Count tokens
echo -n "Count these tokens" | ttok
# Outputs: 3 (the newline is skipped thanks to echo -n)
截断文本:
# Truncation
curl 'https://simonwillison.net/' | strip-tags -m | ttok -t 6
# Outputs: Simon Willison’s Weblog
查看整数 token ID:
# View integer token IDs
echo "Show these tokens" | ttok --tokens
# Outputs: 7968 1521 11460 198
可以使用 -m gpt2 或类似选项选择适用于不同模型的编码。
6. 观察 token 生成过程
理解 token 之后,GPT 模型生成文本的方式会更加清晰。GPT-4 的速度略慢于 3.5 版本,可以更容易地观察到其逐 token 生成过程。
以下示例使用 llm CLI 工具从 GPT-4 生成文本的结果,命令是:llm -s 'Five names for a pet pelican' -4:

如你所见,不在词典中的名字(如 “Pelly”)占据了多个 token,而 “Captain Gulliver” 作为一个整体输出了 token “Captain”。
常见问题
问题1:为什么故障 token 会导致模型出现奇怪的行为?
答案: 故障 token(如 “da vidjl”)位于词嵌入空间的中心区域,模型难以区分它们与其他中心附近的 token。由于这些 token 在训练数据中间出现频率极高(例如 Reddit 用户发布了超过 16 万次递增数字的帖子),但自身预测难度很低,导致梯度几乎为零,嵌入向量在优化过程中逐渐衰减到零。最终模型在需要“重复”这些 token 时,可能会错误地输出其他 token。
问题2:如何在代码中准确计算 token 数量,避免超出 API 限制?
答案: 必须使用与目标模型一致的 tokenizer。例如,对于 GPT-4 或 gpt-3.5-turbo,应使用 cl100k_base;对于 text-da vinci-003,使用 p50k_base。tiktoken 库提供了 encoding_for_model() 方法自动获取正确的编码器。注意不要依赖简单的字符计数(如OpenAI Tokenizer)或通用规则,因为不同模型中文字符通常需要 2-3 个 token,而英文单词平均约 1.3 个 token。
问题3:ttok 工具中如何设置更持久的缓存?
答案: 设置环境变量 TIKTOKEN_CACHE_DIR 指向一个自定义的持久目录。例如:export TIKTOKEN_CACHE_DIR=/path/to/cache。这样即使机器重启,编码数据也不会被清除,避免重复下载。
