Skip to content

分词与词表 — 概念

分词算法

llama.cpp 支持以下分词算法:

BPE (Byte Pair Encoding)

GPT 系列模型使用的算法:

  1. 从字符级开始
  2. 反复合并 rank 最小的相邻 token 对(由优先队列 llm_bigram_bpe::queue 调度,rank 越小越优先)
  3. 编码时对剩余相邻 bigram 重复上述合并,直到无可合并项
"hello world" → ["he", "llo", " world"]

SPM (SentencePiece)

LLaMA 系列使用,基于 byte-level BPE + byte fallback:

  • 预留 256 个 byte token 作为 fallback
  • 空格替换为 (U+2581)
  • 支持添加 BOS token

WPM (WordPiece)

BERT 系列使用:

  • 类似 BPE 但按词汇表匹配,未知词用 ## 前缀标记子词
  • 归一化选项已重构为独立结构体 normalizer_options,含 lowercase 与新增的 strip_accents,用于 Jina Embeddings 等需要大小写/重音不敏感的场景
c
struct normalizer_options {
    bool lowercase     = true;
    bool strip_accents = true;
    // TODO: clean_text, handle_chinese_chars
};

关于 BOS 的实际来源:BOS token 从 GGUF 元数据(tokenizer.ggml.bos_token_id 等)读取,并不存在"采纳 leading TemplateProcessing 特殊 token 作为 BOS"这一机制。

Unigram

T5 系列使用:

  • 从大词表中逐步删减
  • 编码时使用概率最大的路径

近期修复了 UGM(Unigram)tokenizer 在 precompiled_charsmap 处理上的越界读(4a7ee3126#18750)。

HybridDNA

专为 DNA 序列设计的混合分词器:

  • 文本部分使用标准 BPE
  • DNA 序列使用固定长度 k-mer(6 碱基)分词
  • DNA 段由 <dna></dna> 标签界定
  • 非 ACGT 字符回退到 <oov> token
  • 内部通过(普通)继承并重写虚方法扩展 BPE tokenizer(llm_tokenizer_bpe_session 的子类,非 C++ virtual-base 虚拟继承)

词表结构

c
enum llama_vocab_type {
    LLAMA_VOCAB_TYPE_NONE = 0,
    LLAMA_VOCAB_TYPE_SPM  = 1,  // SentencePiece
    LLAMA_VOCAB_TYPE_BPE  = 2,  // Byte Pair Encoding
    LLAMA_VOCAB_TYPE_WPM  = 3,  // WordPiece
    LLAMA_VOCAB_TYPE_UGM  = 4,  // Unigram
    LLAMA_VOCAB_TYPE_RWKV   = 5,  // RWKV greedy
    LLAMA_VOCAB_TYPE_PLAMO2 = 6,  // PLaMo-2 (Aho-Corasick + dynamic programming)
};

// 注意:HybridDNA 是 BPE 的变体(类型仍为 LLAMA_VOCAB_TYPE_BPE),通过虚拟继承扩展

每个 token 包含:

  • 文本表示(text)
  • 分数(score)— 用于 BPE 合并优先级(GGUF 里除 F32 外现也接受 INT32 整数分数数组)
  • 类型(normal, control, unknown, byte 等)
  • 特殊标记(BOS, EOS, PAD, EOT 等)

预分词器(pre-tokenizer)

分词行为由 llama_vocab_pre_typesrc/llama-vocab.h)选择,现共 59 种:本窗口新增 LLAMA_VOCAB_PRE_TYPE_HY_V4(Tencent Hy 4,复用 DeepSeek3-LLM 的正则集)与 LLAMA_VOCAB_PRE_TYPE_SPARK2_5(llama3 风格但单独切分 [\r\n]、增加 \p{N} 第四条表达式)。

suppress_tokens(模型级屏蔽 token)

GGUF 键 tokenizer.ggml.suppress_tokens(INT32 数组)记录模型要求屏蔽的 token。它不再是 gemma4 专属的图级 logits-bias hack(原 llm_graph_input_logits_bias 已删除):词表加载时过滤出有效 id,经新公共 API llama_vocab_get_suppress_tokens() 暴露,common/sampling.cppcommon_sampler_init 里把它们并入 logit-bias 采样器(置 -INFINITY)。

分词流程

相关概念

新增 tokenizer 支持

