9.2 文本的数字化表示:Tokenizer 原理

Tokenizer 分词编码过程

计算机只能处理数字。无论是加法运算还是图像识别,底层都是 0 和 1 的计算。大语言模型也不例外——它无法直接理解「央行宣布降准」这几个汉字,必须先把文字转换成数字序列,才能进行后续处理。

这个转换过程的核心工具叫 Tokenizer(分词器)。它决定了模型如何处理文本,是理解大语言模型工作原理的基础概念。

9.2.1 为什么需要 Tokenizer

文本到数字的桥梁

Tokenizer 的核心任务是建立文本和数字之间的映射关系。这个过程分三步:

  1. 分词:把连续的文本切分成离散的片段(Token)
  2. 编码:把每个 Token 映射到一个唯一的数字 ID
  3. 嵌入:把数字 ID 转换成模型可以运算的向量

用一个具体例子说明:

原始文本: "央行降准0.5个百分点"

第一步 分词:
["央行", "降", "准", "0", ".", "5", "个", "百分点"]

第二步 编码:
[15234, 892, 1456, 15, 13, 20, 156, 28934]

第三步 嵌入:
[[0.12, -0.34, ...], [0.56, 0.23, ...], ...]  # 每个 ID 对应一个向量
重要核心概念

Token(词元)是模型处理文本的基本单位。它不一定是一个完整的词,可能是一个字、一个词的一部分,甚至是一个标点符号。Token 的划分方式直接影响模型的理解能力和处理效率。

为什么不直接按字符处理

最简单的方案是把每个字符作为一个 Token。对于英文,就是 26 个字母加标点;对于中文,就是成千上万个汉字。这种方案看似直观,但存在两个问题:

第一,序列太长。一篇 1000 字的财报摘要,按字符切分就是 1000 个 Token。模型处理序列的计算量与长度的平方成正比,序列越长,计算越慢。

第二,语义被切碎。「百分点」是一个完整的金融术语,切成「百」「分」「点」三个字符后,模型需要从上下文重新推断它们合在一起的含义,增加了理解难度。

为什么不直接按词处理

另一个方案是按完整的词切分。英文天然有空格分隔,中文可以用分词工具。这种方案保留了语义完整性,但也有问题:

第一,词表太大。金融领域有大量专业术语:「逆回购」「MLF」「LPR」「社融」「同业拆借」……加上各类公司名、产品名,词表规模会膨胀到几十万甚至上百万。词表越大,模型参数越多,训练和推理成本越高。

第二,无法处理新词。如果训练时没见过某个词,模型就完全不认识。这在金融领域尤其麻烦——每年都有新的政策术语、金融产品名称出现。

9.2.2 分词的三种粒度

根据切分的精细程度,分词策略分为三类:

粒度 示例 词表大小 序列长度 OOV 问题
字符级 「央」「行」「降」「准」 极小(几千) 极长
词级 「央行」「降准」 极大(几十万) 较短 严重
子词级 「央行」「降」「准」 适中(3-10 万) 适中 轻微
注记知识卡片

OOV(Out-of-Vocabulary,词表外词)指模型词表中没有收录的词。遇到 OOV 时,词级分词只能用一个特殊的「未知词」符号代替,丢失全部语义信息。这在金融领域问题尤其突出——新股代码、新产品名称、新政策缩写都可能触发 OOV。

字符级分词(Character-level)

把每个字符作为独立的 Token。

优点:词表小,永远不会遇到 OOV。 缺点:序列太长,语义碎片化。

对于中文,一个汉字就是一个 Token;对于英文,每个字母是一个 Token。「interest rate」这个词在字符级下会被切成 13 个 Token(含空格),模型需要学会把这些字母组合起来理解「利率」的含义。

词级分词(Word-level)

按完整词切分。英文依靠空格,中文需要分词工具(如 jieba)。

优点:语义完整,序列短。 缺点:词表巨大,OOV 严重。

