大模型实战指南(1)——Token到底是什么?搞懂大模型的“最小货币单位”

你发给 DeepSeek 的每句话,在它“理解”之前,先被剁成碎片、贴上编号、变成一串整数。碎片叫 Token,切碎片叫分词。Token 不是大模型的附属概念——计费按它算、上下文窗口按它量、推理速度被它拖、输出长度被它卡。搞不懂 Token,你连 API 账单都看不懂。

0. 前言

先说清楚这个系列要干什么。

网上大模型教程分两种:一种太学术,开口就是“自注意力机制的多头缩放点积”,小白直接劝退;另一种太浅,教你怎么注册 ChatGPT 账号,看完还是不懂大模型到底咋运作的。

这个系列走中间路线:用人话讲透每个核心概念,配代码和图,讲完你马上能用。 从 Token 开始,一路讲到 Transformer、微调、RAG、Agent,15 篇读完,你对大模型的认知会从“会打字的魔法盒子”变成“一套可拆解可操控的工程系统”。

为什么第一篇讲 Token?因为 Token 是整个大模型技术栈的地基。你往后学的每一个概念——上下文窗口、Embedding、注意力机制、推理优化——全都以 Token 为基本单位。地基打不牢,后面全是空中楼阁。

不废话,直接开始。

1. 大模型不认字,只认数字

很多人以为大模型像人一样“读”文字——它没有。

你发给 DeepSeek 一句“你好世界”,它接收到的不是四个汉字,而是一串类似这样的整数:

[10236, 3827]

“你好世界”四个字,在 DeepSeek 的分词器里可能只被切成 2 个 Token。换成 GPT-4o,可能变成 4 个甚至 6 个。

同一句话,不同模型切碎的方式完全不同——因为每个模型有自己的“刀法”(分词器)。

如果你觉得抽象,打个比方:把大模型想象成一家餐厅,你的话是客人点的菜。

  • 后厨只认标准化的食材包(Token),不认散装的原材料(原始文字)
  • 切菜师傅(分词器)负责把五花八门的原材料,切成后厨认识的标准食材包
  • 同一棵白菜(同一句话),中餐师傅切成“片”(DeepSeek 切法),西餐师傅切成“丝”(GPT 切法),端出来的菜完全不同

这就是为什么同一句话,不同模型“理解”得不一样——它们在入口处看到的食材形态就不同。

在这里插入图片描述

图1:你输入的文字,先被分词器切成 Token,再查表变成整数 ID,最后变成模型能处理的向量。整条流水线不可逆地决定了模型“看到”什么。

整条流水线分四步:

  1. 原始文本:你输入的自然语言文字
  2. 分词器切分:BPE 算法把文字切成 Token 碎片
  3. 查表映射:每个 Token 查词表,变成一个整数 ID
  4. Embedding:整数 ID 映射为一个高维向量(比如 4096 维),模型开始处理

这四步不可逆地决定了模型“看到”什么。同样的文字,换个分词器,模型接收到的信息就变了。

这把“刀”是怎么工作的?为什么“你好”能合在一起、有的字会被单独切开?背后是一套叫 BPE 的算法,我们第 3 节细讲。

但别急,先搞懂一个更基础的问题:为什么不直接用字符?

1.1 Token 和字符到底是什么关系?

很多人搞混 Token 和字符。区分一下:

概念 定义 例子
字符(Character) 文本的最小显示单位 “你”“好”“a”“B”“!”“。”
字节(Byte) 计算机存储的最小单位 一个英文字符 = 1 字节,一个中文字符 = 3 字节(UTF-8)
词(Word) 语言中有意义的最小单位 “自然语言处理”是一个词
Token 分词器切分后的最小单元 可能是 1 个字、半个词、或一整个短语

Token 介于字符和词之间——有时等于一个字,有时等于一个词,有时是半个词。具体等于什么,取决于分词器怎么切。

