Hugging Face 发布 tokenizers v1 候选版,重写编码、解码和多线程路径,在兼容现有 token ID 的前提下单线程编码速度提升 3 至 30 倍,多线程扩展可达约 76%。新版本引入 Bitcannon 字节流分割、线程本地词缓存与改进的 BPE merge loop,旨在减少 CPU 对 GPU 的阻塞并为推理和大规模并发场景提升端到端性能。
Hugging Face 正在为其开源 tokenizers 库准备 1.0.0 版本。团队 9 月 21 日发布的技术说明显示,新的 tokenizers v1 候选版在保持与 v0.23 相同 token ID、词表和合并排名的前提下,重点重写了编码、解码和多线程执行路径。
在 Hugging Face 使用 Apple M4 Max 进行的基准测试中,v1 在单线程编码场景下相较 v0.23 提升约 3 至 30 倍,跨 8 个工作线程时达到线性扩展能力的 76%。不过,这些数字来自候选版和特定测试环境,实际收益仍会随着模型、语言、输入重复度、硬件和调用方式而变化。
Hugging Face 的官方技术说明称,tokenization 过去通常不是机器学习工作流的主要瓶颈,因为它相比模型计算本身要轻得多。但随着模型推理速度提升、数据集规模扩大、并发请求增加,以及长文本被反复处理,CPU 端的文本切分和编码可能开始让 GPU 等待数据。
v1 不改变模型看到的 token ID
Tokenizers v1 的核心承诺不是重新设计 tokenization 结果,而是在兼容性基础上重做内部实现。官方表示,v1 会产生与 v0.23 相同的 token ID,并尽量保留现有 API、词表和 merge rank;它也继续支持不同 tokenizer 家族,而不是只为 BPE 模型优化。
一个 tokenizer 通常要经过四个阶段:先对文本进行标准化,再把文本切分为 pre-token,随后由模型将这些片段转换为词表中的 token,最后加入模型所需的特殊 token。v1 的主要性能工作发生在模型阶段,但团队也同时调整了其他环节。
这意味着,使用者理论上不需要为了获得候选版的性能而改变编码调用方式。对于批量输入,团队特别建议使用 encode_batch,因为基准中的多线程扩展正是围绕批处理和共享 tokenizer 的场景展开。
从正则表达式转向位流处理
BPE tokenizer 通常先使用正则表达式,把输入分割成不会跨越边界合并的 pre-token。v1 针对一批常见的字节级 BPE 模式引入了名为 Bitcannon 的实现:它不再依赖通用正则表达式引擎逐字符扫描,而是使用手写的分割函数和 SIMD 指令处理 UTF-8 字节。
在这种方法中,输入字节被视为并行的位流,分割边界通过布尔运算从整组寄存器中计算出来。官方说明称,一次寄存器操作可以处理 64 个字节。这类思路与 Parabix 文本处理和 simdjson 的 JSON 解析方式有相似之处。
但这项优化并不适用于所有 tokenizer。只有当 tokenizer 的分割模式落入已经覆盖的语法范围时,才能使用这条快速路径;未被识别的模式仍会回退到正则表达式路径。因此,不同模型之间的加速幅度会有明显差异。
词缓存与新的 merge loop
v1 的另一项改动是线程本地 word cache。由于同一个 pre-token 在相同词表和合并规则下会产生相同的 token ID,系统可以把已经处理过的字节序列和结果保存下来。文本中重复词越多,缓存命中机会就越高;而包含大量独特词语的输入,可能无法获得同样的收益,甚至要承担额外的查找成本。
团队还重写了 BPE 的 merge loop。旧实现会在每个 pre-token 的处理过程中重复分配内存,并为每个片段建立新的优先级队列。v1 改为使用由调用方拥有的 scratch buffer,避免编码循环反复触碰分配器;相邻符号则通过预分配数组中的索引连接,合并时只需更新索引,而不必搬移数据。
候选合并对还被压缩进一个 64 位值,合并排名放在高位。这样,比较候选项可以退化为整数比较,下一次合并也能减少额外分支。多个 pre-token 还可以在一次模型调用中批量处理,进一步降低调用开销。
基准测试为何强调“相同 token ID”
Hugging Face 使用 tokbench 仓库运行这组测试,并允许开发者在自己的硬件上重跑。测试不仅比较单线程和多线程编码速度,也覆盖了不同模型、语言、延迟、解码吞吐、堆内存和 crate 体积。
为了避免只比较“谁更快”而忽略“谁算对了”,测试要求不同引擎生成的 ID 通过 FNV-1a 哈希校验,并只对所有引擎都成功运行且验证通过的测试单元计算中位数。加载词表的时间被排除在编码计时之外,工作线程则固定到不同的物理核心上。
这类设计很重要,因为 tokenizer 基准容易受到缓存和输入选择影响。反复编码同一份文档,可能测到的是缓存已经充分预热后的速度;连续处理互不相同的文档,则更接近大规模数据处理或在线服务中的另一类负载。Hugging Face 表示,其 headline 结果采用不同文档组成的输入,完整语料也不会全部装入缓存。
这也意味着“最高提升 30 倍”不能简单理解为所有应用都会获得同样幅度的加速。它是十个模型家族、特定语料和 Apple M4 Max 测试条件下的区间结果。官方结果页面还显示,单线程相对 v0.23 的提升约为 3 至 30 倍,8 核扩展达到线性扩展的 76%,p99 编码延迟提升约 9 至 28 倍,加载后堆内存降低约 2.8 倍。
tokenization 正成为推理系统的一部分
在模型计算占据绝大部分时间的阶段,tokenizer 的优化往往不容易成为产品卖点。但当模型本身变得更快,或者一个服务同时处理大量短请求时,文本进入模型之前的处理时间会更直接地影响首 token 延迟和 GPU 利用率。
这种变化也能从浏览器端推理的工程实践中看出。AIFlux 此前报道过 Hugging Face 发布 207 个 WebGPU 内核,其重点同样不是推出一个新模型,而是把影响端到端性能的底层操作变成可测试、可复用的软件组件。对 tokenizers v1 来说,优化对象则位于模型接收输入之前。
另一方面,性能数字本身也需要配合更严格的评测方法来解释。关于模型基准不应只看一个平均分的问题,可参考 AIFlux 对 BenchMIRT 题目级评测方法的报道。两者关注的问题不同,但共同说明,工程性能和模型能力都不能脱离测试条件来解读。
1.0.0 之前仍有兼容性工作
目前,v1 仍处于 release candidate 阶段。Hugging Face 已在 Rust 预发布版本中实现工作区拆分、Bitcannon、WordCache、可复用模型内存、批量模型调用和更快解码等功能。Node.js 绑定、角色到 token 的支持,以及面向 ExecuTorch 和 llama.cpp 的推理专用 C/C++ 绑定也列在已完成或正在整合的工作中。
距离 1.0.0,团队还计划统一训练验证和推理使用的编码实现,避免训练阶段与推理阶段产生不同 tokenization;让 offsets 和 masks 只在被请求时计算;继续改进 normalizer,并简化 Python bindings,同时保留现有的继承、序列化和自定义 decoder 行为。
官方给出的安装方式是通过 Cargo 添加预发布版本:
cargo add tokenizers --pre
如果只需要编码、不需要训练实现,则可以关闭默认特性并启用 HTTP 支持:
cargo add tokenizers --pre --no-default-features --features http
在更远的路线图中,Hugging Face 还提出探索 tok-devices,把 GPU 编码和批量解码作为可选组件,使文本和 token ID 尽量留在设备上。不过,这仍属于 1.0.0 之后的探索方向,并不代表当前版本已经提供 GPU tokenizer。
对开发者而言,tokenizers v1 的价值最终不只取决于一张峰值性能表,而取决于它能否在更多 tokenizer 家族、更多语言和更多运行时中保持相同输出,并把候选版的性能稳定带入 Transformers 及其他依赖库。Hugging Face 表示,随着支持范围扩大,这篇技术说明和基准结果还会继续更新。
来源: