本文档概述了 tokenizer.cpp 的内部架构和关键实现决策。
分词器遵循标准的 HuggingFace 分词流水线 (Tokenizer Pipeline):
-
预分词 (Pre-tokenization):
- Regex Split (
SplitPreTokenizer):- 使用裁剪版
Oniguruma引擎进行基于正则的切分。 - 支持 GPT-2/4 风格的正则 (如
's|'t|...)。 - 处理特殊的 Unicode 属性正则 (如
\p{L}+或\p{N})。 - 支持
invert行为 (保留/不保留分隔符)。
- 使用裁剪版
- ByteLevel (
ByteLevelPreTokenizer):- 将文本转换为字节序列,通常用于 GPT-2 风格的模型。
- 处理
use_regex标志,决定是否先进行正则切分。
- Digits (
DigitsPreTokenizer/Digits):- 处理数字切分逻辑 (如 Llama 3 将数字单独切分)。
- Regex Split (
-
规范化 (Normalization):
- 顺序组合 (
SequenceNormalizer): 支持按顺序执行多个规范化步骤。 - NFKC (
NFKCNormalizer): 使用utf8proc库进行 Unicode NFKC 标准化。 - 替换 (
ReplaceNormalizer): 基于正则或字符串的替换逻辑 (支持prepend行为)。 - 前缀 (
PrependNormalizer): 添加特定前缀 (如 Llama 的_)。
- 顺序组合 (
-
模型核心 (Model):
- BPE (
BPE/BPEModel):- 实现标准的字节对编码算法。
- 支持
dropout(主要用于训练,推理时通常为 0)。 - Rank 合并: 使用预加载的
merges表和ranks映射进行高效合并,优先级 (Rank) 越小越先合并。 - Cache: 包含
cache机制加速常见单词的分词 (尽管在 C++ 实现中通常直接计算也足够快)。
- WordPiece (
WordPiece/WordPieceModel):- 支持 BERT 风格的最长匹配算法 (
max_input_chars_per_word,unk_token)。
- 支持 BERT 风格的最长匹配算法 (
- Unigram (
Unigram/UnigramModel):- 基于概率的 Unigram 分词算法 (主要用于 AlBERT, SentencePiece 模型)。
- BPE (
-
解码 (Decoder):
- ByteLevel (
ByteLevelDecoder): 将字节级 token 还原为 UTF-8 字符串。 - Fuse (
FuseDecoder): 简单的 token 拼接。 - Strip (
StripDecoder): 处理content的lstrip/rstrip,这在 Chat 模板渲染后处理中非常关键,用于去除特殊 tag 周围不必要的空格。 - Replace (
ReplaceDecoder): 逆向替换。
- ByteLevel (
为了支持复杂的 Unicode 正则 (如 \p{L}) 同时保持轻量级:
- 我们内置了
third_party/oniguruma。 - 裁剪: 移除了所有非 UTF-8 编码支持 (EUC-JP, SJIS 等),移除了 POSIX/GNU 兼容层,仅保留核心正则引擎。
- 体积优化: 使得最终二进制体积增加极小,远小于引入 ICU 或完整版 Oniguruma。
- 直接解析 HuggingFace 标准的
tokenizer.json。 - ujson 桥接: 引入了
ujson桥接层,允许在nlohmann/json和RapidJSON之间灵活切换。 - 后端切换: 开发者可以通过
UJSON_USE_RAPIDJSON宏启用 RapidJSON 后端。 - 性能提升: 在 40+ 模型的大规模测试中,RapidJSON 后端将模型加载时间从 ~92s 降低到 ~47s,实现了近 2 倍 的加载性能提升。
- 针对
pre_tokenizer和normalizer的多态类型实现了工厂模式加载。
- 使用
utf8proc进行字符迭代和宽字符属性查询。 OnigRegex类封装了对 UTF-8 字符串的正则搜索,自动处理char*与OnigUChar*的转换。
- Token ID 映射: 使用
std::unordered_map(或高效的 flat map) 存储词表。 - 字符串视图: 在内部处理中尽量减少字符串拷贝 (尽管为了接口安全,公共 API 接受
std::string).
我们采用 Golden Data 对齐策略,而非仅单元测试:
- Golden Data 生成 (
tests/generate_test_data.py):- 使用 Python
transformers库加载目标模型。 - 输入包含多语言、特殊符号、Emoji、代码片段的复杂文本。
- 导出
ids(Encode 结果) 和decoded(Decode 结果)。
- 使用 Python
- C++ 验证:
- C++ 测试程序加载相同的
tokenizer.json。 - 执行 Encode/Decode。
- 断言:
cpp_ids == py_ids且cpp_decoded == py_decoded。
- C++ 测试程序加载相同的
(测试结果基于 tests/model_tests.cpp 的自动化运行)
- Llama:
Llama-2-7b,Meta-Llama-3-8B-Instruct,Llama-3.2-3B-Instruct - Qwen:
Qwen2.5-3B-Instruct,Qwen2.5-Coder-32B,Qwen/QwQ-32B - DeepSeek:
DeepSeek-V3,DeepSeek-R1-Distill-Llama-8B - Phi:
Phi-3.5-mini-instruct - Mistral:
Ministral-3-3B-Instruct - GLM:
GLM-4-9b-chat - Gemma:
gemma-3-4b-it