一个粗略的经验换算:

  • 英文:1 个 Token ≈ 0.75 个单词(4 个字母)→ 100 个单词 ≈ 130 个 Token
  • 中文(GPT-3.5):1 个汉字 ≈ 1.5-2 个 Token → 100 个汉字 ≈ 150-200 个 Token
  • 中文(DeepSeek/Qwen):1 个汉字 ≈ 0.5-0.7 个 Token → 100 个汉字 ≈ 50-70 个 Token
  • 代码:1 个 Token ≈ 3-4 个字符 → 100 行代码 ≈ 800-1200 个 Token

注意这些只是经验估值,实际 Token 数必须用分词器测。不同文本内容差异很大——常见词多的文本 Token 数少,生僻字多的文本 Token 数多。

2. 为什么不按“字”切?为什么不按“词”切?

直觉上,按字切最简单——“你好世界”切成“你”“好”“世”“界”四个字,每个字一个编号,完事。

问题在于:按字切,模型看不到“词”。

“银行”和“行走”里都有“行”字,但意思完全不同。如果按字切,模型只能看到孤零零的“行”,它得自己猜这个字是什么意思。而你把“银行”当一个整体给模型,模型一上来就知道这是个词,省了大量推理功夫。

更关键的是,按字切会让序列变得很长。一段 1000 字的中文,按字切就是 1000 个 Token。模型处理 1000 个 Token 和处理 500 个 Token,计算量差一倍——注意力机制的计算复杂度是 Token 数的平方,差得更多。

这里有个直觉:如果一个字的序列是 1000,平方就是 100 万次计算;如果压缩到 500,平方就是 25 万次,计算量直接降到四分之一。所以“把文字压缩成更少的 Token”不光是省钱,更是让模型能处理更长文本的关键。

那按词切呢?“自然语言处理”切成一个词,“我喜欢吃苹果”切成五个词——听起来完美。

问题来了:词的组合是无限的。

“自然语言处理工程师”是一个词还是两个词?“反内卷”算不算一个词?“YYDS”呢?按词切,你的词表会膨胀到几百万,模型的嵌入层(每个 Token 对应一个向量)参数直接爆炸,训练根本扛不住。

而且按词切有个致命缺陷:遇到新词就抓瞎。 训练时没见过的词(人名、新术语、拼写错误),分词器不认识,只能返回一个 <UNK>(unknown)标记,模型完全不知道这个词是什么。你发一句“今天 ChatGPT 发布了 GPT-5o”,分词器不认识“GPT-5o”,全变成 <UNK>,模型看到的就是“今天 <UNK> 发布了 <UNK>”——信息全丢了。

你可能会说:那不断往词表里加新词不就行了?问题是语言是活的——每天都有新词诞生(“打卡”“内卷”“多巴胺穿搭”),词表永远追不上。而且每加一个词,模型的嵌入层就要重训,成本极高。

BPE 的思路是在“字”和“词”之间找个平衡点:高频组合合并成短 Token,低频文本拆成碎片拼。 这样词表大小可控(几万到十几万),又能覆盖几乎所有文本。最重要的是——遇到没见过的词,BPE 把它拆成已知的子词碎片,永远不会出现 <UNK>

比如“tokenization”这个词,如果训练语料里没见过,BPE 会把它拆成“token”+“ization”两个子词。模型虽然没见过“tokenization”整体,但见过“token”和“ization”,能推断出大概意思。这就是子词分词的核心优势:开放词汇表——任何文本都能处理,永远不会抓瞎。

反过来,高频词会被合并成一个短 Token。比如“the”在英文语料里出现频率极高,BPE 几轮合并后就会把它变成一个独立 Token。这样一来,模型处理“the”只需要一个步骤,而不是三个(“t”+“h”+“e”),效率大幅提升。OpenAI 官方 README 里的原话:“It attempts to let the model see common subwords. For instance, ‘ing’ is a common subword in English, so BPE encodings will often split ‘encoding’ into tokens like ‘encod’ and ‘ing’.” 常见组成部分被独立成 Token,模型在不同上下文反复见到它,有助于泛化和理解语法。

3. BPE:大模型的“切词刀法”

BPE 全称 Byte Pair Encoding(字节对编码),2015 年由 Sennrich 等人在论文《Neural Machine Translation of Rare Words with Subword Units》中提出,最初用于机器翻译,后来被 GPT-2 采用,如今是主流大模型分词的事实标准。