近期上游新增了以下 tokenizer:

  • Cohere2 MoE (TINY_AYA) — 新增 cohere2moe vocab 与专用 chat template parser(North Code)
  • jina-embeddings-v2-base-zh — 中文嵌入模型,使用 whitespace 预分词
  • LFM2.5-8B-A1B — 新增 tokenizer 和 chat template(含 reasoning round-trip 修复)
  • MiniCPM5 — 转换工具新增 tokenizer 支持;并新增 MiniCPM5 工具调用解析器(PEG parser,处理 XML 风格 tool call),修复 Jinja min/max API 以对齐 Jinja2
  • MiniMax-M3 — 新增专用 chat parser(common/chat);其架构使用 MSA 稀疏注意力
  • Granite 4.1 — 新增 chat template
  • Hy 4(hy_v4)与 Spark 2.5(spark2_5)(本窗口)— 两个新预分词类型,见上文「预分词器」
  • GGUF 读取加固 — vocab 加载时校验各 KV 的数组类型(merges 必须字符串数组、scores 接受 INT32/F32、suppress_tokens 为 INT32 数组等),类型不符抛异常而非未定义行为;plamo2 的 byte-token hex 先验证再 stoi;超范围的特殊 token id 改为禁用并告警(原先会越界)

多模态分词(mtmd)新 API

本窗口 mtmd 的输入分词接口有明显扩展:

  • mtmd_tokenize_from_parts() — 新公共 API:接受 mtmd_input_part(文本或位图)数组,不再要求 prompt 里有媒体标记(<__media__> 等),可逐 part 控制 parse_specialadd_special 提升为调用级参数。原 mtmd_tokenize 重构为 mtmd_tokenizer
  • 位图合并改为显式 opt-in — 连续视频帧的时间维合并现在要求每帧调用 mtmd_bitmap_set_mergeable(true),不再自动合并(95c409c13
  • chunk 序列化mtmd_input_chunk_save/_load/_get_placeholder:server 保存会话 KV 时媒体 chunk 只存元数据,加载后为占位符
  • TTS(音频生成)子系统 — 实验性 mtmd_gen_* API(mtmd_gen_audio_type:QWEN3TTS / POCKETTTS;mtmd_gen_audio_process 等)与 clip 侧 CLIP_MODALITY_GEN_AUDIO / clip_encode();helper 拆出 mtmd-helper-gen.cpp(1000+ 行)的 step-prompt / step-gen 循环。模型图:qwen3tts-gen.cpppockettts-gen.cpp(+ spkenc / seanet)。llama-tts 二进制有破坏性变更
  • Pillow 级精确缩放 — 图像预处理的 resize 算法(BILINEAR / BICUBIC / 新增 LANCZOS)与 pad 样式(NONE / CEIL / NEAREST)声明为与 PIL 逐字节一致,overview 与 refined 图可分别配置算法;预处理器方法全部 const 化,新增 deepseek4v / minicpmv / muse-glimmer 预处理器与 dots3-note 音频预处理器
  • webp 解码(经 ffmpeg)、视频 moov-atom-at-EOF 修复、--mmproj-device 参数(mtmd_context_params 新增 device 字段)、位图 id 改用 SHA-256(原 FNV)

Chat Template 变化

llama.cpp 使用 Jinja 模板处理 chat 格式,近期更新:

  • Cohere2 MoE (North Code) — 专用 chat template parser
  • Jinja 过滤器 — 新增 count/d/e 过滤器别名;修复负步长 slice(带 start/stop)与 split/replace 空 first arg 的解析
  • Jinja call 语句 — 模板内现支持 {% call %} 调用语句,把宏(macro)回传的 caller 上下文移入函数处理,适配更复杂的 chat 模板
  • LFM2 / LFM2.5 — 修复 tool-call 双重转义、json_schema 忽略、reasoning round-trip、空白处理及 peg-native grammar 生成器 bug
  • MiniCPM5 工具调用解析器 — 新增 PEG parser 处理 MiniCPM5 的 XML 风格 tool call(autoparser + 严格 JSON 参数解析),配套 models/templates/openbmb-MiniCPM5-1B.jinja 模板
  • --reasoning-preserve — jinja/chat caps 新增保留推理(reasoning)内容的开关,控制输出是否保留 thinking 内容
  • reasoning-budget sampler — 推理预算采样支持多个 end-sequence 强制停止;common_params 新增 reasoning_budget_tokens / _start / _end(vector) / _forced / _message / reasoning_control 等字段。配套新增 common/trie.{cpp,h} 用于多序列匹配(注意:是 reasoning-budget 基础设施,不是推测解码)
  • --dump-prog测试专用(仅 tests/test-chat-template.cpp)选项,导出模板编译后的程序(AST),便于排查 chat 模板渲染问题;底层走 common/jinja/runtime.hdebug_dump_program 辅助函数,不是 llama-server 的运行时参数
  • Granite 4.1 template (models/templates/ibm-granite-granite-4.1.jinja)
  • LFM2/LFM2.5 tool parser 统一重构

语法生成 (Grammar)

约束生成(grammar-constrained generation)依赖从结构化输入(JSON schema、GBNF)生成解析器:

  • PEG ac parser — 新增 ac parser 用于更严格的语法生成;until 子句的 GBNF 语法生成重构
  • json-schema-to-grammar — 间距规则(spacing)与各 parser 实现对齐,减少生成语法的歧义
  • regex-partial 移除 — 删除未使用的 common/regex-partial.cpp/.h 及其测试(-551 行),精简公共解析代码