当前为 v1.0.0.rc 预发布版,功能未全部就绪,需用 --pre 安装;Rust 侧 train 与 http 为默认关闭的可选 feature,需显式开启;README 提到 ModernBERT / OLMo 加载存在 64 位哈希碰撞问题待修复。

tokenizers
huggingfaceApache-2.0活跃维护用 Rust 实现的高性能分词器库,支持 BERT、GPT 等主流模型,兼顾研究实验与生产部署,解决文本预处理速度慢、跨语言调用不便的问题。
💥 Fast State-of-the-Art Tokenizers optimized for Research and Production
pip install --pre tokenizersREADME 未说明显存要求;不强制 SIMD,所有算子都有可移植的标量实现作为正确性基准,aarch64 用 NEON、x86_64 运行时检测 AVX-512 VBMI/SSE4.1、wasm32 需 simd128 目标特性,缺失时走标量路径。
Hugging Face 出品的工业级分词引擎,覆盖所有语言与模型,兼顾研究与生产
用 Rust 实现的高性能分词器库,支持 BERT、GPT 等主流模型,兼顾研究实验与生产部署,解决文本预处理速度慢、跨语言调用不便的问题。
核心能力
- 用 compile() 生成 CompiledTokenizer 执行编解码
- 内置 BPE、Unigram、WordPiece、WordLevel 模型
- bitcannon 以位流程序实现预分词,自带多套字节级语法
- 提供 Python、Node.js、WebAssembly 绑定,Rust 为参考实现
- 训练与 Hub 下载为可选 feature,可按需裁剪二进制体积
容易踩的坑
- v1.0.0-rc.4v1.0.0-rc.4 Release candidatepre-release2026-11-09 · 2026-11-09 14:23 UTC
查看更新说明
v1.0.0-rc.4
Release Candidate number 4 is out! It brings back the required Python APIs to use
tokenizersin other python libraries, for exampletransformers,vLLM, ...Install it with:
pip install --pre tokenizers==1.0.0rc4We're making steady progress towards feature parity with 0.23 on encoding and decoding, the main milestone left before a stable 1.0.0!
Breaking changes from
v1.0.0-rc.3If you built something on a previous release candidate, here are the breaking changes from
v1.0.0-rc.3:-
Python: The
Tokenizerclass no longer encodes, you need to calltokenizer.compile()first. ATokenizernow holds thetokenizer.jsonconfig and can be edited.compile()builds a frozenCompiledTokenizerfrom it.CompiledTokenizercan encode and decode. (#2562)tokenizer = Tokenizer.from_pretrained("openai-community/gpt2") tokenizer.truncation = Truncation(max_length=512) compiled = tokenizer.compile() compiled.encode("Hello, World!").ids
Benefits:
- Encoding takes no lock and releases the GIL
- Copying a
Tokenizeris cheap: copies share the config until one of them is edited - Padding and truncation live in the config and become the encode defaults; each encode call can still override them
New
- Python: composition API. Build a tokenizer from components, or swap one out on an existing tokenizer:
tokenizer.normalizer,.pre_tokenizer,.model,.post_processorand.decodercan be read and set. Components are shared like in 0.23: editing one updates every tokenizer using it (#2579)- Models:
BPE,Unigram,WordLevel,WordPiece, andTokenizer(model)(#2569) - Post-processors:
TemplateProcessing,RobertaProcessing,BertProcessing,ByteLevelandSequence(#2573) Sequencefor normalizers (#2577)- Component classes are now generated from the Rust declarations, so adding the remaining ones is cheap (#2629)
- Python: serialization.
Tokenizer.from_str,to_strandsave(#2563) - Python: vocab APIs.
get_vocab,get_vocab_size,token_to_idandid_to_token(#2614) - Python: added tokens.
AddedToken,Tokenizer.add_tokens,Tokenizer.add_special_tokensandCompiledTokenizer.added_tokens. Adding tokens one at a time is ~1000x faster than 0.23.1 (5k calls: 4 ms vs 3.85 s) (#2615) - Python:
DecodeStream, compatible with vLLM's usage (#2551)
Performance
- Added tokens use ~1000x less memory: gpt-oss goes from 1.71 MiB to 1.6 KiB. Decode is up to 20% faster (#2612)
- Many threads encoding at once no longer build a throwaway thread pool when the shared one is busy: up to 3x the throughput with 32 concurrent callers (#2633)
- Loading a BPE model builds its vocabulary once instead of three times (#2613)
Fixes
- Fixes a bug causing forked child processes to hang when they loaded or unpickled a tokenizer after the parent had loaded one. This is the pattern behind
datasets.map(num_proc=...)and PyTorchDataLoaderfork workers (#2581, #2632) - Fixes a bug causing byte-level BPE tokenizers whose vocabulary lacks some bytes to fail to load. They load again with a warning, and encoding drops the missing bytes, like 0.23 (#2550)
- Fixes a bug causing WordPiece tokenizers with a
"\0"token in their vocabulary (e.g.ai4bharat/IndicBERTv2-MLM-only) to fail to load (#2661) - Fixes a bug causing
Splitpre-tokenizers read fromtokenizer.jsonto ignore thefancy-regexbackend when it is enabled (#2547)
Internal
- Dependency updates, CI and README maintenance (#2575, #2582, #2587, #2606, #2609, #2610, #2621, #2659)
Contributors
- @SBrandeis
- @McPatate
- @ArthurZucker
- @dlowzzxx made their first contribution 🎉 Thank you!
Full Changelog: https://github.com/huggingface/tokenizers/compare/v1.0.0-rc.3...v1.0.0-rc.4
This changelog has been written with AI assistance
-
- v1.0.0-rc.3Release candidate v1.0.0-rc.3pre-release2026-10-19 · 2026-10-19 17:08 UTC
查看更新说明
v1.0.0-rc.3
1.0.0-rc.3is mainly a bindings release: it brings tokenizers to the browser with WebAssembly bindings!Install them with:
npm install tokenizers-wasm@nextWe're making steady progress towards feature parity with 0.23, the main milestone left before a stable 1.0.0.
New
- WebAssembly bindings, published on npm as
tokenizers-wasm. Release candidates go out under thenextdist-tag (#2450, #2497, #2552) - Python: encode pairs of text (#2453)
Fixes
- Fixes a bug causing byte-level BPE tokenizers whose vocabulary lacks bytes that can never appear in UTF-8 (e.g. ModernBERT) to fail to load. The error now names the missing byte (#2466)
- Fixes a bug causing WordPiece tokenizers with an empty-string token in their vocabulary to fail to load (#2538)
- Fixes a bug causing the Python, Node and WASM bindings to reject any
tokenizer.jsonwith a regexSplitpre-tokenizer orReplacenormalizer, which covers NLLB and every model transformers converted from SentencePiece (#2536) - Fixes a bug causing data races when Python's
Encoding.*_arraygetters were used concurrently; they now return an owned copy instead of a view (#2464) - Fixes a bug causing a 64-bit hash collision between two tokens to silently drop one of them when loading a vocabulary; it is now an error. Duplicate tokens log a warning instead of printing the whole vocab to stdout (#2467)
- Fixes a bug causing
ParityBpeTrainerto produce a different output than the regular BPE trainer; it now trains intoVocab+Mergeslike the others (#2474)
Python
huggingface_hub2.0 is allowed (#2486)- One unnecessary
Encodingcopy at the Rust/Python boundary is gone (#2452)
Internal
- Umbrella crate features: depend on
tokenizersalone and pick components by feature, e.g.cargo add tokenizers -F trainorcargo add tokenizers -F bpe --no-default-features. Thetk-*crates still work on their own (#2476, #2477, #2479)- The bindings now depend on the umbrella crate (#2478, #2552)
- The crate README and docs.rs front page now explain how to install the crate and pick its features (#2481)
- Dependency updates, CI hardening and other maintenance (#2446, #2448, #2449, #2465, #2471, #2472, #2473, #2475, #2480, #2492, #2495, #2510, #2537, #2548)
Thank you
- @SBrandeis
- @paulinebm
- @musicinmybrain
Full Changelog: https://github.com/huggingface/tokenizers/compare/v1.0.0-rc.2...v1.0.0-rc.3
This changelog has been written with AI assistance
- WebAssembly bindings, published on npm as
- v0.23.3Release v0.23.3 (python only)最新正式版2026-10-09 · 2026-10-09 10:20 UTC
Release v0.23.3
Python package release, other packages are unchanged
:rotating_light: Fixs a breaking change introduced in v0.23.1
What's changed
Patch for the python package with three fixes:
- Loosen the
huggingface_hubrequirements to allowhuggingface_hubv2 https://github.com/huggingface/tokenizers/pull/2486#issuecomment-6062880361 - Unblockstransformersinstalls - Fix type hints not resolving for pyright: https://github.com/huggingface/tokenizers/issues/2456
- Fix a breaking change from 0.23.1 regarding truncation and overflowing: https://github.com/huggingface/tokenizers/issues/2091
Full Changelog: https://github.com/huggingface/tokenizers/compare/v0.23.2...v0.23.3
- Loosen the
- v1.0.0-rc.2Release candidate v1.0.0.rc.2pre-release2026-09-21 · 2026-09-21 11:28 UTC
查看更新说明
Making
tokenizersthe sota tokenization library, same API, same token IDs, same standardsWe are happy to finally bring
tokenizersup to speed :zap: !tokenizerswas released 7 years ago, in a time where running the model was always the bottleneck. The ecosystem has evolved so much thattokenizersbecame a the bottleneck.As we saw many different libraries and project pop out, improving the tokenizer, we realized that our library's entire stack was too outdated for external contributors to contribute. This pushed us to refactor the codebase, and make sure the next developers come here to push the performances of tokenization.
The TLDR; is that we're a lot faster than before, crate size 6x smaller, peak memory is reduced. We worked a lot to reduce the UTF-8 tax, and make sure the improvements span across all languages.
For more details on the release, read this.
Big Kudos to @SBrandeis and @McPatate for this sprint. Special thanks to @sebpop for his involvement as well.
- v0.23.22026-09-03 · 2026-09-03 10:09 UTC
查看更新说明
This is the last v0 release, we are moving to v1!!
More details coming soon 👀
What's Changed
- Update daachorse from 1.0.1 to 3.0.0 by @musicinmybrain in https://github.com/huggingface/tokenizers/pull/2050
- Ci test fixes by @sebpop in https://github.com/huggingface/tokenizers/pull/2079
- Avoid full vocab clone in get_vocab_size() by @eunseo9311 in https://github.com/huggingface/tokenizers/pull/2074
- ci: pin benchmark jobs to a larger dedicated runner by @ArthurZucker in https://github.com/huggingface/tokenizers/pull/2084
- ci: target benchmark runners by group, not nonexistent label by @ArthurZucker in https://github.com/huggingface/tokenizers/pull/2090
- Bindings: acquire model read-lock once per call instead of per pre-token by @sebpop in https://github.com/huggingface/tokenizers/pull/2072
- ci: split audit in a separate job by @SBrandeis in https://github.com/huggingface/tokenizers/pull/2103
- ci: install libcairo on benchmark runners by @SBrandeis in https://github.com/huggingface/tokenizers/pull/2100
- feat(ci): enable npm's trusted publisher for releases by @McPatate in https://github.com/huggingface/tokenizers/pull/2111
- chore: bump pyo3 to version 0.29 by @SBrandeis in https://github.com/huggingface/tokenizers/pull/2115
- perf: add benchmarks for the
decodeAPI by @SBrandeis in https://github.com/huggingface/tokenizers/pull/2099 - ci: cache HF test data and authenticate Hub downloads to avoid rate limits by @SBrandeis in https://github.com/huggingface/tokenizers/pull/2102
- Implementing Parity-aware BPE by @cimeister in https://github.com/huggingface/tokenizers/pull/1974
- Make doc builds faster by @mishig25 in https://github.com/huggingface/tokenizers/pull/2158
- chore(deps-dev): bump websocket-driver from 0.7.4 to 0.7.5 in /tokenizers/examples/unstable_wasm/www by @dependabot[bot] in https://github.com/huggingface/tokenizers/pull/2208
- chore(deps-dev): bump shell-quote from 1.8.3 to 1.10.0 in /tokenizers/examples/unstable_wasm/www by @dependabot[bot] in https://github.com/huggingface/tokenizers/pull/2244
- chore: remove stale examples (kills 50% of dependabot traffic) by @ArthurZucker in https://github.com/huggingface/tokenizers/pull/2285
- chore(node): drop 8 unused devDependencies by @ArthurZucker in https://github.com/huggingface/tokenizers/pull/2286
- ci: pin every action to one SHA, drop stale audit suppressions by @ArthurZucker in https://github.com/huggingface/tokenizers/pull/2291
- chore(node): take node security alerts from 33 to 0 by @ArthurZucker in https://github.com/huggingface/tokenizers/pull/2287
- fix(python): pin the ruff rule set so a ruff release can't redden main by @ArthurZucker in https://github.com/huggingface/tokenizers/pull/2292
- Add a ParityBpeTrainer example and list it in the trainers API docs by @cimeister in https://github.com/huggingface/tokenizers/pull/2217
New Contributors
- @eunseo9311 made their first contribution in https://github.com/huggingface/tokenizers/pull/2074
- @cimeister made their first contribution in https://github.com/huggingface/tokenizers/pull/1974
Full Changelog: https://github.com/huggingface/tokenizers/compare/v0.23.1...v0.23.2
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
The SOTA tokenization library on all languages, all models, all hardwares.
Our goal with tokenizers is to develop and maintain the industry's standard tokenization engine, making it the defacto place for everyone to contribute to the whole ecosystem.
Release candidate: v1.0.0.rc.x
As we are switching from 0.23 to v1.0.0, the current library does not ship all features. If you are worried, check the v1 features section for more details of what we are bringing back. We will publish a blog about what breaking changes we introduced. We strived to keep them as small as possible.
Installation
pip install --pre tokenizers
For Rust, add the crate to your Cargo.toml:
[dependencies]
tokenizers = "1.0.0"
Training (train) and downloading from the Hub (http) are opt-in features. The crate's
Using the crate section lists every feature and how to
build a smaller binary.
Usage
>>> from tokenizers import Tokenizer
>>> tokenizer = Tokenizer.from_pretrained("meta-llama/Llama-3.1-8B") # or .from_file(path)
>>> compiled = tokenizer.compile()
>>> compiled.encode("Hello, y'all! How are you 😁 ?")
Encoding(ids=[128000, 9906, 11, 379, 65948, 0, 2650, 527, 499, 27623, 223, 949], type_ids=[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], attention_mask=[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
>>> compiled.encode(("What is this?", "A pair.")).type_ids
[0, 0, 0, 0, 0, 1, 1, 1, 1]
>>> compiled.tokenize("Hello")
['<|begin_of_text|>', 'Hello']
>>> compiled.decode([15339, 1917])
'hello world'
>>> compiled.decode_tokens(compiled.encode("Hello"))
['<|begin_of_text|>', 'Hello']
Tokenizer holds the tokenizer.json config and can be edited (e.g. tokenizer.truncation = ...);
compile() builds the CompiledTokenizer that encodes and decodes.
Performances
Bindings
| status | |
|---|---|
| Rust | ✅ reference implementation |
| Python | ✅ |
| Node.js | ✅ |
| WebAssembly | ✅ tokenizers-wasm on npm |
| C / C++ / Java / Go/ etc | 🚧 planned |
| Ruby | community, external repo |
v1.0.0 roadmap:
As we work toward v1, we are gonna bring back the entire python API that allows transformers' style of interacting with a tokenizer object:
TODOS:
Python Bindings
- Bring training back: this should be easy, but we want the perfs gains to come as well.
- Batch encoding API with zero-copy views on encoded ids
- dlpack API for torch / JAX / numpy interop.
- Composition: edit components of the pipeline (
tokenizer.normalizer = [...]). The editableTokenizerspec andcompile()have landed; per-component handles are next. tokenizer.saveto persist a modified or trained tokenizer on disk.role_to_tokenoverride.- Bring back offset output: this should be useful for people training.
- Windowing / stride in encode.
decode_stream.
Rust core
bitnorm: more performance from optimized normalization, as this will bring breaking changes we want to make sure it gets to v1.- Windowing (truncation with stride).
- Improve training performance
- Improve decode performance
- Offset tracking
- Padding: 2D flat buffer
- Wordcache:
shrnvs emulated movemask - Fix ModernBERT / OLMo load failure ("64-bit hash collision").
- Unroll more regex: we need to cover the most used ones, we might have missed one or two.
Other
- C & C++ bindings for loading, encode and decode APIs
After 1.0.0
- Improve / rewrite the
bitcannoncrate - Java, Go, Swift... bindings, supported by the C bindings
tk-devices— exploratory GPU encoding and batch decoding that keeps text and ids on the device: upload the vocabulary once, compute output positions in parallel, gather the bytes on the GPU. An optional component aimed at large batches, subject to prototyping and measurement.
Crate details
Crate size
Sub-crates
The models (BPE, Unigram, WordPiece, WordLevel) and the full pipeline: Normalizer,
PreTokenizer, Model, PostProcessor, Decoder.
Why separate: you don't need all features to serve a model in production settings. Splitting it from training means a serving binary never links a trainer, a corpus reader, legacy converters, etc.
Unicode tag classification plus pre-tokenization as a bitstream program. Follows Interleaved Bitstream Execution for Multi-Pattern Regex Matching on GPUs
(MICRO'25, 10.1145/3725843.3756052) but we added our own algorithm to make sure we don't need a parabix engine.
Characters are first classified into "tags", which use a sparse table generated when compiling bitcannon. This means there are 0 depencencies to the heavy unicode tables.
Ships byte-exact grammars for gpt2 / ByteLevel, cl100k, o200k, tekken, deepseek and kimi-k2. The classes or tags each have a high and low nibble (encoded on a u8), and carry refinement when needed. Using u8 tags allows us to leverage less bitsream, meaning have a simpler / faster pre tokenization.
Why separate: we just thought it might be useful to some people someday. As far as we know, this implementation is the fastest for every single pretokenizer regex on CPU.
from_json_file turns a canonical tokenizer.json into a PipelineTokenizer.
Why separate: this just makes it optional for some usecases!
canonicalize_file rewrites legacy a tokenizer.json written by any older version of this library into
the canonical form the reader accepts. A pure JSON→JSON rewrite; it depends on nothing but
std::path and serde_json.
Why separate: every config ever published stays readable without the runtime carrying a decade of compatibility branches. But, the legacy code amounted to quite a lot of the final crate size. Disabling it allows on device builds to be smaller.
The Trainer trait, every concrete *Trainer, TrainerWrapper and the Trainable extension.
The tokenizers crate re-exports it behind the train feature, which is off by default. It
trains only the models whose feature is on (bpe, unigram, wordpiece, wordlevel). Add
esaxx_fast for the C++ suffix array behind Unigram training.
Why separate: training is a batch job on a workstation; inference is a hot loop in a server. They have opposite constraints, so they get opposite dependency budgets.
cargo run -p bitmap_gen regenerates bitcannon's committed classify tables from unicode-properties, emitting one Atom tag per codepoint.
Why separate — this is what buys the zero Unicode dependency. unicode-properties is a
dependency of this crate and of nothing else: the tables it produces are checked into
bitcannon/src/classify/atom_tables.rs as ordinary source, so the Unicode data is resolved once, at
development time, by a crate that is never linked into anything that ships and is never published.
No build script either — bitcannon compiles with no code generation step, and a release binary
contains the tags without containing a Unicode crate to derive them. The release workflow re-runs
the generator and fails if the committed table differs, so "baked" cannot silently mean "stale".
A thin re-export so existing tokenizers::… paths keep working. Depend on this one unless you know
you want less.
Hardware
No SIMD is required. Every kernel has a portable path that is always compiled and is the byte-exact test oracle for its vectorised siblings, so correctness never depends on a kernel being present — only throughput does.
| op | aarch64 | x86_64 | wasm32 | portable fallback |
|---|---|---|---|---|
| Unicode atom classify | NEON (baseline) | AVX-512 VBMI → SSE4.1/SSSE3, runtime-detected | SIMD128 | classify_scalar |
| bitstream block build | NEON | SSE/AVX | — | build_block_scalar |
| literal & added-token scan | NEON | x86_64 | — | scalar |
| vocab bucket nibble match | NEON | — | — | scalar |
aarch64 selects at compile time (NEON is baseline). x86_64 dispatches at runtime via
is_x86_feature_detected!, so one binary covers every x86_64 CPU since 2008. wasm32 needs the
simd128 target feature; without it, the scalar walk.
Acknowledgements
This work stands on the shoulders of a lot of open source projects. Thanks to all of them for their great work.
gigatoken, tiktoken, kitoken, tokie, fastokens, wordchipper and ai-tokenizer each pushed on what a fast tokenizer can be; we read that work, and several of the ideas above reached us because another project showed they were worth trying.