3.1 一句话讲清 BPE 的核心逻辑

统计训练语料里所有相邻字符对的出现频率,把频率最高的组合合并成新 Token,重复这个过程直到词表达到目标大小。

就像压缩文件——你发现“th”这两个字母老一块出现,就把它们合成一个单元;又发现“the”老一块出现,再合成一个更大的单元。合并到最后,常见的词变成了一个 Token,罕见的词被拆成几个子词碎片。

3.2 用一个例子走一遍

假设训练语料里有这些词(后面的数字是出现次数):

low: 5次,  lower: 2次,  newest: 6次,  widest: 3次

初始状态,每个词被拆成单个字符:

l o w: 5
l o w e r: 2
n e w e s t: 6
w i d e s t: 3

第 1 轮:统计所有相邻字符对,发现 es 一起出现了 9 次(newest 6 次 + widest 3 次),频率最高。合并 e + ses

l o w: 5
l o w e r: 2
n e w es t: 6
w i d es t: 3

第 2 轮:现在 est 一起出现了 9 次,合并 → est

l o w: 5
l o w e r: 2
n e w est: 6
w i d est: 3

第 3 轮lo 一起出现了 7 次(low 5 次 + lower 2 次),合并 → lo

lo w: 5
lo w e r: 2
n e w est: 6
w i d est: 3

第 4 轮low 一起出现了 7 次,合并 → low

low: 5
low e r: 2
n e w est: 6
w i d est: 3

继续合并下去,low + e + r 会变成 lowern + e + w + est 会变成 newest……

在这里插入图片描述

图2:BPE 每一轮找出最高频的相邻组合,合并成新 Token。从单字符开始,一轮一轮“攒”出常见词,就像捏乐高——经常一起出现的积木先拼好,下次直接用。

这个过程的关键特征:

  • 自底向上:从最小单元(字符)开始,逐步合并
  • 频率驱动:只合并出现频率最高的组合,低频组合不动
  • 可逆:任何 Token 序列都能无损还原成原始文本
  • 开放词汇:没见过的词被拆成已知子词,不会出现 <UNK>

你可能注意到了一个细节:BPE 的合并顺序完全由训练语料决定。换一批训练语料,合并顺序就不同,最终词表也不同。这就是为什么不同公司的模型——即使都用 BPE——分词结果完全不一样:它们的分词器是在各自的训练语料上训练出来的。

还有一个容易混淆的点:BPE 的“训练”和模型的“预训练”是两回事。分词器的训练发生在模型预训练之前,是一个独立的、相对简单的过程(统计频率 + 贪心合并)。分词器训练好了,词表固定下来,然后才开始用这个分词器把全部训练语料转成 Token 序列,喂给模型做预训练。分词器一旦确定,在整个模型生命周期里不再改变——这也是为什么换分词器等于换模型。

3.3 中文怎么切?

BPE 原本是处理英文字符的,中文怎么办?两种主流方案:

方案一:字节级 BPE(Byte-level BPE,简称 BBPE)。把所有文本(不管中文英文 emoji)先转成 UTF-8 字节,再在字节层面做 BPE。一个中文字占 3 个字节,所以理论上可能被拆成 3 个碎片。好处是——不管什么语言什么字符,都能处理,永远不会出现“未知字符”。

DeepSeek-V3 用的就是这种方案,词表大小 128,100。GPT-4o 用的 o200k_base 也是字节级 BPE,词表 200,000。

方案二:SentencePiece。Google 开源的工具,直接在 Unicode 字符层面做分词,不依赖空格切分。LLaMA 1/2 用的就是 SentencePiece,词表只有 32,000。而 Qwen 系列虽然也用 BBPE,但词表更大(约 152,000),中文压缩率和 DeepSeek 接近。

你可能会问:为什么不直接在中文“字”的层面做 BPE?因为字节级方案有一个巨大优势——它不关心你的文本是什么语言。英文、中文、日文、韩文、emoji、甚至二进制数据,统统先变成字节,再统一处理。一个分词器通吃所有语言,这对多语言大模型非常重要。

