vLLM自定义对话模板
LLamaFactory微调效果与vllm部署效果不一致如何解决
LLaMA Factory 微调效果与 vLLM 部署效果不一致,核心原因通常是训练与推理时的“对话模板(Chat Template)”不匹配,此外还可能涉及 LoRA 权重未合并、Tokenizer 配置缺失等问题。你可以按以下思路逐一排查和解决:
1. 对齐对话模板(最常见原因)
LLaMA Factory 微调时会使用内置的特定模板(如 chatml、qwen、llama3等)格式化数据,而 vLLM 默认可能加载模型自带的模板或不使用模板,导致输入格式差异,生成结果跑偏。
-
解决方法:从 LLaMA Factory 中提取对应的 jinja 模板文件,并在启动 vLLM 时通过
--chat-template参数指定该文件。示例代码如下(在 LLaMA Factory 环境下运行):
from llamafactory.data.template import TEMPLATES
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("你的模型路径")
template = TEMPLATES["你的模板名称"] # 如 "qwen"、"llama3"
template.fix_jinja_template(tokenizer)
with open("your_template.jinja", "w", encoding="utf-8") as f:
f.write(tokenizer.chat_template)
启动 vLLM 时:vllm serve 你的模型路径 --chat-template your_template.jinja。
2. 确保 LoRA/QLoRA 权重已合并
如果你使用的是 LoRA 或 QLoRA 微调,需先将适配器权重与基座模型合并,再导出为完整的 Hugging Face 格式模型,避免 vLLM 只加载了未微调的基座模型。
-
可通过 LLaMA Factory 的
export_model.py工具或peft.merge_lora命令完成合并,合并后检查导出目录包含config.json、model.safetensors、tokenizer_config.json等完整文件。
3. 检查 Tokenizer 与配置一致性
确保部署时使用的 tokenizer_config.json、special_tokens_map.json、generation_config.json与微调时完全一致,避免特殊 token 处理差异或生成参数(如 temperature、top_p)不同导致的结果偏差。
-
若模型是自定义结构,启动 vLLM 时需加上
--trust-remote-code参数;同时确认config.json中的architectures字段是 vLLM 支持的标准值(如LlamaForCausalLM、Qwen2ForCausalLM)。
4. 推理参数对齐
测试时保证两方的采样参数一致(如 temperature=0.7、top_p=0.9、max_tokens等),排除随机性带来的效果差异后再排查模板/权重问题。
按照以上步骤对齐后,两者的推理效果通常就能保持一致。
vLLM与LMDeploy如何自定义对话模板
1.vLLM聊天模板
为了使语言模型支持聊天协议,vLLM 要求模型在其tokenizer 配置中包含聊天模板。聊天模板是一个 Jinja2 模板,用于指定角色、消息和其他特定于聊天的token 如何在输入中编码。
可以在NousResearch/Meta-Llama-3-8B-Instruct 的示例聊天模板中找到
有些模型即使经过指令/聊天微调,也不提供聊天模板。对于这些模型,您可以在--chat-template 参数中使用聊天模板的文件路径或字符串形式的模板手动指定其聊天模板。如果没有聊天模板,服务器将无法处理聊天,并且所有聊天请求都会出错。
vllm serve <model> --chat-template ./path-to-chat-template .jinja
vLLM 社区为流行的模型提供了一组聊天模板。您可以在examples 目录下找到它们。
2. LMDeploy自定义对话模板
LMDeploy 支持两种添加对话模板的形式:
一种是利用现有对话模板,直接配置一个如下的json 文件使用。
{
"model_name": "your awesome chat template name",
"system": "<|im_start|>system\n",
"meta_instruction": "You are a robot developed by LMDeploy.",
"eosys": "<|im_end|>\n",
"user": "<|im_start|>user\n",
"eoh": "<|im_end|>\n",
"assistant": "<|im_start|>assistant\n",
"eoa": "<|im_end|>",
"separator": "\n",
"capability": "chat",
"stop_words": ["<|im_end|>"]
}
model_name 为必填项,可以是LMDeploy 内置对话模板名(通过lmdeploy list 可查阅),也可以是新名字。其他字段可选填。当model_name 是内置对话模板名时,json文件中各非null字段会覆盖原有对话模板的对应属性。而当model_name 是新名字时,它会把将 BaseChatTemplate直接注册成新的对话模板。其具体定义可以参考BaseChatTemplate。
这样一个模板将会以下面的形式进行拼接。
{system}{meta_instruction}{eosys}{user}{user_content}{eoh}{assistant}
{assistant_content}{eoa}{separator}{user}...
在使用CLI 工具时,可以通过--chat-template 传入自定义对话模板,比如:
lmdeploy serve api_server internlm/internlm2_5-7b-chat --chat-template
${JSON_FILE}
也可以在通过接口函数传入,比如:
from lmdeploy import ChatTemplateConfig , serve
serve('internlm/internlm2_5-7b-chat ',
chat_template_config=ChatTemplateConfig .from_json('${JSON_FILE}'))
一种是以LMDeploy 现有对话模板,自定义一个python对话模板类,注册成功后直接用即可。优点是自定义程度高,可控性强。下面是一个注册LMDeploy 对话模板的例子:
from lmdeploy.model import MODELS, BaseChatTemplate
@MODELS.register_module(name='customized_model')
class CustomizedModel(BaseChatTemplate):
"""A customized chat template."""
def __init__(self,
system='<|im_start|>system\n',
meta_instruction='You are a robot developed by LMDeploy.',
user='<|im_start|>user\n',
assistant='<|im_start|>assistant\n',
eosys='<|im_end|>\n',
eoh='<|im_end|>\n',
eoa='<|im_end|>',
separator='\n',
stop_words=['<|im_end|>', '<|action_end|>']):
super().__init__(system=system,
meta_instruction=meta_instruction,
eosys=eosys,
user=user,
eoh=eoh,
assistant=assistant,
eoa=eoa,
separator=separator,
stop_words=stop_words)
from lmdeploy import ChatTemplateConfig, pipeline
messages = [{'role': 'user', 'content': 'who are you?'}]
pipe = pipeline('internlm/internlm2_5-7b-chat',
chat_template_config=ChatTemplateConfig('customized_model'))
for response in pipe.stream_infer(messages):
print(response.text, end='')
在这个例子中,我们注册了一个LMDeploy 的对话模板,该模板将模型设置为由LMDeploy 创造,所以当用户提问模型是谁的时候,模型就会回答由LMDeploy 所创。
参考资料:
OpenAI 兼容服务器— vLLM 文档
自定义对话模板— lmdeploy
1. 生成式语言模型的对话模板介绍
对话模板(Chat Template)本质是把多轮对话(角色+内容)按模型训练时固定的格式拼成单一文本序列,并插入对应的特殊标记(如 <|im_start|>、<|im_end|>等)。
-
模型本身只做“下一个 token 预测”,并不天然理解“哪句是用户/助手/系统”,模板就是训练/推理对齐的“协议”。
-
不同模型家族(Llama / Qwen / ChatML 等)模板不同;格式不对会直接导致生成质量下降甚至“答非所问”。
-
在 HuggingFace 里,模板通常体现为
tokenizer.chat_template(Jinja 模板),由 tokenizer 负责把messages=[{role, content}]转成模型输入。
2. LoRA 微调后单独部署大模型输出结果不一致
常见根因基本集中在“训练/推理两端没对齐”:
-
对话模板/Prompt 格式不一致:训练用了某模板(如 chatml/qwen/llama3),推理端没用同一套,模型看到的上下文结构不同。
-
LoRA 权重未合并或加载异常:部署时实际只跑了基座模型;或用 PEFT 加载但配置(rank/alpha/dropout 等)或模块绑定不一致。
-
Tokenizer / 配置不一致:
tokenizer_config.json、special_tokens_map.json、generation_config.json与训练侧不一致。 -
数值精度/采样随机性:合并权重精度(fp16/bf16)、
do_sample、temperature、top_p 等也会导致肉眼可见差异。
解决思路:优先对齐“模板 + tokenizer + 生成参数”;LoRA 建议先合并成完整权重再部署(或确保推理框架正确加载 adapter 并与基座一致)。
3. 如何导出 LLaMA Factory 的对话模板
LLaMA Factory 内部在 template.py里管理模板,可通过其模板对象把对应模板“修复/生成”为 Jinja 格式,再拿到 tokenizer.chat_template。
核心步骤(示例代码逻辑):
from llamafactory.data.template import TEMPLATES
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("你的模型路径")
template_name = "qwen" # 与训练时选择的模板名一致
template = TEMPLATES[template_name]
template.fix_jinja_template(tokenizer)
print(tokenizer.chat_template)
拿到字符串后,可保存为 xxx.jinja文件供推理框架使用。
4. vLLM 推理模型时自定义对话模板
vLLM 支持通过参数指定对话模板,保证与训练侧一致:
vllm serve 你的模型路径 --chat-template ./path-to-chat-template.jinja
其中 ./path-to-chat-template.jinja即为第 3 步导出的 Jinja 模板内容(或直接写 Jinja 字符串路径均可)。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)