芥末
发布于 2025-11-28 / 0 阅读
0
0

大模型 Token 与分词器:从切分规则到计费成本

在大语言模型(Large Language Model,LLM)的 API(应用程序编程接口)文档里,经常会看到几个说法:

  • 这个模型支持 128K Token 上下文;
  • 输入价格是每百万 Token 多少元;
  • 输出价格比输入价格更贵;
  • 提示词(Prompt)太长会导致请求失败。

这里的 Token 不是中文里的“字”,也不是英文里的“单词”。它更像大模型内部处理文本时使用的“信息块”:一段文本进入模型之前,会先被分词器(Tokenizer)切成若干 Token,再转换成数字编号,模型真正处理的是这些编号以及由编号映射出来的向量。

理解 Token,能解决三个很实际的问题:

  1. 为什么同样一句话,不同模型统计出来的 Token 数不一样;
  2. 为什么上下文窗口不是按字数计算,而是按 Token 计算;
  3. 为什么大模型 API 通常按照输入 Token 和输出 Token 计费。

Token 是大模型看到的文本单位

人看到的是文字,模型看到的是 Token。

例如一句话:

今天天气不错。

对人来说,它可以按字理解:

今 / 天 / 天 / 气 / 不 / 错 / 。

也可以按词理解:

今天 / 天气 / 不错 / 。

大模型不会直接用人类语言里的“词”作为唯一标准,而是使用分词器决定切分方式。切出来的 Token 可能是一个汉字、一个词、一个标点、一个英文单词,也可能只是英文单词的一部分。

文本片段可能成为 Token 的形式说明
一个汉字单字很常见,可能单独成 Token
苹果一个词高频组合可能被打包
孙悟空一个名字高频专有名词可能被打包
一个标点标点通常也会参与编码
apple一个英文单词常见英文词可能整体保留
ing单词后缀高频子词可能单独成 Token

“可能”这个词很重要。Token 的切分结果由具体模型使用的分词器决定,同一段文本换一个模型,Token 数量和切法都可能变化。

整个流程可以画成这样:

flowchart LR
    A[原始文本] --> B[分词器 Tokenizer]
    B --> C[Token 序列]
    C --> D[Token ID 数字编号]
    D --> E[向量表示 Embedding]
    E --> F[大模型计算]
    F --> G[预测下一个 Token]
    G --> H[分词器解码为文字]

模型不是直接“读”汉字或英文字符,而是先把文本变成 Token ID。Token ID 再映射成向量,进入 Transformer 等模型结构里参与计算。

为什么不直接按字或按词处理

最直观的方案是按字处理中文、按单词处理英文,但实际会遇到很多麻烦。

切分方式优点问题
按字符切分规则简单,任何文本都能处理序列会变长,模型需要处理更多位置
按单词切分符合部分语言习惯遇到生僻词、新词、拼写变化会很麻烦
按子词切分在长度和泛化之间折中切法不一定符合人类直觉

现代大模型常用的是“子词”思路。它不会强行把所有内容都切成单字,也不会要求词表覆盖世界上所有单词,而是把高频片段打包,把低频片段拆开。

英文里常见的 ingtionpre 这类片段,可能成为 Token;中文里常见的词语、名字、短语,也可能成为 Token。这样做的好处是:常见表达可以用更少 Token 表示,不常见表达也能被拆成更小单元处理,不至于完全无法编码。

分词器怎样决定切哪里

分词器的核心工作是两件事:

  1. 建一个 Token 词表;
  2. 把输入文本映射成词表里的 Token ID。

很多分词器会使用 BPE(Byte Pair Encoding,字节对编码)、Unigram、WordPiece 或 SentencePiece 一类算法。不同算法细节不一样,但思想很接近:从大量语料中统计高频片段,把经常一起出现的字符或字节组合合并成更大的单元。

以 BPE 的简化过程为例,可以这样理解:

flowchart TD
    A[准备大量训练文本] --> B[从字符或字节级别开始]
    B --> C[统计相邻片段出现频率]
    C --> D[合并最高频的相邻片段]
    D --> E[更新词表]
    E --> C
    E --> F[达到词表大小后停止]
    F --> G[得到分词器词表]