还有一个小知识:BPE 不是唯一的子词分词算法。还有两种常见变体——WordPiece(BERT 用的,合并标准不是频率最高而是似然增益最大)和 Unigram(SentencePiece 的另一种模式,从大量候选词里逆向选出最优子词集)。但主流大语言模型(GPT 系列、DeepSeek、Qwen、LLaMA 3)基本都用 BPE 或 BBPE,所以这个系列只重点讲 BPE,其他两种你了解存在即可。

3.4 词表大小是什么意思?

词表(vocabulary)就是分词器认识的所有 Token 的列表。词表大小就是这张列表有多少行。

  • 词表太:很多常见词被拆成碎片,模型处理同样文本需要更多 Token,推理更慢,也浪费上下文窗口
  • 词表太:模型嵌入层参数暴增(每个 Token 对应一个几千维的向量),训练更贵,而且低频 Token 在训练数据中出现次数太少,学不好

主流大模型的词表大小:

模型 词表大小 分词算法
GPT-3.5 / GPT-4 100,256(cl100k_base) BPE
GPT-4o / o1 / o3 200,000(o200k_base) BPE
DeepSeek-V3 128,100 BBPE(字节级 BPE)
Qwen 系列 ~152,000 BBPE
LLaMA 3 128,256 BBPE

在这里插入图片描述

图3:GPT-4o 把词表从 10 万翻到 20 万,是为了压缩非英语文本的 Token 数。国产模型普遍 12-15 万,兼顾中文压缩率和词表效率。

GPT-4o 为什么要翻倍词表?因为 cl100k_base 对中文压缩率太差——一个汉字经常被切成 2-3 个 Token。o200k_base 扩大词表后,中文压缩率显著提升(OpenAI 官方发布说明的说法是“增加了 tokenizer 的压缩率”,但未公布具体倍数)。

注意一个细节:GPT-3.5 和 GPT-4 用的是同一套分词器(cl100k_base),但 GPT-4o 换了新的(o200k_base)。这意味着同一个 OpenAI API,换模型版本可能导致 Token 数变化,这是很多人没意识到的坑。

3.5 动手实现一个极简 BPE

理论看再多,不如自己跑一遍。下面用 Python 实现一个 20 行的极简 BPE(只保留核心逻辑),你跑完就彻底懂了。

from collections import Counter

def train_bpe(corpus, num_merges=20):
    # 1. 初始化:每个词拆成字符序列(字符间加空格,便于按"对"合并)
    words = {}
    for w, freq in corpus.items():
        words[w] = [' '.join(list(w)), freq]

    vocab = set(''.join(corpus.keys()))  # 初始字符集

    for _ in range(num_merges):
        # 2. 统计所有相邻字符对的频率
        pair_freq = Counter()
        for seq, freq in words.values():
            symbols = seq.split()
            for i in range(len(symbols) - 1):
                pair_freq[(symbols[i], symbols[i+1])] += freq

        if not pair_freq:
            break

        # 3. 找到频率最高的对,合并成新 Token
        best_pair = max(pair_freq, key=pair_freq.get)
        merged = ''.join(best_pair)

        # 4. 把语料里所有出现该对的地方替换成新 Token
        new_words = {}
        for w, (seq, freq) in words.items():
            new_seq = seq.replace(best_pair[0] + ' ' + best_pair[1], merged)
            new_words[w] = [new_seq, freq]
        words = new_words

        vocab.add(merged)

    return vocab

# 训练语料:词 -> 出现次数
corpus = {
    "low": 5,
    "lower": 2,
    "newest": 6,
    "widest": 3,
}

vocab = train_bpe(corpus)
print("训练出的词表:", vocab)

运行这段代码,你会看到“low”、“lower”、“est”、“newest”等词条逐渐被合并出来——和我们前面手推的 4 轮过程完全一致。这就是 BPE 的全部秘密。

真实世界的 BPE 比这复杂得多(还要处理字节级、特殊 Token、正则预分词等),但核心就是这个 20 行的贪心合并逻辑。Karpathy(OpenAI 创始成员)写过 minbpe 教学项目,有兴趣可以去看他的视频讲解。

4. 同一句话,不同模型 Token 数能差多少?

这是最容易踩的坑。来看实测对比——拿“人工智能正在改变世界”这句话测几个模型:

