LumTokenizer 1.0.8
dotnet add package LumTokenizer --version 1.0.8
NuGet\Install-Package LumTokenizer -Version 1.0.8
<PackageReference Include="LumTokenizer" Version="1.0.8" />
<PackageVersion Include="LumTokenizer" Version="1.0.8" />
<PackageReference Include="LumTokenizer" />
paket add LumTokenizer --version 1.0.8
#r "nuget: LumTokenizer, 1.0.8"
#:package LumTokenizer@1.0.8
#addin nuget:?package=LumTokenizer&version=1.0.8
#tool nuget:?package=LumTokenizer&version=1.0.8
LumTokenizer 1.0.8
English: A BPE tokenizer for .NET that loads Hugging Face tokenizer.json (vocab, merges, added_tokens, normalizer, pre_tokenizer) and runs a compiled encode-time pipeline aligned with upstream behavior, with DeepSeek V4–style configs as the primary interoperability target.
面向 .NET(当前目标框架 .NET 10)的 BPE 分词库:从 tokenizer.json 加载词表与合并规则,并在编码路径上执行与上游尽量一致的 normalizer 与 pre_tokenizer 管线。
快速开始
安装
dotnet add package LumTokenizer
编码与解码
using LumTokenizer.Tokenizer;
// 从 tokenizer.json 加载(merges / pattern 等见下文「自动适配」)
var tokenizer = BPETokenizer.CreateTokenizer("path/to/tokenizer.json");
var ctokenizer = ConcurrentBPETokenizer.CreateTokenizer("path/to/tokenizer.json");
Console.WriteLine(tokenizer.VocabSize);
var ids = tokenizer.Encode("hello! 你好");
Console.WriteLine(string.Join(",", ids));
Console.WriteLine(tokenizer.Decode(ids));
// 可选:限制词表大小(按 id 升序保留前 vocabSize 个 token)
// BPETokenizer.CreateTokenizer("path/to/tokenizer.json", vocabSize: 32000);
BPETokenizer:单线程使用。ConcurrentBPETokenizer:多线程共享实例时使用。
加载时的自动适配(无需额外参数)
CreateTokenizer(path) 内部统一调用 TokMap.LoadFromTokenizerJson。不要求为 merges 格式单独传开关,下列由实现根据 JSON 自动判断:
| 场景 | 行为 |
|---|---|
model.merges 首元素为字符串 |
按 Hugging Face 常见的 ["a b", "c d", ...] 解析;每行用第一个空格拆成左右两段 pair(与 mergesAsString 历史语义一致)。 |
model.merges 首元素为 JSON 数组 |
按 [["a","b"], ...] 二维 pair 解析。 |
merges 为空数组 |
走 pair 分支的反序列化(无合并规则时 BPE 仅依赖词表粒度)。 |
Split / normalizer Replace 的 pattern |
支持 "…" 纯字符串正则、{"Regex":"…"}、{"String":"…"}(字面量会按正则转义后再编译);其它形态会抛错提示。 |
文件须为含 "model" 节点的合法 JSON(扩展名不限);不再提供「强制字符串 merges」的公开 API——与 HF 导出的两种 merges 形态均由上表自动覆盖。
为何以 DeepSeek V4 的 tokenizer.json 为适配基准
DeepSeek V4(及同族)导出的 tokenizer.json 在真实业务里同时具备:
model.merges为字符串数组("a b"一行一个 pair),而非仅二维["a","b"];pre_tokenizer常为Sequence,内含 多段Split(正则 / String 字面量等多种 JSON 形态) 再接 ByteLevel;added_tokens与对话模板中的特殊串强相关。
我们以 该类配置文件能正确加载、且编码结果与 Hugging Face tokenizers / 官方推理栈一致 为第一目标;其它模型的 tokenizer.json 只要在下列字段能力范围内,一般也可直接使用。若某模型超出下表(例如 normalizer: Precompiled),会在构造或编译管线时显式报错或标注未支持。
设计取舍(我们是怎么考虑的)
语义优先于「手写 RegexType」
早期可在代码里固定一套正则做预切分;一旦对接任意tokenizer.json,切分规则必须以 JSON 里的pre_tokenizer为准。因此编码前增加 由 JSON 编译出的管线(TokenizerEncodePipeline):构造完成后根据normalizer/pre_tokenizer生成委托与步骤数组,避免在热路径上反复解析 JSON。ByteLevel 与 BPE 的衔接
当pre_tokenizer链 以 ByteLevel 结尾 时,片段已是词表所用字符空间,实现上会 跳过再次 UTF-8 + byte 映射,直接与 BPE 表交互,避免双重映射错误。merges双格式
Hugging Face 常见为字符串行;部分工具导出为二维数组。加载时根据model.merges[0]的 JSON 类型自动分支(见上文「加载时的自动适配」),再统一成内部 BPE 所需的 merge 顺序。性能与 AOT
BPE 核心仍配合ArrayPool等习惯用法;编码前管线工作在string片段列表上,与「纯 Span、零配置」相比会增加分配与分支,这是为兼容真实tokenizer.json的必要成本。
类库仍标记IsAotCompatible;运行时编码路径不依赖反射。加载 JSON 仍使用System.Text.Json的反射式反序列化,在 裁剪 / Native AOT 宿主中需按 .NET 文档自行处理 trim 或源生成上下文(与多数类库一致)。与上游的边界
极端Split行为、Metaspace.prepend_scheme、UnicodeScripts等与 Rust 版逐字节一致性的差异可能存在;遇到与官方不一致时,建议以 同一段文本 + 同一份tokenizer.json对照 HF 输出再提 issue。
tokenizer.json 字段与能力(概览)
下列为 当前版本会读取或参与编码语义 的节点;未列出的字段(如 decoder、post_processor、model 非 BPE 等)未接入 本库的加载与编码逻辑。
model
| 字段 | 说明 |
|---|---|
vocab |
词表 token → id,必需。 |
merges |
支持 ["a b", ...] 与 [["a","b"], ...];由加载逻辑按首元素类型自动选择解析方式(无需调用方配置)。 |
added_tokens
| 字段 | 说明 |
|---|---|
id / content |
特殊 token;编码时优先按最长匹配拆分,整段命中则走特殊 id。 |
normalizer
支持 type: "Sequence" 嵌套,以及下列子类型(未列出即构造编译阶段可能 NotSupportedException):
type |
说明 |
|---|---|
| Sequence | 子链 normalizers。 |
| Lowercase | ToLowerInvariant()。 |
| NFC / NFD / NFKC / NFKD | Unicode 规范化形式。 |
| Strip | left / right 控制 TrimStart / TrimEnd。 |
| Replace | pattern(与 Split 相同的多形态 JSON)+ content。 |
| StripAccents | 去重音类组合字符。 |
| ByteLevel | 对 UTF-8 字节做 ByteLevel 字符映射(与 HF 语义对齐为主)。 |
| Nmt | 控制字符与空白归一化近似。 |
| BertNormalizer | clean_text、handle_chinese_chars、strip_accents、lowercase。 |
| Precompiled | 不支持(需 SentencePiece 等预编译表)。 |
pre_tokenizer
支持 type: "Sequence" 与下列子类型:
type |
主要 JSON 字段 | 说明 |
|---|---|---|
| Sequence | pretokenizers |
顺序执行子步骤。 |
| Split | pattern(字符串 / Regex / String 对象)、behavior、invert |
behavior 支持常见写法:Isolated、Removed、MergedWithPrevious、MergedWithNext、Contiguous(下划线/连写均可)。 |
| ByteLevel | add_prefix_space、use_regex |
use_regex 为 false 时整段映射;GPT 式切分使用内置正则。字段 trim_offsets 可出现在 JSON 中,当前编码实现未单独实现其偏移语义。 |
| Whitespace / WhitespaceSplit | — | 空白处理。 |
| BertPreTokenizer | — | 空白与标点切分近似。 |
| Digits | individual_digits |
数字是否逐位拆开。 |
| Punctuation | behavior |
与 Split 类似的 Isolated 等策略。 |
| Metaspace | replacement(单字符)、prepend_scheme |
prepend_scheme:always / first / never 等常见取值。 |
| CharDelimiterSplit | delimiter(单字符) |
按分隔符切开。 |
| Replace | pattern、content |
正则替换。 |
| Strip | lstrip、rstrip |
段首尾空白。 |
| UnicodeScripts | — | 按脚本粗分桶的简化实现,与上游「按 Unicode Script 属性切分」在边界上可能略有差异。 |
测试与贡献
- 仓库内
UnitTest覆盖基础与鲁棒性用例;对接新模型时建议用 HFtokenizers或transformers对同一输入做 id 对比。 - 欢迎通过 Issue / PR 反馈未覆盖的
tokenizer.json片段或边界行为。
许可证
LumTokenizer 使用 MIT 许可证,见 LICENSE。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.