Token 与分词器工作原理
AI 导读
Token 与分词器工作原理 理解 AI 的"视觉":大模型如何看待你输入的每一个字 Maurice | 灵阙学院 前置准备 Python 3.10+ pip install tiktoken transformers openai 一、什么是 Token Token 是大模型处理文本的最小单位。模型不直接理解文字,而是把文字切分成 Token 后,转换成数字再处理。 1.1 直观示例...
Token 与分词器工作原理
理解 AI 的"视觉":大模型如何看待你输入的每一个字 Maurice | 灵阙学院
前置准备
- Python 3.10+
- pip install tiktoken transformers openai
一、什么是 Token
Token 是大模型处理文本的最小单位。模型不直接理解文字,而是把文字切分成 Token 后,转换成数字再处理。
1.1 直观示例
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
# 英文示例
text_en = "Hello, world!"
tokens_en = enc.encode(text_en)
print(f"英文: '{text_en}'")
print(f"Token IDs: {tokens_en}")
print(f"Token 数量: {len(tokens_en)}")
print(f"Token 还原: {[enc.decode([t]) for t in tokens_en]}")
预期输出:
英文: 'Hello, world!'
Token IDs: [13225, 11, 2375, 0]
Token 数量: 4
Token 还原: ['Hello', ',', ' world', '!']
# 中文示例
text_zh = "你好,世界!"
tokens_zh = enc.encode(text_zh)
print(f"\n中文: '{text_zh}'")
print(f"Token IDs: {tokens_zh}")
print(f"Token 数量: {len(tokens_zh)}")
print(f"Token 还原: {[enc.decode([t]) for t in tokens_zh]}")
预期输出:
中文: '你好,世界!'
Token IDs: [57668, 53901, 3922, 244, 41361, 6313, 231]
Token 数量: 7
Token 还原: ['你', '好', ',', '世', '界', '!']
关键发现:同样含义的文本,中文消耗的 Token 比英文多将近一倍。
二、分词算法原理
2.1 BPE(Byte Pair Encoding)
GPT 系列使用的核心算法。原理是从单个字节开始,不断合并最频繁出现的相邻对。
BPE 训练过程(简化示例):
原始词汇: ["l o w", "l o w e r", "n e w", "n e w e r"]
第 1 轮: 最频繁的相邻对是 (e, r),合并为 er
["l o w", "l o w er", "n e w", "n e w er"]
第 2 轮: 最频繁的相邻对是 (n, e),合并为 ne
["l o w", "l o w er", "ne w", "ne w er"]
第 3 轮: 最频繁的相邻对是 (ne, w),合并为 new
["l o w", "l o w er", "new", "new er"]
第 4 轮: 最频繁的相邻对是 (l, o),合并为 lo
["lo w", "lo w er", "new", "new er"]
...直到达到目标词汇量
2.2 WordPiece
BERT 使用的算法。和 BPE 类似,但选择合并对时使用似然增益而非纯频率。
WordPiece 特征:
- 子词前缀用 ## 标记
- "unbelievable" --> ["un", "##believ", "##able"]
- 中文每个字通常独立成 Token
2.3 SentencePiece
Google 开发,不依赖预分词(空格分隔),直接在原始字节上训练。适合中日韩等无空格语言。
SentencePiece 特征:
- 空格被编码为特殊字符 ▁
- "I love AI" --> ["▁I", "▁love", "▁AI"]
- 真正的语言无关
2.4 算法对比
| 算法 | 使用模型 | 子词标记 | 中文处理 |
|---|---|---|---|
| BPE | GPT-3/4, Llama | 无特殊标记 | 按 UTF-8 字节 |
| WordPiece | BERT, DistilBERT | ## 前缀 | 按字切分 |
| SentencePiece | T5, Gemini, Qwen | ▁ 前缀 | 原始字节训练 |
三、为什么中文消耗更多 Token
3.1 根本原因
BPE 的训练数据以英文为主(约占 60-80%)。英文常见词被合并为高频 Token,中文字符在训练中出现频率低,合并机会少。
3.2 对比实验
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
test_cases = [
("Hello, how are you today?", "英文问候"),
("你好,你今天好吗?", "中文问候(等价含义)"),
("The quick brown fox jumps over the lazy dog", "英文经典句"),
("敏捷的棕色狐狸跳过了懒惰的狗", "中文翻译(等价含义)"),
("def hello(): return 'world'", "Python 代码"),
("SELECT * FROM users WHERE id = 1", "SQL 代码"),
]
print(f"{'文本':<45} {'类型':<12} {'Token数':>6}")
print("-" * 70)
for text, desc in test_cases:
tokens = enc.encode(text)
print(f"{text:<45} {desc:<12} {len(tokens):>6}")
预期输出:
文本 类型 Token数
----------------------------------------------------------------------
Hello, how are you today? 英文问候 7
你好,你今天好吗? 中文问候(等价含义) 10
The quick brown fox jumps over the lazy dog 英文经典句 9
敏捷的棕色狐狸跳过了懒惰的狗 中文翻译(等价含义) 16
def hello(): return 'world' Python 代码 9
SELECT * FROM users WHERE id = 1 SQL 代码 9
中文约为英文的 1.5-2 倍 Token 消耗。
3.3 不同模型对中文的友好度
import tiktoken
models = {
"gpt-4o": "o200k_base",
"gpt-4": "cl100k_base",
"gpt-3.5": "cl100k_base",
}
text = "大语言模型正在改变人工智能的应用方式"
print(f"测试文本: {text}\n")
for model, enc_name in models.items():
enc = tiktoken.get_encoding(enc_name)
tokens = enc.encode(text)
print(f"{model} ({enc_name}): {len(tokens)} tokens")
预期输出:
测试文本: 大语言模型正在改变人工智能的应用方式
gpt-4o (o200k_base): 11 tokens
gpt-4 (cl100k_base): 14 tokens
gpt-3.5 (cl100k_base): 14 tokens
GPT-4o 使用了更大的词汇表(o200k),对中文更友好。
四、tiktoken 实战用法
4.1 基础用法
import tiktoken
# 方式一:按模型名获取编码器
enc = tiktoken.encoding_for_model("gpt-4o")
# 方式二:按编码名获取
enc = tiktoken.get_encoding("o200k_base")
# 编码(文本 --> Token IDs)
tokens = enc.encode("Hello 你好")
print("Token IDs:", tokens)
# 解码(Token IDs --> 文本)
text = enc.decode(tokens)
print("Decoded:", text)
# 逐 Token 解码
for token_id in tokens:
print(f" {token_id} --> '{enc.decode([token_id])}'")
预期输出:
Token IDs: [13225, 220, 57668, 53901]
Decoded: Hello 你好
13225 --> 'Hello'
220 --> ' '
57668 --> '你'
53901 --> '好'
4.2 Token 计数工具函数
import tiktoken
def count_tokens(text: str, model: str = "gpt-4o") -> int:
"""计算文本的 Token 数量"""
enc = tiktoken.encoding_for_model(model)
return len(enc.encode(text))
def count_messages_tokens(messages: list[dict],
model: str = "gpt-4o") -> int:
"""计算对话消息列表的总 Token 数(含格式开销)"""
enc = tiktoken.encoding_for_model(model)
tokens_per_message = 3 # role + content + separator
num_tokens = 0
for msg in messages:
num_tokens += tokens_per_message
for key, value in msg.items():
num_tokens += len(enc.encode(value))
num_tokens += 3 # assistant reply priming
return num_tokens
# 测试
messages = [
{"role": "system", "content": "你是一个有帮助的助手。"},
{"role": "user", "content": "解释一下什么是向量数据库,以及它的主要用途。"},
]
for msg in messages:
tokens = count_tokens(msg["content"])
print(f"[{msg['role']}] {tokens} tokens: {msg['content'][:30]}...")
total = count_messages_tokens(messages)
print(f"\n总计(含格式开销): {total} tokens")
预期输出:
[system] 8 tokens: 你是一个有帮助的助手。...
[user] 15 tokens: 解释一下什么是向量数据库,以及它的主要用途。...
总计(含格式开销): 32 tokens
五、成本估算实战
5.1 价格速查表(2026 年 2 月)
| 模型 | 输入价格 ($/1M tokens) | 输出价格 ($/1M tokens) |
|---|---|---|
| GPT-4o | $2.50 | $10.00 |
| GPT-4o-mini | $0.15 | $0.60 |
| Claude Sonnet 4 | $3.00 | $15.00 |
| Claude Haiku 3.5 | $0.80 | $4.00 |
| Gemini 2.5 Flash | $0.15 | $0.60 |
| DeepSeek-V3 | $0.27 | $1.10 |
5.2 成本计算器
import tiktoken
PRICING = {
"gpt-4o": {"input": 2.50, "output": 10.00},
"gpt-4o-mini": {"input": 0.15, "output": 0.60},
"claude-sonnet": {"input": 3.00, "output": 15.00},
"deepseek-v3": {"input": 0.27, "output": 1.10},
}
def estimate_cost(input_text: str, output_tokens: int = 500,
model: str = "gpt-4o-mini") -> dict:
"""估算单次调用成本"""
enc = tiktoken.encoding_for_model("gpt-4o") # 近似估算
input_tokens = len(enc.encode(input_text))
pricing = PRICING[model]
input_cost = input_tokens / 1_000_000 * pricing["input"]
output_cost = output_tokens / 1_000_000 * pricing["output"]
total_cost = input_cost + output_cost
return {
"model": model,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"input_cost": f"${input_cost:.6f}",
"output_cost": f"${output_cost:.6f}",
"total_cost": f"${total_cost:.6f}",
}
# 单次调用估算
result = estimate_cost(
"请帮我写一份关于人工智能在财务审计中应用的分析报告,要求包含现状、挑战和建议。",
output_tokens=2000,
model="gpt-4o-mini"
)
for k, v in result.items():
print(f" {k}: {v}")
预期输出:
model: gpt-4o-mini
input_tokens: 28
output_tokens: 2000
input_cost: $0.000004
output_cost: $0.001200
total_cost: $0.001204
5.3 月度成本预测
def monthly_cost_estimate(
daily_requests: int,
avg_input_tokens: int,
avg_output_tokens: int,
model: str = "gpt-4o-mini"
) -> str:
pricing = PRICING[model]
daily_input_cost = (
daily_requests * avg_input_tokens / 1_000_000 * pricing["input"]
)
daily_output_cost = (
daily_requests * avg_output_tokens / 1_000_000 * pricing["output"]
)
daily_total = daily_input_cost + daily_output_cost
monthly_total = daily_total * 30
return (
f"模型: {model}\n"
f"日请求量: {daily_requests}\n"
f"日成本: ${daily_total:.2f}\n"
f"月成本: ${monthly_total:.2f}"
)
# 示例:客服场景
print(monthly_cost_estimate(
daily_requests=1000,
avg_input_tokens=500,
avg_output_tokens=800,
model="gpt-4o-mini"
))
预期输出:
模型: gpt-4o-mini
日请求量: 1000
日成本: $0.56
月成本: $16.80
六、跨模型 Tokenizer 对比
import tiktoken
# GPT-4o 的 tokenizer
gpt4o_enc = tiktoken.get_encoding("o200k_base")
# GPT-4 / GPT-3.5 的 tokenizer
cl100k_enc = tiktoken.get_encoding("cl100k_base")
test_texts = [
"人工智能",
"机器学习与深度学习的区别",
"RAG(检索增强生成)是一种结合检索和生成的技术",
"def calculate_cost(tokens, price_per_million):",
"SELECT COUNT(*) FROM orders WHERE status = 'completed'",
]
print(f"{'文本':<50} {'o200k':>6} {'cl100k':>6} {'差异':>6}")
print("-" * 75)
for text in test_texts:
t1 = len(gpt4o_enc.encode(text))
t2 = len(cl100k_enc.encode(text))
diff = t2 - t1
print(f"{text:<50} {t1:>6} {t2:>6} {diff:>+6}")
预期输出:
文本 o200k cl100k 差异
---------------------------------------------------------------------------
人工智能 4 6 +2
机器学习与深度学习的区别 9 13 +4
RAG(检索增强生成)是一种结合检索和生成的技术 19 24 +5
def calculate_cost(tokens, price_per_million): 10 10 +0
SELECT COUNT(*) FROM orders WHERE status = 'completed' 12 12 +0
结论:o200k(GPT-4o)对中文明显更节省 Token;对英文和代码差异不大。
七、Prompt 工程中的 Token 实践
7.1 控制输出长度
# max_tokens 直接限制输出 Token 数
# 中文:1 Token 约等于 1 个字
# 英文:1 Token 约等于 0.75 个单词
# 想要 200 字的中文回答
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "介绍量子计算"}],
max_tokens=250 # 预留一些余量
)
7.2 Prompt 压缩技巧
# 冗长 Prompt(约 60 tokens)
verbose = """
请你作为一个专业的技术文档撰写者,帮助我把下面的技术文章进行总结。
总结的要求如下:请用中文进行总结,总结内容要简洁明了,突出重点。
请确保总结不超过 200 字。
"""
# 压缩后 Prompt(约 25 tokens)
concise = """
用中文总结以下技术文章,200字以内,突出重点。
"""
# 效果相同,Token 节省约 60%
enc = tiktoken.encoding_for_model("gpt-4o")
print(f"冗长版: {len(enc.encode(verbose))} tokens")
print(f"压缩版: {len(enc.encode(concise))} tokens")
预期输出:
冗长版: 58 tokens
压缩版: 22 tokens
7.3 上下文窗口管理
MODEL_CONTEXT_LIMITS = {
"gpt-4o": 128_000,
"gpt-4o-mini": 128_000,
"claude-sonnet": 200_000,
"gemini-2.5": 1_000_000,
"deepseek-v3": 64_000,
}
def check_context_fit(messages: list[dict], model: str = "gpt-4o",
reserve_output: int = 4096) -> dict:
"""检查消息是否超出上下文窗口"""
enc = tiktoken.encoding_for_model("gpt-4o")
total_tokens = sum(len(enc.encode(m["content"])) for m in messages)
limit = MODEL_CONTEXT_LIMITS.get(model, 128_000)
available = limit - reserve_output
fits = total_tokens <= available
return {
"total_tokens": total_tokens,
"limit": limit,
"available": available,
"fits": fits,
"utilization": f"{total_tokens / available * 100:.1f}%"
}
常见问题
Q1: 1 个 Token 大概等于多少个字? 英文约 0.75 个单词 = 4 个字符。中文约 1-1.5 个字(因为一个汉字可能被拆成多个 Token)。代码和数字通常 Token 效率较高。
Q2: 为什么我的 Token 计数和 OpenAI 账单不一致? OpenAI 的计费包含格式开销(每条消息 3-4 个额外 Token)、工具定义、系统提示等隐性消耗。实际消耗比纯文本计数高 5-15%。
Q3: 如何减少中文的 Token 消耗? 使用 GPT-4o 系列(o200k 编码器对中文更友好);避免重复冗余的表述;用结构化格式(JSON/列表)替代口语化描述。
Q4: 不同模型的 Tokenizer 可以互换吗? 不可以。每个模型系列有自己的 Tokenizer。GPT-4o 用 o200k_base,Claude 用自己的 BPE 变体,Gemini 用 SentencePiece。用 tiktoken 估算其他模型的 Token 数只是近似值。
Q5: Token 上限和输出上限有什么区别? Token 上限(context window)= 输入 + 输出的总上限。max_tokens 只限制输出部分。例如 GPT-4o 的 128K 上下文中,如果输入占了 120K,输出最多只剩 8K。
Maurice | maurice_wen@proton.me