模型 Token 数 你看到的
GPT-3.5(cl100k_base) 约 12-14 几乎每个字被拆成 1-2 个碎片
GPT-4o(o200k_base) 约 8-10 中文压缩率提升,但仍不如国产
DeepSeek-V3 约 5-6 中文优化,常见词整块保留
Qwen 约 5-7 同样针对中文优化

在这里插入图片描述

图4:同一句中文,GPT-3.5 可能切 12+ 个 Token,DeepSeek 只要 5-6 个。这意味着同样 128K 上下文窗口,DeepSeek 实际能塞下的中文是 GPT-3.5 的 2 倍多。

为什么差这么多? 因为分词器是在模型预训练之前单独训练的。分词器的训练语料和模型预训练语料通常是一致的——DeepSeek 和 Qwen 的训练语料里中文比例高,BPE 合并时自然把高频中文组合攒成了 Token。GPT 系列训练语料以英文为主,中文组合频率低,没被合并成短 Token。

这个差异直接影响三个东西:费用、速度、上下文容量。下一节细说。

4.1 先记住一张“日常换算表”

不想每回都跑代码?先记住这张常用表(DeepSeek/Qwen 的估值):

常见场景 大约消耗
一句“你好” 1-2 Token
一条 20 字的微信消息 10-15 Token
一页 A4 中文文档(约 500 字) 300-350 Token
一篇 2000 字公众号文章 1200-1400 Token
一份 10000 字技术文档 6000-7000 Token
一本 20 万字小说 12-14 万 Token

注意这是**国产模型(DeepSeek/Qwen)**的换算。用 GPT-3.5 的话,所有数字直接翻 2-3 倍。

用这张表算一笔账:DeepSeek-V3 的上下文窗口是 128K Token。按一个汉字约 0.6 Token 算,大约能塞进 20 万+汉字——相当于 2000 字文章约 100 篇,或 500 页的厚书。这就是为什么很多人说“DeepSeek 能直接读整本书”。

5. Token 如何影响你的钱包和体验

5.1 计费:API 按 Token 收费

OpenAI、DeepSeek、阿里云——所有大模型 API 的计费单位都是 Token,不是字符,不是请求次数。

价格通常写成“每百万输入 Token 多少钱”和“每百万输出 Token 多少钱”(输出比输入贵,因为生成比理解难)。你写的 Prompt 是输入 Token,模型生成的回答是输出 Token。

这就意味着:同样一段中文,用 GPT-3.5 处理比用 DeepSeek 贵不止一倍——不是因为单价差,而是因为 GPT-3.5 对同样文本消耗的 Token 数是 DeepSeek 的 2 倍多。

举个例子:你有一篇 10000 字的中文文档要喂给模型做摘要。

  • 用 DeepSeek:约 6000 个输入 Token
  • 用 GPT-3.5:约 15000-18000 个输入 Token

如果 DeepSeek 每百万输入 Token 收 1 元,GPT-3.5 收 0.5 元,你会觉得 GPT 更便宜?算一下:DeepSeek 花 0.006 元,GPT-3.5 花 0.008 元。GPT 单价便宜一半,实际花费反而更高——因为 Token 消耗量翻了 2-3 倍。

比价时必须按等量文本换算,不能直接比“每百万 Token 单价”。

5.2 速度:生成 Token 是串行的

大模型生成文本时,是一个 Token 一个 Token 吐出来的(实际上有投机解码等优化,但本质是串行过程)。每秒能生成多少 Token,叫“输出速度”或“生成速度”。

DeepSeek-V3 的生成速度大约 30-60 Token/秒(取决于部署方式)。如果一段回答有 500 个 Token,大约需要 8-17 秒。

这就是为什么你问大模型一个问题,它要“思考”几秒才开始打字,然后一个字一个字蹦出来——它在逐个生成 Token。

注意:输入 Token 和输出 Token 的处理速度完全不同。 输入(Prompt 处理)可以并行计算,速度快得多——10000 个输入 Token 可能 1 秒就处理完了;输出必须逐个生成,500 个输出 Token 要 8-17 秒。所以 API 计费时输入和输出分开算,输出贵得多。

