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