Misc. bug: Tokenizer (GPT-4o and Tekken pre-tokenizers): token IDs differ from HF because combining marks are split off their letters (Hindi

当使用 llama-tokenize 或加载 gpt-oss、Mistral NeMo 等模型处理印地语、泰语、孟加拉语等含组合标记的文字时,llama.cpp 给出的 token ID 与 Hugging Face 不一致。优先检查 src/llama-vocab.cpp 中 GPT-4o/Tek

快速结论:当使用 llama-tokenize 或加载 gpt-oss、Mistral NeMo 等模型处理印地语、泰语、孟加拉语等含组合标记的文字时,llama.cpp 给出的 token ID 与 Hugging Face 不一致。优先检查 src/llama-vocab.cpp 中 GPT-4o/Tekken 预分词器的 lookahead 正则是否遗漏了 \p{M}。

适用环境:Issue 确认的环境为 macOS,源码构建自 commit 31288c9c6(2026-10-18),受影响模块为 libllama(核心库)。模型侧涉及 gpt-oss、Mistral NeMo,以及与 gpt-4o、llama4、kanana2、talkie、minimax-m2、tekken 这些 tokenizer.ggml.pre 取值相关的预分词器。Issue 未提供 Python、CUDA、显卡或具体依赖版本信息。

最快修复方案:暂无确认的一步修复方案。Issue 中提出并在讨论中确认要提交的两行修改为:在 TEKKEN 和 GPT4O/MINIMAX_M2 两处 lookahead 正则中加入 \p{M},参考 GRANITE_EMB_MULTI 已采用的 (?=[\p{L}\p{M}]) 写法。该修改尚需与对应的新词表测试 PR 一起合并后才能验证。

注意事项:直接修改只影响上述两个预分词器分支,不会自动修复其他未采用相同正则的预分词器。新增测试词表按维护者要求应放到 Hugging Face 的 ggml-org/vocabs,而不是加入本仓库;Issue 中未给出该修改已合并的最终证据,应用前请确认对应 PR 状态。

问题场景

用户在 macOS 上源码构建 llama.cpp,调用 llama-tokenize 对 gpt-oss 模型做编码测试,输入印地语文本 दी(U+0926 U+0940)。llama.cpp 将其拆成两个 token,而 Hugging Face 的 tokenizers 库将其编码为单个 token。同类问题在 Mistral NeMo 上也可复现。

由于该问题位于 libllama 的词表/预分词逻辑中,凡是使用受影响 tokenizer.ggml.pre 取值的模型,在普通句子中遇到组合标记都可能出现 token 数偏多,进而导致提示词变长,或产生模型训练时未见过的 token 序列。

报错原文

Misc. bug: Tokenizer (GPT-4o and Tekken pre-tokenizers): token IDs differ from HF because combining marks are split off their letters (Hindi, Thai)

$ ./build/bin/llama-tokenize -m gpt-oss_20b.gguf -p "दी" --log-disable
  2587 -> 'द'
   840 -> 'ी'

$ python -c "import tokenizers as t; print(t.Tokenizer.from_file('gpt-oss/tokenizer.json').encode('दी', add_special_tokens=False).ids)"
[25589]

原因分析

最可能的原因是 src/llama-vocab.cpp 中的重写规则 ((?=[\p{L}])([^a-z])) 只检查 Unicode 字母类 \p{L},没有包含组合标记类 \p{M}。而上游(Hugging Face)的字母类定义把 \p{M} 视为可延续单词的一部分。

因此,当组合标记跟在字母后面时,llama.cpp 不会把它并入前一个词,而是将其切分成独立的预分词单元,最终导致 token ID 与参考实现不同。该问题出现在 TEKKEN 分支和 GPT4O/MINIMAX_M2 分支两处。

该重写规则由 PR #8579 于 2024 年引入,当时已注意到底层类别 M 被忽略,但缺少能暴露问题的测试用例。此前 GRANITE_EMB_MULTI(#22716)已单独改为 (?=[\p{L}\p{M}]),但该修复仅覆盖该模型。

环境排查

  • 确认 llama.cpp 构建所基于的 commit;Issue 中出问题的版本为 31288c9c6(2026-10-18)。
  • 确认模型的 tokenizer.ggml.pre 取值是否属于 gpt-4o、llama4、kanana2、talkie、minimax-m2、tekken 之一。
  • 确认所比较的 Hugging Face tokenizer.json 与 GGUF 内嵌词表来自同一模型。
  • 用含组合标记的文本(印地语、泰语、孟加拉语、泰米尔语、泰卢固语等)分别向 llama.cpp 和 Hugging Face 编码,对比 token ID 和 token 数量。
  • 检查 src/llama-vocab.cpp 中对应分支的 lookahead 正则是否仍为仅含 \p{L} 的旧写法。

解决步骤

  1. 定位 src/llama-vocab.cpp 中 GPT4O/MINIMAX_M2 分支和 TEKKEN 分支的预分词重写正则,确认当前为 ((?=[\p{L}])([^a-z])) 形式。
  2. 按 Issue 提出的两行修改,将两处 lookahead 均补上 \p{M},即采用 (?=[\p{L}\p{M}]) 形式,与 GRANITE_EMB_MULTI 已有写法保持一致。
  3. 重新构建 llama.cpp,使用与 Issue 相同的 llama-tokenize 命令对 दी 做一次编码。
  4. 若需要补充回归测试,按维护者意见,新词表放入 Hugging Face 的 ggml-org/vocabs,不要往本仓库 models/ 添加;并注意该测试需要与修复 PR 一同合入,否则会在 master 上失败。

验证方法

修复后再次执行 llama-tokenize,दी 应输出与 Hugging Face 一致的单个 token,而不再是 2587 与 840 两个 token。进一步用标准测试字符串集和含组合标记的多语言句子,对比 llama.cpp 与 Hugging Face 的 token ID 是否一致。Issue 作者报告,在加入修复后,gpt-oss 的 7,248 条测试字符串中此前 271 处不一致全部消失,且已有的 15 个 test-tokenizer-0 词表仍全部通过。

参考来源

ggml-org/llama.cpp #30787

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29507

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注