金融领域的专业术语多,词级分词的词表会极为庞大。更麻烦的是,一个新出现的术语(如某只新股的名称)会直接变成未知词。

子词级分词(Subword-level)

现代大语言模型普遍采用的方案。它的核心思想是:高频词保留完整,低频词拆成更小的片段。

例如,「央行」是高频词,保持完整;「逆周期」可能拆成「逆」「周期」;一个罕见的公司名可能被拆成若干个字符组合。

这种方案在词表大小、序列长度和 OOV 处理之间取得了平衡。即使遇到训练时没见过的词,子词分词也能把它拆成见过的片段,保留部分语义信息。

9.2.3 BPE 算法原理

BPE(Byte Pair Encoding,字节对编码)是最流行的子词分词算法,GPT 系列模型使用的就是 BPE 的变体。它的核心思想很直观:从字符开始,反复合并最常一起出现的字符对,直到达到预设的词表大小。

算法步骤

以一个简化的例子说明。假设训练语料只有三句话:

"央行降准"(出现 5 次)
"央行加息"(出现 3 次)
"降准预期"(出现 2 次)

第一步:初始化

把所有文本拆成字符,统计每个字符的出现频率:

初始词表: {"央", "行", "降", "准", "加", "息", "预", "期"}

字符频率:
央: 8 次, 行: 8 次, 降: 7 次, 准: 7 次
加: 3 次, 息: 3 次, 预: 2 次, 期: 2 次

第二步:找出最高频字符对

扫描语料,统计相邻字符对的出现频率:

字符对频率:
(央, 行): 8 次  ← 最高频
(降, 准): 7 次
(加, 息): 3 次
(预, 期): 2 次

第三步:合并最高频对

把「央」和「行」合并成一个新 Token「央行」,加入词表:

更新后词表: {"央行", "央", "行", "降", "准", "加", "息", "预", "期"}

第四步:重复合并

重新统计字符对频率,继续合并次高频的「降准」:

更新后词表: {"央行", "降准", "央", "行", "降", "准", "加", "息", "预", "期"}

如此反复,直到词表达到预设大小(如 50,000 个 Token)。

为什么 BPE 有效

BPE 的关键优势在于它自动学习了语言的统计规律:

  1. 高频组合会被合并成完整 Token,如「央行」「降准」
  2. 低频组合保留字符级表示,保证任何新词都能处理
  3. 词表大小可控,不会无限膨胀

对于金融文本,BPE 会自动识别并合并常见术语。「存款准备金率」可能被分成「存款」「准备」「金」「率」四个 Token——虽然不是完美的切分,但每个片段都保留了部分语义,模型可以从上下文理解完整含义。

提示实践提示

不同模型的 Tokenizer 切分结果可能不同。同样一句话,GPT-4 可能切成 10 个 Token,Claude 可能切成 12 个。这是因为它们的训练语料和词表不同。评估模型成本时,需要使用对应模型的 Tokenizer 计算 Token 数量。

9.2.4 其他分词算法

除了 BPE,还有几种常见的子词分词算法:

WordPiece

由 Google 开发,BERT 系列模型使用。与 BPE 的主要区别在于合并策略:

  • BPE 合并频率最高的字符对
  • WordPiece 合并能最大化语言模型似然的字符对

实践中两者效果接近。WordPiece 的一个特点是用「##」前缀标记非词首的子词。例如「playing」会被切成「play」和「##ing」。

Unigram Language Model

由 Google 提出的另一种方法。它从一个较大的初始词表开始,逐步删除对语言模型影响最小的词,直到词表缩小到目标大小。与 BPE 的从小到大策略相反,Unigram 采用从大到小的删减策略。

SentencePiece

严格来说,SentencePiece 不是一种分词算法,而是一个分词工具库。它的特点是:

  • 直接在原始文本上训练,不依赖预分词
  • 把空格也作为普通字符处理(用特殊符号「▁」表示)
  • 支持 BPE 和 Unigram 两种算法

