JiRackPrecisionTokenizer对接Qwen2.5模型完整指南:resize_token_embeddings嵌入扩展实操
JiRackPrecisionTokenizer对接Qwen2.5模型完整指南:resize_token_embeddings嵌入扩展实操
本文带你完整走一遍 JiRackPrecisionTokenizer 对接 Qwen2.5 模型的核心流程。JiRackPrecisionTokenizer 是一个完全兼容 Qwen2.5 与 DeepSeek R1 的增强版 Byte-Level BPE 分词器,内置工具调用、机器人控制、RAG 路由等领域专用特殊词元。由于它在原词表上追加了上百个新词元,接入现成模型权重时必须调用 resize_token_embeddings 扩展嵌入层——本文就是这一关键步骤的实操指南。
一、JiRackPrecisionTokenizer 是什么?为什么词表会变长?
JiRackPrecisionTokenizer 基于 Qwen2 系列分词格式构建(分词器类型为 Qwen2Tokenizer,见 tokenizer_config.json),在原始 Qwen2.5 词表(约 151,665 个词元)基础上新增了约 114 个专用词元,最终词表大小约 151,779(见 README.md 中的 SIZE 一节)。
新增词元大致分为五类:
| 类别 | 示例词元 | 用途 |
|---|---|---|
| 工具调用 | `< | tool_call_start |
| 机器人与具身智能 | `< | action_start |
| RAG 路由标签 | __CODING__、__ROBOTICS__、__BANKING__ |
RAG MoE 系统的领域路由 |
| 多模态 | `< | image |
| FIM 代码补全 | <fim_prefix>、<fim_middle>、<fim_suffix> |
Fill-in-the-Middle 代码生成 |
这些词元完整清单可以直接在 tokenizer_config.json 的 additional_special_tokens 字段中查看。
二、为什么要调用 resize_token_embeddings?
模型的嵌入层(embedding matrix)是按训练时的词表大小"定行"的:每一行对应一个词元的向量。而模型输出端的 lm_head 同样按词表大小存着一份权重。
当你换用 JiRackPrecisionTokenizer 后,出现了新 ID(如 <|tool_call_start|>),但权重文件里并没有对应行,直接推理会触发越界报错,或者新词元被忽略。解决办法只有两个字:扩容。Hugging Face transformers 提供的 resize_token_embeddings 会同时扩宽输入嵌入和 lm_head 两块权重,新行通常以随机值或均值初始化——这就是为什么扩容后建议再微调(fine-tune)让新词元"学会"自己。
另外注意 config.json 中 tie_word_embeddings 为 false,说明输入嵌入与 lm_head 是两份独立权重,两者都需要扩容,resize_token_embeddings 会一并处理,无需手动分开操作。
三、实操前的准备:先克隆仓库、装好环境
git clone https://gitcode.com/hf_mirrors/CMSManhattan/JiRackPrecisionTokenizer
cd JiRackPrecisionTokenizer
pip install transformers torch
本仓库即分词器本身,核心文件及其作用如下:
- tokenizer.json:分词器主文件(BPE 词表与编码规则,约 11 MB)
- tokenizer_config.json:分词器配置,含全部新增特殊词元
- chat_template.jinja:Qwen 风格聊天模板,原生支持 tool calls 渲染
- config.json:模型结构配置(
Qwen2ForCausalLM,hidden_size: 5120,vocab_size: 152064) - generation_config.json:生成参数(temperature 0.6、top_p 0.95)
- check_tokenizer_reserve.py:检查脚本,判断是否需要扩容(下文第五节会用)
💡 小提示:config.json 中的
vocab_size为 152064,大于分词器词表大小 151779,意味着模型权重已预留了"空行",是否真的必须扩容由第五节的检查脚本给出最终结论。
四、resize_token_embeddings 扩展嵌入层:核心三步
把下面三步串起来,就完成了分词器与 Qwen2.5 权重的对接(这是 README.md 中标注 "The Must!" 的关键步骤):
from transformers import AutoTokenizer, AutoModelForCausalLM
# 第 1 步:加载模型与 JiRack 分词器
model = AutoModelForCausalLM.from_pretrained("your_qwen25_model")
tokenizer = AutoTokenizer.from_pretrained("./JiRackPrecisionTokenizer")
# 第 2 步:关键一步!按新词表大小扩展嵌入层
model.resize_token_embeddings(len(tokenizer))
# 第 3 步:验证新形状,应为 (151779, 5120)
print("New Embedding size:", model.get_input_embeddings().weight.shape[0])
三点说明:
len(tokenizer)即新词表大小(约 151779),它是唯一的"真相来源",不要手填数字;- 扩容后新增行是未训练的权重,此时模型对新词元的理解是"空白",需要通过继续训练让其收敛(README 中提到,路由类功能大约需要 10 万条示例数据来充分适应);
- 训练结束后记得把
model与tokenizer一起保存,否则下次加载时词表与权重会再次对不上。
五、验证结果:用检查脚本确认是否需要扩容
仓库自带了一个非常实用的核查脚本 check_tokenizer_reserve.py,它会自动定位分词器目录和 .pt 检查点,对比两者后给出明确结论:
python3 check_tokenizer_reserve.py
脚本内部逻辑(见 check_tokenizer_reserve.py 的 main 函数):
- 统计词表中 最大 token id + 1,得到嵌入矩阵需要的总行数;
- 读取检查点中的
token_emb.weight与lm_head.weight,确认两者行数一致且都 ≥ 所需行数; - 同时核对 4 个工具调用词元是否都存在于词典中,防止用错旧版分词器。
最终会输出两种结论之一:
- RESIZE 不需要:所有新词元都落在权重预留行内,
VOCAB_SIZE保持原值即可; - RESIZE 必须执行:缺少若干行,必须按第四节调用
resize_token_embeddings扩宽token_emb.weight和lm_head.weight。
六、常见坑与注意事项
- 🚨 未扩容直接推理:新词元 ID 越界,报
IndexError或输出异常——这是最高频的错误,务必先跑resize_token_embeddings。 - 🚨 扩容后不继续训练:新行权重是随机初始化的,模型可能输出不合理的工具调用格式,需配合工具调用语料微调。
- 🚨 模型与分词器不同步保存:只保存模型不保存分词器(或反之),下次加载两边词表对不上,问题重现。
- ⚠️ pad / eos 词元:本分词器沿用 Qwen 约定,
pad_token_id与eos_token_id均为 151643(<|end▁of▁sentence|>),批量推理时保持配置一致即可。 - ⚠️ 许可证合规:本项目基于 Apache 2.0(Qwen 2.5)与 MIT(DeepSeek R1)生态构建,详见 NOTICE.md 及 README.md 的 License 一节。
七、小结
对接 JiRackPrecisionTokenizer 到 Qwen2.5 的完整链路可以浓缩为一句话:克隆仓库 → 加载模型与分词器 → resize_token_embeddings(len(tokenizer)) 扩容 → 用 check_tokenizer_reserve.py 验证 → 保存并微调。完成之后,你就获得了一个原生支持工具调用、机器人控制语义与 RAG 领域路由的 Qwen2.5 分词体系,为后续的领域微调打下基础。