5.3 上下文窗口:Token 是“长度单位”

大模型能“记住”的对话长度有限,上限叫上下文窗口(Context Window),单位当然是 Token。

  • DeepSeek-V3:128K Token
  • GPT-4o:128K Token
  • Claude 3.5:200K Token
  • Gemini 1.5 Pro:1M Token

但 128K Token 能塞多少中文?取决于分词器。DeepSeek 大约能塞 20 万字中文,GPT-3.5 只能塞 7-8 万字。同一个“128K”,实际容量差 2-3 倍。

这就是为什么很多人感觉“模型记不住前面说的话”——不是模型记忆差,是对话历史已经超出了上下文窗口的 Token 上限,旧消息被自动截断了。

这里有个常见的误解需要澄清:上下文窗口不是“对话框能显示多少条消息”,而是“模型一次推理能处理的 Token 总量”。它包括系统提示词、历史对话、当前问题、以及模型要生成的回答。很多新手把对话历史全塞进去,结果留给输出的空间不够,模型回答到一半就断了。

实操建议:在构建大模型应用时,始终用代码计算当前对话的 Token 总量,在接近窗口上限时主动截断或摘要旧消息。主流的大模型 SDK 都提供 Token 计数功能,不要靠肉眼估计。

5.4 最大输出 Token:模型能“说”多少

除了上下文窗口(输入+输出总量),还有一个容易忽略的限制——最大输出 Token。模型一次能生成多少 Token 是有上限的:

  • GPT-4o:最大输出 16,384 Token
  • DeepSeek-V3:最大输出 8,192 Token(默认 4,096)
  • Claude 3.5:最大输出 8,192 Token

这意味着什么?如果你让模型写一篇长文,它写到上限就会自动停止,不管写没写完。很多人遇到“模型回答到一半突然断了”就是踩了这个坑——不是模型不想说了,是它被限制不能再说更多了。

解法:需要长输出时,把任务拆成多轮,让模型分段生成,你在每轮之间传一个“继续”的指令。或者用流式输出(Streaming)配合后端拼接,在模型停止后自动发起续写请求。

实操代码:用 OpenAI SDK 处理长输出的标准姿势:

from openai import OpenAI

client = OpenAI()

full_text = ""
while True:
    # 把已经生成的内容拼回消息里,让模型接着写
    resp = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "写一篇 20000 字的中文技术文章"},
            {"role": "user", "content": "开始写"},
            {"role": "assistant", "content": full_text},  # 已生成的部分
            {"role": "user", "content": "继续"},
        ],
        max_tokens=4000,  # 每次只生成一段
    )
    chunk = resp.choices[0].message.content
    full_text += chunk
    if "完" in chunk or len(full_text) > 20000:
        break

这段代码用“接力写作”的方式突破单次最大输出限制——每次生成 4000 Token,把已生成的内容塞回对话让模型接着写,直到文章写完。这是所有“AI 写长文”应用的底层套路。

6. 动手实验:用 tiktoken 数 Token

理论讲完,动手跑一遍。这里用 OpenAI 开源的 tiktoken 库(GitHub 19.1k stars),它就是 GPT 系列分词器的官方实现。

tiktoken 官方 README 里有一段值得注意的说明:“On average, in practice, each token corresponds to about 4 bytes.”(平均每个 Token 对应约 4 字节)。这是英文场景的经验值——1 个英文单词平均 4-5 个字母,约 4-5 字节,大约 1 个 Token。但中文一个字就 3 字节,按这个算法应该接近 1 个 Token 对应 1 个字,实际却不是——因为分词器的训练语料以英文为主,中文没有被充分合并。

6.1 安装和基本用法

pip install tiktoken
import tiktoken

# 选择编码器
# cl100k_base 对应 GPT-4 / GPT-3.5-turbo
# o200k_base 对应 GPT-4o / o1 / o3 系列
enc = tiktoken.get_encoding("cl100k_base")

# 编码:文本 → Token ID 列表
tokens = enc.encode("你好世界,Hello World!")
print(tokens)
# 输出类似: [57668, 53901, 10236, 3827, ..., 3304, 4422, 0]

