Tokenizer 详解目录本质问题为什么需要 TokenizerTokenizer 的三种切法——按粒度分2.1 Word-level单词级2.2 Char-level字符级2.3 Subword子词级⭐ 业界标准三种主流 Subword 算法——按怎么切分3.1 BPEByte Pair Encoding—— 最主流、GPT 系列在用1. 本质问题为什么需要 Tokenizer一句话模型只认识数字不认识文字。Token 是文本和数字之间的翻译单位。我今天学习了反向传播 ↓ Tokenizer [12875, 392, 561, 18903, 6521, 98] ← token IDs一串整数 ↓ Embedding 层 [[0.12, -0.34, ...], [...], ...] ← 向量模型真正能处理的东西没有 Tokenizer模型连一个汉字都读不懂。它是所有 NLP 模型的入口。核心的 “翻译” 过程输入文本 → Tokenizer → Token IDs → Embedding → 模型计算 ↑ 词典vocabulary / vocabTokenizer 做的事情本质上就是查词典编码Encode文本 → Token IDs解码DecodeToken IDs → 文本可逆性要求理想情况下编码再解码应该完全还原原文Tokenizer 做决策的关键问题任何一个 Tokenizer 都要回答三个问题切多细——按词切按字切还是按中间粒度切词表多大——几万个还是几十万个没见过的新词怎么办——是抛异常还是能拼出来这三个问题的不同回答就衍生出下面三种切分方法。2. Tokenizer 的三种切法——按粒度分三种方式对应三个不同层级的切分单位。─────────────────────────────────────────────────────────── 方法 | 粒度 | 例子我爱机器学习 ─────────────────────────────────────────────────────────── Word-level | 单词级 | [我, 爱, 机器, 学习] ← 按词切 Char-level | 字符级 | [我, 爱, 机, 器, 学, 习] ← 按单字切 Subword | 子词级 | [我, 爱, 机器, 学习] ← 按最常用的片段切 ───────────────────────────────────────────────────────────2.1 Word-level单词级核心思路按自然语言的单词为单位切分。英文以空格和标点为界中文需要额外分词器。英文 I love machine learning → [I, love, machine, learning] 中文需分词工具如 jieba 我爱机器学习 → [我, 爱, 机器, 学习]词表规模英文约 50 万 单词包含所有变形 run, runs, running, ran — 4 个独立的词 unbelievable, believable, believe — 3 个独立的词 中文理论上无限组合词太多 学习、学会、学好、学完、学成、学以致用...优点缺点直觉最自然每个 token 语义完整词表极大英文 50 万实现简单遇到未登录词OOV直接认不出人类可理解性最强“run”/“running”/“ran” 被当成三个完全不同的词中文需要额外分词工具且边界划分有歧义OOVOut-of-Vocabulary超出词表外的字词问题词表里只有word-level 级别的常见词 实际遇到 tokenizationalization ← 不在词表中 → OOV模型不认识 → 只能用 UNKUnknown特殊 token 替代 → 信息丢失了 结论Word-level 在开放场景下不可行。现实GPT / LLaMA / Qwen 都不用 Word-level。词表太大 OOV 问题无解。2.2 Char-level字符级核心思路以单个字符字母/汉字为单位切分。词表极小。英文 unbelievable → [u, n, b, e, l, i, e, v, a, b, l, e] I love ML → [I, , l, o, v, e, , M, L] 中文天然适合因为汉字本身就是字符 我爱机器学习 → [我, 爱, 机, 器, 学, 习]词表规模英文26 字母 大小写 数字 标点 空格 ≈ 200 个 token 中文常用汉字约 6,763 个GB2312 一级 二级 全部 CJK 统一汉字约 80,000 中文用字符级的话词表要比英文大得多优点缺点词表极小英文 ~200序列极长1 个单词变 12 个 token没有 OOV 问题任何字符都能表示注意力计算量是序列长度的平方增长任何语言、任何特殊符号都能处理单个字符几乎没有语义信息跨语言一致性统一处理模型难以学到单词级别的模式序列长度问题同样是 100 个英文单词的段落 Word-level: ≈ 100 tokens含标点 Char-level: ≈ 500-600 tokens平均每个单词 5-6 个字母 Transformer 自注意力是 O(n²) 复杂度 Word-level: 100² 10,000 次计算 Char-level: 600² 360,000 次计算 → 36 倍 这意味着同样的计算资源下Char-level 只能处理 Word-level 1/6 的文本长度。现实极少直接使用。Transformer 的序列长度是瓶颈用太多 token 表示一段文本太浪费。2.3 Subword子词级⭐ 业界标准核心思想常用词不分保留语义生僻词切成已知片段解决 OOV。平衡了 Word-level 和 Char-level 的优缺点。unbelievable → [un, believe, able] ← 3 个已知子词 believable → [believe, able] ← 2 个已知子词 特朗普 → [特朗, 普] ← 2 个高频汉字片段BPE 训练后合并处理未登录词这是 Subword 最强大的能力tokenizationalization 情况 A这个词不在词表中Word-level → OOV ❌ 情况 B每个字母拆开Char-level → 13 个 token太长 ❌ 情况 CSubword 切分 → [token, ization, al, ization] → 4 个 token每个都在词表里 ✓ → 意思保留token 化 的 化大致可理解优点缺点词表可控典型 8k-128k 大小切分结果有时违反直觉无 OOV 问题生僻词也能拼出来相同词在不同上下文中可能被不同切法序列长度适中比 Word-level 略长远短于 Char-level多语言场景下低频语言编码效率低平衡了语义完整性和词表大小训练 Tokenizer 需要大量语料为什么 Subword 能成为业界标准三者的量化对比同一段英文 100 词 词表大小 序列长度 OOV处理 语义密度 Word-level 50k ~100 ❌ ⭐⭐⭐ Char-level ~200 ~500 ✓ ⭐ Subword 32k ~120 ✓ ⭐⭐⭐ ↑ ↑ ↑ 合理大小 几乎不膨 无死角覆盖 Subword 在三者中找到了最佳平衡点。Subword 的反向理解如果你学过词根词缀背单词你就已经理解 Subword 了 unlockable un lock able → 不认识这个词没关系三个子词你都认识 → 组合意思不能 锁 能够 → 可以解锁的 Transformer 的 Subword Tokenizer 做的也是类似的事 把词汇拆成语素级别的片段组合使用。3.三种主流 Subword 算法——按怎么切分上文我们讲了 Subword “是什么”——它是介于单词和字符之间的切分粒度。接下来讲 Subword “具体怎么切”——有三种算法分别被不同模型家族采用。Subword 算法三大家族 BPEByte Pair Encoding ← GPT / LLaMA / Qwen / StableLM Unigram ← T5 / Gemma / SentencePiece 默认 WordPiece ← BERT / ELECTRA3.1BPEByte Pair Encoding—— 最主流提出Neural Machine Translation of Rare Words with Subword Units (Sennrich et al., 2016)使用模型GPT 全系列GPT-2/GPT-3/GPT-4、LLaMA 1/2/3、Qwen、DeepSeek、StableLM 等——绝大多数当前主流模型。3.1.1 原理自底向上的合并一句话从字母/单字开始逐步合并出现频率最高的相邻字节对。训练过程图解Step 0初始状态 词表 所有单字符26 字母 标点 空格等 语料 low low low low low lower lowest Step 1拆成字符 l o w _ l o w _ l o w _ l o w _ l o w _ l o w e r _ l o w e s t _ 表示空格 Step 2统计相邻 pair 频率 (l, o): 出现 8 次 ← 假设数字 (o, w): 出现 8 次 (w, _): 出现 5 次 (w, e): 出现 2 次 ... 最高频的 pair: (l, o) 或 (o, w) Step 3合并最高频 pair 假设 (l, o) 是最高频 → 合并为 lo 词表新增 lo 语料变成lo w _ lo w _ lo w _ lo w _ lo w _ lo w e r _ lo w e s t Step 4继续统计、合并 现在 (lo, w) 出现 8 次 → 合并为 low 词表新增 low 语料变成low _ low _ low _ low _ low _ low e r _ low e s t Step 5继续 (low, _) 出现 5 次 → 合并 low_ (e, r) → 合并 er (e, s) → 合并 es (es, t) → 合并 est ... Step 6停止 当词表大小达到预设值如 32,000时停止训练 最终词表 {..., low, low_, er, est, ...}核心要点训练完成后BPE 的贪婪解码规则 → 对输入文本从左到右优先匹配词表中最长的子词 → 匹配不上就逐字符回退 例子假设词表里有 low 但没 lower lower → 从左往右匹配 l → 不是最长继续 lo → 有 low→ 匹配 low ✓ 剩下 er → 匹配 er ✓ → 结果[low, er]3.1.2 实际案例GPT 系列的编码表现英文编码效率GPT-4≈100k 词表 hello world → [15339, 1917] ← 2 个 token Hello World → [15496, 21592] ← 2 个不同 token大小写敏感 the quick brown fox → [9, 9, 9, ...] ← 高频词各占 1 个 token antidisestablishment → 未知词 → 切成子词 ← 不会 OOV 英文平均效率约 3.5-4.5 字符/token中文编码效率在 GPT 这种英文优化的 Tokenizer 下GPT-4中文编码效率较差——这是 GPT 的已知弱点 我在学习反向传播 → 每个中文字几乎各占 1-2 个 token → 约 8-12 个 token 对比 Qwen 2.5中文优化 Tokenizer 我在学习反向传播 → 我 在 学习 反向传播 → 约 4-5 个 token 结论GPT 的中文效率约为 1.5-2 chars/token 而 Qwen 可以达到 4-5 chars/token → 同样的中文文本GPT 要多花 2-3 倍的价格GPT-4o 的 Tokenizer 更新2024旧版 GPT-3.5 的 p50k_base gpt → [g, pt] ← 被切开了 ChatGPT → [Chat, GPT] ← 还可以 api → [api] ← 完整 新版 GPT-4o 的 o200k_base gpt → [gpt] ← 作为一个完整 token ✓ ChatGPT → [Chat, GPT] ← GPT 作为整体 ✓ api → [api] ← 完整 词表从 ~100k 扩展到 ~200k 常见 AI 相关术语被保留为完整 token 编码效率整体提升 10-20%3.1.3 代码场景的 BPE 表现BPE 在代码 自然语言混合场景下有一些特殊现象# Python 代码的 tokenization def fibonacci(n): → 可能保持完整因为 def 是高频词 softmax_attention → [softmax, _, attention] 或 [soft, max, _attention] __init__ → [_, _, init, _, _] ← dunder 方法常被切成碎片代码和自然语言混合的挑战挑战 1空格和缩进 Python 用缩进表示代码块 如果 Tokenizer 把连续空格/缩进合并了代码语义就变了 解决方案保留空格作为单独 token或在 Pre-tokenizer 阶段特殊处理 挑战 2命名约定 snake_case: tokenizer_config → 下划线作为分隔符 camelCase: tokenizerConfig → 大写字母作为隐式分隔 PascalCase: TokenizerConfig → 大写开头作为隐式分隔 BPE 对这些命名约定的处理方式不同影响代码理解效率 挑战 3数字 1234567890 → BPE 可能切成 [123, 456, 7890] → 模型看到的是碎片化的数字而不是完整数字 → 影响数学计算能力 → 解决方案split_digitsTrue — 每个数字独立成 token3.1.4 GPT-4o 的被切开的单词特征表以下是一些实际测试的常见词汇的 token 分解情况基于 o200k_base完整 token1 个 token GPT, AI, chat, api, token, model, data, train, test, python, java hello, world, transformer, attention 被切开的2 个 token tokenization → [token, ization] finetuning → [fine, tuning] reinforcement → [reinforce, ment] bidirectional → [bi, direction, al] parameter → [para, meter] 或完整 initialization → [initial, ization] 这些切分方式有些符合语言学直觉如 token ization 有些则完全出于统计频率如 bi direction al。3.1.5 BPE 的优缺点总结优点缺点原理简单、训练高效贪心匹配不是全局最优切分词表大小可控相同词在不同上下文可能被不同切法无 OOV 问题对中文等非空格语言不友好需预处理经验证明效果最好GPT/LLaMA 都用低频 token 可能出现浪费实现成熟tiktoken / HuggingFace 都支持训练语料分布偏差会影响合并结果为什么 BPE 是事实标准不是因为它理论最优Unigram 理论上更优雅而是因为经验最优——OpenAI 在 GPT-2/GPT-3 上大规模验证了它的有效性后来 LLaMA/Qwen/DeepSeek 跟进形成了生态效应。工具链tiktoken、HuggingFace tokenizers也围绕 BPE 最成熟。3.1.6 与 Unigram / WordPiece 的前瞻对比概要维度BPEUnigramWordPiece合并方向自底向上合并自顶向下裁剪自底向上合并合并/保留标准出现频率损失函数减少量互信息PMI是否概率模型否是否训练速度快中中代表模型GPT / LLaMA / QwenT5 / GemmaBERT / ELECTRA中文支持需配合 Pre-tokenizer原生支持更好SentencePiece需配合预处理附录关键概念快速索引概念一句话解释Token文本切分后的最小单位可能是词、字符或子词Token ID每个 Token 对应的整数编号词典中的索引词表Token 到 ID 的映射字典VocabularyOOV词表中不存在的词Out-of-VocabularyUNK代替 OOV 的特殊 tokenUnknownBPE自底向上合并高频子词的算法Pre-tokenizer正式训练前的粗切分如空格 标点分割Encoder文本 → Token IDs 的过程DecoderToken IDs → 文本的过程编码效率每个 token 平均能表示多少字符chars/token