这使得 SentencePiece 能统一处理各种语言,不需要针对每种语言设计预处理规则。

算法 使用模型 特点
BPE GPT 系列、LLaMA 简单高效,工业标准
WordPiece BERT、DistilBERT 基于似然合并
Unigram T5、ALBERT 从大词表删减
SentencePiece 多语言模型 语言无关,统一处理

9.2.5 Token 与模型能力的关系

Tokenizer 的设计直接影响模型的多项能力指标。

上下文窗口的真实容量

模型的上下文窗口以 Token 数量计算,而非字符或字数。Claude 3.5 Sonnet 的上下文窗口是 200K Token,这个数字对应多少实际文字,取决于 Tokenizer 的切分效率。

对于英文,1 个 Token 大约对应 4 个字符,即 0.75 个单词。一篇 1000 词的英文文章约占用 1300 个 Token。

对于中文,1 个 Token 大约对应 1.5-2 个汉字。一篇 1000 字的中文文章约占用 500-700 个 Token。

上下文容量估算:

Claude 3.5 Sonnet (200K Token):
- 英文: 约 15 万词 / 约 50 万字符
- 中文: 约 30-40 万字

GPT-4 Turbo (128K Token):
- 英文: 约 10 万词
- 中文: 约 20-25 万字
注记知识卡片

中文的 Token 效率通常高于英文,这与中文的信息密度有关。一个汉字承载的语义信息通常比一个英文字母多。在处理同等信息量的文本时,中文占用的 Token 数更少。这意味着中文用户在相同的上下文窗口限制下,可以输入更多的内容。

Token 计费与成本控制

API 调用按 Token 计费。输入和输出分别计价,输出通常比输入贵 2-4 倍。

模型 输入价格 输出价格
Claude 3.5 Sonnet $3 / 百万 Token $15 / 百万 Token
GPT-4o $2.5 / 百万 Token $10 / 百万 Token
Claude 3 Opus $15 / 百万 Token $75 / 百万 Token

一个金融问答系统,假设每次请求平均:

  • 系统提示:500 Token
  • 用户问题:100 Token
  • 检索文档:2000 Token
  • 模型回答:500 Token

则每次请求消耗 2600 输入 Token + 500 输出 Token。使用 Claude 3.5 Sonnet,每次成本约:

输入成本: 2600 / 1,000,000 × $3 = $0.0078
输出成本: 500 / 1,000,000 × $15 = $0.0075
单次总成本: $0.0153(约 0.11 元人民币)

日均 1000 次请求,月成本约 3300 元。

警告注意

在设计 RAG 系统时,检索返回的文档片段是成本的主要来源。返回 5 个 500 Token 的片段意味着每次请求增加 2500 输入 Token。平衡检索数量和成本是系统设计的重要考量。

9.2.6 Tokenizer 的局限性

尽管子词分词已经是当前的最佳实践,它仍有一些固有局限:

对数字和代码不友好

Tokenizer 通常把每个数字单独切分。「2024」可能被切成「20」「24」或「2」「0」「2」「4」。这导致模型对数字的理解不如对文字那么精确——它很难把「2024」理解为一个完整的年份。

在金融场景中,数字至关重要。股价、汇率、收益率都是精确到小数点的数字。模型在处理这些数字时,偶尔会出现计算错误或数字混淆。

对专业术语切分不理想

即使是高频金融术语,也可能被切分得不够理想。「逆回购利率」可能被切成「逆」「回」「购」「利率」四个 Token,而非「逆回购」「利率」两个 Token。模型需要从上下文学习这些片段的组合含义。

跨语言一致性问题

多语言模型的 Tokenizer 需要平衡各语言的表达效率。有些模型对英文优化更多,导致其他语言的 Token 效率较低。选择模型时,可以测试一下目标语言的 Token 效率。