# 数 Token 数
print(f"Token 数: {len(tokens)}")

# 解码:Token ID → 文本(无损可逆)
text = enc.decode(tokens)
print(text)  # 你好世界,Hello World!

# 也可以按模型名自动选择编码器
enc_4o = tiktoken.encoding_for_model("gpt-4o")  # 自动用 o200k_base

6.2 对比不同模型的 Token 数

import tiktoken

text = "人工智能正在改变世界,大模型技术日新月异。"

# GPT-4 / GPT-3.5
enc_cl100k = tiktoken.get_encoding("cl100k_base")
print(f"GPT-4 (cl100k_base): {len(enc_cl100k.encode(text))} tokens")

# GPT-4o
enc_o200k = tiktoken.get_encoding("o200k_base")
print(f"GPT-4o (o200k_base): {len(enc_o200k.encode(text))} tokens")

# 对比英文
en_text = "Artificial intelligence is changing the world."
print(f"English (cl100k_base): {len(enc_cl100k.encode(en_text))} tokens")
print(f"English (o200k_base): {len(enc_o200k.encode(en_text))} tokens")

跑完你会发现两个现象:

  1. 同样一句中文,cl100k_base 切出的 Token 数明显多于 o200k_base
  2. 英文的 Token 数差异远小于中文——因为两套词表都是英文优先设计的

这也是 OpenAI 在 GPT-4o 里换新词表的原因:非英语用户的体验太差了,同样一段话比英语用户多花 2-3 倍费用。

6.3 用 HuggingFace 加载国产模型分词器

如果你用 DeepSeek 或 Qwen,可以用 HuggingFace 的 transformers 库加载对应模型的分词器:

from transformers import AutoTokenizer

# DeepSeek-V3 分词器(注意:DeepSeek-V3 需要 trust_remote_code=True,
# 因为它的分词器依赖自定义代码,这是官方示例的标准写法)
tokenizer = AutoTokenizer.from_pretrained(
    "deepseek-ai/DeepSeek-V3", trust_remote_code=True
)
tokens = tokenizer.encode("你好世界")
print(f"DeepSeek-V3: {len(tokens)} tokens")
print(f"Token IDs: {tokens}")

# Qwen 分词器(无需 trust_remote_code)
qwen_tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
qwen_tokens = qwen_tokenizer.encode("你好世界")
print(f"Qwen: {len(qwen_tokens)} tokens")

注意:第一次运行会从 HuggingFace 下载模型分词器文件(几十 MB),需要网络。如果下载慢,可以用 ModelScope(魔搭社区)的镜像源,把 from_pretrained 的路径换成魔搭对应的仓库名即可。

6.4 别忘了特殊 Token

以上代码数的是“文本内容”的 Token 数,但实际 API 调用时,还有一类隐藏的 Token 消耗——特殊 Token(Special Tokens)

每个模型在处理你的请求时,会自动在文本里插入一些控制标记:

  • <|im_start|> / <|im_end|>:标记一轮对话的开始和结束(ChatML 格式)
  • <|system|> / <|user|> / <|assistant|>:标记角色
  • BOS(Beginning of Sequence)/ EOS(End of Sequence):序列首尾标记

这些特殊 Token 也算进 Token 总数,但你在界面上看不到它们。一轮对话可能多出 5-10 个 Token 的隐藏消耗。对话轮次越多,这部分开销越大。

# 查看 tiktoken 的特殊 Token
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
# cl100k_base 的特殊 Token
print(enc.special_tokens_set)
# 输出类似: {'<|endoftext|>', '<|fim_prefix|>', '<|fim_middle|>', '<|fim_suffix|>'}

所以在做成本预估时,给每轮对话额外预留 10-20 个 Token 的余量,别算得太紧。

7. 三个真坑,每个都付过费

坑 1:你以为的“免费额度”根本不够用

你看到某 API 送“100 万 Token 免费额度”,心想够用一个月。结果一跑——你用 GPT-3.5 的 cl100k_base 编码器,一篇 5000 字的中文文档大约消耗 8000-10000 Token。一篇就吃掉 1%。如果每篇还要让模型输出 2000 字,又多 3000-4000 Token。