假设训练文本里反复出现这些内容:

人工智能
人工智能模型
人工智能应用

分词器可能先发现:

人 + 工

经常相邻出现,于是合并成:

人工

又发现:

智 + 能

经常相邻出现,于是合并成:

智能

如果 人工智能 作为整体出现频率非常高,它还可能继续被合并成一个 Token。

最终词表里可能出现这样的条目:

TokenToken ID
1024
2048
人工8301
智能9120
人工智能15678
13

Token ID 只是词表编号,不代表大小关系。ID 为 15678 的 Token 并不比 ID 为 13 的 Token “更重要”,它们只是不同位置上的索引。

分词、编码和解码是一组配套动作

一次完整的大模型调用,大致可以拆成三个阶段。

sequenceDiagram
    participant User as 用户
    participant Tok as 分词器
    participant Model as 大模型

    User->>Tok: 输入文本
    Tok->>Tok: 切分为 Token
    Tok->>Model: Token ID 序列
    Model->>Model: 计算下一个 Token 的概率
    Model-->>Tok: 输出 Token ID
    Tok-->>User: 解码成人类可读文本

输入阶段,分词器把文本变成 Token ID:

"今天天气不错。"
        ↓
[TokenA, TokenB, TokenC, TokenD]
        ↓
[1234, 5678, 9012, 13]

计算阶段,模型根据已有 Token 预测下一个 Token 的概率分布。比如在“今天天气”后面,模型可能认为:

候选 Token概率
不错0.42
很好0.21
很热0.08
0.03

输出阶段,模型选出一个 Token,把它追加到已有序列后,再继续预测下一个 Token。大模型生成文字看起来像“一个字一个字打出来”,本质上通常是一个 Token 一个 Token 地生成,只是 Token 和可见字符不一定一一对应。

有时一个输出 Token 可能包含多个汉字,有时一个英文单词会被拆成多个 Token,所以流式输出时会出现“半个单词先出来”的感觉。

不同模型为什么切法不同

Token 不是统一标准,而是模型工程的一部分。不同模型的分词器可能差异很大,原因主要有几类。

影响因素会造成什么差异
训练语料不同高频词判断不同,常见片段会被更容易打包
词表大小不同词表越大,越可能容纳更多长片段
分词算法不同BPE、Unigram、WordPiece 的切分习惯不一样
多语言比例不同中文、英文、代码、日文等语言的 Token 效率不同
规范化规则不同空格、大小写、标点、全角半角处理方式不同
特殊 Token 不同系统提示、工具调用、图片占位等可能有专门 Token

例如同样是中文词语:

文本模型 A 可能的切法模型 B 可能的切法
鸡蛋鸡蛋 /
鸭蛋 / 鸭蛋
孙悟空孙悟空 / 悟空
哈哈哈哈哈哈哈哈哈 / 哈哈 / 哈哈哈

这些差异并不说明哪个模型“更懂”这些词,只说明它们的分词器词表和训练语料不同。判断某段文本到底消耗多少 Token,不能靠肉眼估算,应该使用对应模型的 tokenizer 或 API 返回的 usage 字段。

Token 和上下文窗口是什么关系

模型文档里的“上下文窗口”指一次请求中模型最多能处理多少 Token。这个数量通常包括:

  • 系统提示词;
  • 用户输入;
  • 多轮对话历史;
  • 工具调用描述;
  • 检索到的参考资料;
  • 模型即将生成的输出预算。

假设一个模型支持 128K Token 上下文,不代表你可以输入 128K Token 后再让它输出 10K Token。输入和输出往往共享同一个窗口,实际请求还要给输出预留空间。

flowchart LR
    A[系统提示 1K] --> B[历史对话 20K]
    B --> C[用户新问题 2K]
    C --> D[检索资料 30K]
    D --> E[预留输出 4K]
    E --> F[总上下文 57K]

当上下文超过模型限制时,常见处理方式有三种:

方式做法适合场景代价
截断删除最早或不重要的内容简单聊天、低风险任务可能丢掉关键信

评论