一、问题的起点
理解大语言模型(LLM)的处理机制,最常见的误区是将其类比为”会读字、会写字”的程序。事实并非如此——LLM 内部不存在”字符”或”文字”的概念,全部输入与输出在工程上均表现为数字,更准确地说,是高维空间中的向量。文字仅是人类世界与模型世界之间的一层转换接口,完成这层转换的组件称为 Tokenizer(分词器)。
要理解一段自然语言如何被 LLM 处理、为何不同输入的成本差异巨大、为何模型在某些字符级任务上(如统计 “strawberry” 中字母 r 的数量)表现异常,前提是搞清楚:一段自然语言如何被转换为模型实际处理的数字。
这一过程在工程上分为两个独立阶段。第一阶段是 分词器的训练,由模型厂商在模型发布前一次性完成,产物是一张固定的”切词规则表”。第二阶段是 编码,在每次 API 调用时执行,按照规则表将任意输入文本切成 token、再查表转换为整数 ID 交给模型。需要注意的区分:调用模型时分词器已经完成训练,规则表固定不变,编码阶段执行的仅是机械的”查表 + 合并”,不涉及任何学习行为。
二、Token 的本质
Token 是一种介于字母和单词之间的子词(subword)单位。一个具体 token 的形态,完全取决于该字符序列在训练语料中出现的频率。OpenAI 给出的经验值是英文场景下 1 token ≈ 4 个字符 ≈ ¾ 个英文单词,但该数值会因内容性质显著波动:纯英文常用词每个仅占 1 个 token,中文一个字往往占 1–2 个 token,乱码或罕见专有名词可能每个字符都要单独占用一个 token。
下表列出若干真实示例(可在 platform.openai.com/tokenizer 上验证):
| 输入文本 | 切分结果 | Token 数 | 说明 |
|---|---|---|---|
" the" | [" the"] | 1 | 英语最高频词之一,ID 极小(约 290) |
"hello" | ["hello"] | 1 | 常见词,整体保留 |
"lowest" | ["low", "est"] | 2 | 词根 + 后缀,BPE 的典型行为 |
"tokenization" | ["token", "ization"] | 2 | 同上 |
"strawberry" | ["str", "aw", "berry"] | 3 | 拆分比预期更碎 |
"你好" | ["你", "好"] | 2–3 | 中文每字常占 1–2 token |
"xq7zk!" | ["x", "q", "7", "z", "k", "!"] | 6 | 罕见组合退化到单字节 |
高频整词被合并成 1 个 token,中频词被拆为多个高频子词,罕见词或乱码退化为按单字节切分。这是 BPE 算法的核心特征。
三、BPE 训练过程
BPE 全称为 Byte-Pair Encoding(字节对编码),OpenAI、Anthropic、Meta Llama、Google Gemini 系列底层均使用该算法或其变体。训练过程可通过一段迷你语料逐步演示。
设训练语料仅包含四个词:
low low low low low (5 次)
lower lower (2 次)
newest newest newest (3 次)
widest widest widest (3 次)第一步,所有词拆为单字符,词尾添加特殊符号 </w> 标记词边界:
"l o w </w>" × 5
"l o w e r </w>" × 2
"n e w e s t </w>" × 3
"w i d e s t </w>" × 3此时词表包含 11 个 token:{l, o, w, e, r, n, s, t, i, d, </w>}。
第二步,扫描整个语料,统计”相邻字符对”的出现次数。例如 l + o 在 “low” 与 “lower” 中累计 5 + 2 = 7 次;o + w 同样为 7 次;e + s 在 “newest” 与 “widest” 中累计 6 次。l + o 与 o + w 并列最高频,任选其一(如 l + o)合并为新 token lo 加入词表,并记录合并规则:l + o → lo。
第三步,重复扫描与合并。下一轮 lo + w 成为最高频对,合并为 low;再下一轮 e + s 最高频,合并为 es;再下一轮 es + t 合并为 est;继而 est + </w> 合并为 est</w>。整个过程持续进行,每轮合并当前最高频的字符对,直至词表达到预设大小。
OpenAI 训练 GPT-4o 时,合并至词表约 20 万 token 停止(对应的编码方案命名为 o200k_base)。训练完成后,产物仅有两个文件:
-
vocab.json:
{token 文本 → token ID}的映射表。 -
merges.txt:数万至数十万条”x + y → xy”形式的合并规则,严格按照训练时的合并顺序排列。
这两个文件合计通常仅几 MB,构成了 tiktoken 库的全部核心数据。训练完成后,两个文件永久固定,不再变更。
四、BPE 编码过程
编码阶段以一个训练语料中未出现过的新词 "lowest" 为例:
Step 1: 拆为单字符 "l o w e s t </w>"
Step 2: 按 merges.txt 顺序遍历规则,能合并则合并
规则 #1 l + o → lo → "lo w e s t </w>"
规则 #2 lo + w → low → "low e s t </w>"
规则 #3 e + s → es → "low es t </w>"
规则 #4 es + t → est → "low est </w>"
规则 #5 est + </w> → est</w> → "low est</w>"
Step 3: 无更多可用规则,终止
最终结果: ["low", "est</w>"] → token ID 例如 [10516, 478]关键事实:训练语料中并未包含 “lowest” 一词,但分词器依靠已学到的 “low” 与 “est” 两个高频子词,仍能合理切分。这是 BPE 的核心价值——以有限的子词表覆盖近乎无限的词汇组合。
由此也可解释 Token ID 与频率的对应关系:BPE 训练按频率从高到低依次分配 ID,因此 ID 本身即反映频率高低:
Token ID 本质上是一张按频率排序的索引表:ID 越小代表 token 越高频、越常见;ID 越大代表越罕见、越”碎”。这也解释了为何 "the the the" 切出的 ID 都很小,而 "xqzpfk" 这类乱码切出的 ID 都在数万以上。
五、不同厂商的分词器差异
虽然算法本质均为 BPE,但各厂商在实现细节、词表大小、预处理方式上存在差异。这直接决定了能否在本地精确计算 token 数,以及相同文本在不同模型间的成本差异。
| 厂商 | 工具/方案 | 开源情况 | 词表大小 | 关键特征 |
|---|---|---|---|---|
| OpenAI | tiktoken | 完全开源 | 50K → 100K → 200K(GPT-4o) | 纯 byte-level BPE,本地可精确计算 |
| Anthropic | 自研 BPE 变体 | 完全闭源 | 约 100K(推测) | 仅能通过 count_tokens API 查询,无法离线计算 |
| SentencePiece + BPE | 开源 | 约 256K(Gemini) | 空格替换为 ▁,原生支持多语言 | |
| Meta | SentencePiece + BPE | 开源 | 32K(Llama 2)→ 128K(Llama 3) | 基于 SentencePiece 框架的 BPE 实现 |
OpenAI 的透明度最高,通过 pip install tiktoken 即可在本地精确计算任意字符串的 token 数与 token ID。Anthropic 透明度最低,分词器从未公开,社区曾通过监听 streaming 流量逆向 token 边界。Google 的 SentencePiece 存在一个独特设计:将空格视为普通字符,以 ▁(U+2581)替代,因此 "This is a test" 在 SentencePiece 中先转为 "▁This▁is▁a▁test" 再行切分。该设计的工程意义在于完全语言无关——中文、日文、泰文等无空格语言不再需要特殊处理,与英文采用统一流程。这也是 Gemini 在多语言场景表现优于早期 GPT 的原因之一。
六、LLM 实际处理的对象
回到核心问题:LLM 处理的是文字还是数字?
全流程均为数字,更准确地说是高维空间中的向量。 LLM 本质是一个超大规模的数学函数,输入与输出均为数字。模型不识别 “hello”、“你好” 等字符串——这些字符序列对模型内部计算而言不具备任何含义。
完整的”文字 → 数字 → 文字”流程如下:
Part 1:输入 → 模型内部
Part 2:模型推理 → 输出
其中一个关键中间步骤是 Embedding(嵌入层)。仅有 token ID 不足以支撑模型计算——整数 13225 与 13224 之间不存在任何语义关系,无法直接参与运算。因此模型内部维护一张大规模的 Embedding 矩阵,将每个 token ID 映射为一个高维向量(GPT-3 为 12288 维,Llama 3 为 4096 维)。该矩阵通过训练得到——训练过程中模型自主学习”语义接近的 token,向量也应当接近”的特性。训练完成后,"king" 与 "queen" 的向量距离较小,"happy" 与 "joyful" 的向量距离较小,著名的等式 king − man + woman ≈ queen 在该向量空间中近似成立。
模型的全部”推理”过程发生在向量空间内。模型接收一串向量,经过多层矩阵乘法与非线性变换,最终输出一个长度等于词表大小的概率分布,表示”下一个 token 在词表中每一个候选位置的概率”。采样器从该分布中选出一个 token,追加至输入末尾,再次输入模型预测下一个 token——如此循环,直至模型输出表示终止的特殊 token <end_of_text>。该机制称为 autoregressive generation(自回归生成),是所有 GPT 类模型的统一生成原理。
七、由该机制衍生的常见现象
理解”LLM 仅处理数字、不处理字符”这一前提后,许多常见现象可获得统一解释。
| 现象 | 原因 |
|---|---|
| 早期GPT 无法准确统计 “strawberry” 中字母 r 的数量 | 模型看到的是 ["str", "aw", "berry"] 三个 token ID,字母层级的信息已丢失 |
| 模型偶尔拼写错误 | 模型并非在”拼字母”,而是在”接龙 token”,罕见词易接错 |
"hello" 与 " hello" 输出不同 | 二者是两个完全不同的 token,对应不同向量 |
| 中文体验弱于英文 | 中文 token 在训练语料中占比低,对应的 embedding 训练不充分 |
| 微调(Fine-tuning)能改变模型行为 | 微调即调整 Embedding 矩阵与 Transformer 内部参数 |
| RAG 成本低于微调 | RAG 仅在输入端追加 token,不修改模型参数;微调实际修改模型内部权重 |
| context window 溢出导致”遗忘” | 模型仅能感知 context 内的 token,溢出部分不可见 |
| 同等内容中文调用 API 成本更高 | 中文每字 1–2 token,英文常用词单个仅 1 token,token 数相差 2–3 倍 |
八、核心结论
-
自然语言在 LLM 内部不存在。 模型内部仅有 token ID,且 token ID 会被立即转换为向量。
-
Tokenizer 是人类世界与模型世界之间唯一的接口。 理解 Tokenizer 即理解 LLM 工程问题的一半。
-
token ID 本质上是按频率排序的索引表。 ID 越小越高频,ID 越大越罕见。
-
BPE 的核心思想:以有限子词表覆盖无限词汇组合。
-
LLM 不读字符,它执行数学运算。