算一下:每天处理 10 篇文档,每篇输入 8000 + 输出 3500 = 11500 Token,一天 115000 Token,9 天就花完 100 万免费额度。

解法:换模型前先算实际 Token 消耗。中文场景优先用 DeepSeek/Qwen,Token 压缩率高,同样额度能用更久。按上面的例子换 DeepSeek:同样 5000 字中文只消耗约 3000 Token,100 万额度能用 30 天以上。

坑 2:上下文窗口“虚标”

模型宣称 128K 上下文,你以为能塞 12.8 万字中文。实际呢?用 GPT-3.5 的 cl100k_base,一个汉字约 1.5-2 个 Token,128K 大约只能塞 6-8 万字。用 DeepSeek,一个汉字约 0.6 个 Token,能塞 20 万字+。

更坑的是,有些模型的“128K 上下文”是指输入+输出合计。你以为还能再让它输出 2 万字,实际上输入已经把窗口挤满了,输出吐了几个 Token 就被截断。

解法:用目标模型的分词器实际计算你的文本 Token 数,别信“1 Token ≈ 1 个字”这种粗略估算。给输出预留足够的 Token 空间,别把上下文窗口塞满。

坑 3:换模型 = 换刀法,Token 数会变

你用 DeepSeek 跑通了一个流程,Prompt 精心设计成 4000 Token 以内。换 GPT-4o 后一测——同样 Prompt 变成 6000+ Token,直接超出某些限制。

更隐蔽的情况:你从 GPT-4 换到 GPT-4o,以为同是 OpenAI 系列没事。结果 GPT-4 用 cl100k_base,GPT-4o 用 o200k_base,Token 数不一样。你的 Prompt 里如果有很多中文,GPT-4o 的 Token 数会减少(词表大了,压缩率高了);如果有很多英文,变化不大。

解法:切换模型前,用目标模型的分词器重新数一遍 Token。OpenAI 系用 tiktoken,HuggingFace 系用 transformers.AutoTokenizer,国产模型用官方 SDK。把它做成一个自动化检查脚本,切模型之前自动跑一遍。

8. 经验清单:带走这 5 条就够了

  1. Token 是大模型的最小单位:计费、上下文窗口、输出长度、推理速度,全以 Token 计。搞不懂 Token,你连账单都看不懂。

  2. 不同模型对同一句话切出的 Token 数可能差 2-3 倍:国产模型中文压缩率比 GPT-3.5 高 2-3 倍。比价时必须按等量文本换算,不能直接比每百万 Token 单价。

  3. BPE 的核心逻辑就一句话:高频组合合并成短 Token,低频文本拆成碎片拼——和 ZIP 压缩是同一个道理。词表大小是效率和覆盖率的折中。遇到没见过的词,BPE 拆成已知子词,永远不会抓瞎。

  4. 永远用分词器实际数 Token,不要用字符数估算:1 个中文字在 GPT-3.5 里是 1-2 个 Token,在 DeepSeek 里可能只有 0.6 个。估算误差能让你的成本预算翻倍。

  5. 切模型 = 换刀法:同一个 Prompt 在不同模型里 Token 数不同,即使同是 OpenAI 系列,GPT-4 和 GPT-4o 的分词器也不一样。迁移模型前,用目标模型的分词器重新数一遍 Token,防止上下文超限。

下篇预告

下一篇:《大模型实战指南(2)——上下文窗口:为什么聊着聊着模型就“失忆”了?》

1M 上下文到底能不能塞下一整本书?为什么模型说“忘了”前面的对话?KV Cache 怎么让长文本推理不死等?注意力机制的计算量为什么随 Token 数平方增长?

系列:大模型实战指南,篇篇连载,从 Token 到 Agent,带你从零搞懂大模型。关注不迷路。


本文所有数据基于 2026 年 8 月公开信息整理。词表大小和分词算法来自官方文档/技术报告:OpenAI tiktoken GitHub 仓库(github.com/openai/tiktoken,19.1k stars)、DeepSeek-V3 技术报告(arxiv.org/abs/2412.19437)、Qwen 官方文档。Token 换算比例为实测估值,具体数值取决于模型版本和分词器配置。

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