ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

PaddleNLP Chat Template 完全指南:多轮对话构造、自定义模板与微调实战

PaddleNLP Chat Template 完全指南:多轮对话构造、自定义模板与微调实战 PaddleNLP Chat Template 完全指南多轮对话构造、自定义模板与微调实战【免费下载链接】PaddleNLPEasy-to-use and powerful LLM and SLM library with awesome model zoo.项目地址: https://gitcode.com/gh_mirrors/pa/PaddleNLP导读PaddleNLP 已集成 Llama、Qwen、ChatGLM 等主流开源对话大模型而不同模型的 Prompt 构造规则各不相同。为此 PaddleNLP 提供了一套统一的对话模板Chat Template机制通过apply_chat_template一行 API 即可按各模型自身规则拼接系统提示system、对话历史与用户最新 query同时支持通过chat_template.json自定义模板并贯通推理与多轮对话微调训练。读完本文你将掌握对话模板的构造原理、配置字段语义、自定义加载方式以及如何在run_finetune.py中用它完成多轮对话 SFT 精调与动态 system prompt 定制。一、为什么需要对话模板统一多模型、多场景的 Prompt 规则当前开源 Chat 类模型越来越多PaddleNLP 已经集成了 Llama、Qwen、ChatGLM 等系列模型。每一类模型的对话式 Prompt 拼装规则都不一致——有的用特殊 token如 Qwen 的|im_start|/|im_end|有的用轮次编号如[Round 1]有的需要把 system 提示放在最前。如果每次使用都在业务代码里手写拼接逻辑代码会变得难以维护且极易出错。对话模板Chat Template就是为了解决这个问题而设计的统一抽象推理侧调用apply_chat_template即可把系统提示 对话历史 用户最新 query按模型指定规则拼接成一个完整 prompt实现不同模型的定制化 Prompt 规则推理训练侧在多轮对话精调中模板同时承担哪些 token 参与 loss 计算的职责——用户提问部分不参与、模型回答部分参与从而标准化不同模型在前处理上的差异。二、快速上手一行代码构造多轮对话PaddleNLP 支持主流 LLM 对话模型并会自动为它们构造多轮对话。基础用法只需两步加载 tokenizer然后调用apply_chat_template。单轮对话from paddlenlp.transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(THUDM/chatglm-6b-v1.1) # Single-round conversation query Whats fun to do in Beijing inputs tokenizer.apply_chat_template(query, return_tensorspd)多轮对话多轮对话的输入格式为嵌套列表前若干轮是[user, bot]对对话历史最后一轮只需要用户输入最新 query# Multi-round conversation query [[11, 112], [Add one more]] inputs tokenizer.apply_chat_template(query, return_tensorspd)只拼文本 vs 编码成模型输入apply_chat_template的tokenize参数控制返回内容from paddlenlp.transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(THUDM/chatglm-6b-v1.1) # tokenizeFalse只返回拼接后的完整文本 query What are some fun things to do in Beijing full_query tokenizer.apply_chat_template(query, tokenizeFalse) # tokenizeTrue默认并指定 return_tensorspd编码为模型可直接使用的 Tensor inputs tokenizer.apply_chat_template(query, tokenizeTrue, return_tensorspd)从源码看apply_chat_template的完整签名定义在 paddlenlp/transformers/tokenizer_utils.py核心逻辑为若 tokenizer 尚未设置chat_template直接抛出ValueError提示先设置模板按模板类型执行渲染PaddleNLP 原生ChatTemplate或兼容的 jinja2Template当tokenizeFalse时直接返回渲染后的字符串当tokenizeTrue时会强制把add_special_tokens置为False后再调用 tokenizer 编码——这是因为模板文本中已经包含了 bos/eos 及|im_start|这类自定义标记不应再由 tokenizer 额外添加 special token详见 tokenizer_utils.py。三、模板构造逻辑final_query system conversation_history query在自定义模板之前必须先理解对话模板的统一构造逻辑final_query system conversation_history query三个组成部分各司其职组成部分含义说明system最终 prompt 开头的固定文本例如 You are an AI assistant with a witty sense of humor, typically preferring to communicate in a literary style.在训练中通常不参与 loss 计算conversation_history多轮对话历史将多轮对话构造成一段文本不同模型可能有不同的构造规则轮次标记、特殊 token 等query用户最新输入用户的最后一轮请求通常只在推理阶段使用对应到 PaddleNLP 源码ChatTemplate.__call__的渲染流程tokenizer_utils.py正是按此逻辑实现的先渲染 systemrender_system对除最后一轮外的所有历史对话逐轮调用render_conversation拼接进final_query最后一轮必须是单条用户输入调用render_query追加到末尾若最后一轮长度大于 1会警告并跳过多余内容。四、自定义对话模板chat_template.json 三字段详解创建自定义对话模板非常简单——只需创建一个默认名为chat_template.json的配置文件{ system: You are an AI assistant with a witty sense of humor, typically preferring to communicate in a literary style., conversation: [[Round {{index}}]\nQuestion: {{user}}\n, Answer: {{bot}}\n], query: [Round {{index}}]\nQuestion: {{query}}\nAnswer: }字段语义配置文件主要包含三个字段system、conversation、query。system拼接到最终 prompt 开头的固定文本。训练阶段通常不参与 loss 计算。conversation多轮对话配置必须包含两个模板元素数组长度为 2依次对应 [user-template, bot-template]即用户 query 和模型响应的构造规则。该字段在训练与推理两个阶段都会用到。query用户最新 query 的构造规则配置形式与conversation类似但通常只在推理阶段使用。模板中的变量占位符配置值支持 jinja2 模板语法常用占位符有{{user}}当前轮的用户输入{{bot}}当前轮的模型回答{{query}}最后一轮用户的最新输入{{index}}当前对话轮次索引从 0 开始可用于生成[Round 1]之类的轮次标记。在源码中这些变量由ChatTemplate.render_conversationtokenizer_utils.py注入它会将一轮对话构造成{user: ..., bot: ..., index: ...}字典再通过沙箱化的 jinja2 环境编译渲染render_querytokenizer_utils.py则注入query与index变量。真实模型模板示例Qwen Chat以qwen-14b-chat的模板为例该配置参考了 Qwen 官方生成工具的实现可见系统提示、用户与助手分隔符完全由模板控制{ system: You are a helpful assistant., conversation: [\n|im_start|user\n{{user}}|im_end|\n|im_start|assistant\n, {{bot}}|im_end|], query: \n|im_start|user\n{{query}}|im_end|\n|im_start|assistant\n }五、加载自定义模板的两种方式创建好chat_template.json后有两种加载方式方式一自动加载放入模型权重目录将chat_template.json文件放到模型权重目录下然后正常使用Tokenizer.from_pretrained(/path/)加载tokenizer 会自动读取该文件tokenizer AutoTokenizer.from_pretrained(/path/to/model_dir) # 目录内含 chat_template.json源码中的加载逻辑位于ChatTemplateMixin.from_pretrainedtokenizer_utils.py它会寻找模型目录下名为chat_template.json的文件常量CHAT_TEMPLATE_CONFIG_NAME定义于 paddlenlp/utils/env.py存在则自动调用init_chat_template完成加载。若 tokenizer 配置文件中已存在模板加载文件时还会给出告警并提示未来chat_template.json将废弃、建议迁移到tokenizer_config.json中配置。方式二手动加载init_chat_template先初始化 tokenizer再通过tokenizer.init_chat_template(/path/to/file)手动加载tokenizer AutoTokenizer.from_pretrained(THUDM/chatglm-6b-v1.1) tokenizer.init_chat_template(/path/to/chat_template.json)init_chat_templatetokenizer_utils.py是一个灵活的统一入口支持多种入参文件路径字符串存在则通过ChatTemplate.from_file从 JSON 文件加载不存在则尝试按 jinja2 模板字符串编译若两者都失败则抛出TemplateSyntaxError字典通过ChatTemplate.from_dict直接传入配置字典ChatTemplate 实例直接赋值。此外在保存 tokenizer 时调用save_resourcestokenizer_utils.py会把当前ChatTemplate序列化写回模型目录下的chat_template.json方便导出与复用。六、在训练中使用 Chat Template多轮对话精调实战对话模板不只是推理工具它在多轮对话精调SFT中同样承担核心作用。PaddleNLP 为训练侧设计了专门的数据管线本节以qwen-7b-chat为例说明完整流程。1. 数据格式src/tgt 成对列表使用 chat_template 进行训练时训练数据需要保证如下格式——src与tgt为长度相同的列表分别存放各轮的用户输入与模型回答{src: [user-1, user-2, ..., user-n], tgt: [bot-1, bot-2, ..., bot-n]} ...训练管线会按第 i 轮的src[i]tgt[i]逐轮配对构造对话src/tgt长度不一致会直接断言报错对应实现见 llm/utils/data.py。2. 通过 --chat_template 参数启用将构造好的chat_template.json传入run_finetune.py即可启用模板训练入口初始化见 llm/run_finetune.pypython run_finetune.py ... --model_name_or_path qwen/qwen-7b-chat --chat_template qwen/qwen-7b-chat--chat_template参数有三种取值行为底层由init_chat_template统一处理见 paddlenlp/trl/llm_utils.py与model_name_or_path一致默认使用模型自带的chat_template.json文件该文件随权重一并下载可通过权重目录下是否存在该文件判断模型是否支持 chat-template为文件路径使用该文件中的配置例如--chat_template ./qwen_14b_chat_template.json为空 / 显式传none不使用 chat-template 配置进行训练传none时会主动清空 tokenizer 上的模板。该参数同样适用于run_embedding.py、run_quantization.py、run_finetune_auto.py自动并行版以及llm/alignment/rl/run_rl.py等入口见 llm/run_embedding.py、llm/run_quantization.py、llm/auto_parallel/run_finetune_auto.py推理侧对应参数定义在 llm/predict/predictor.py。3. 训练侧的关键约束结合 llm/utils/data.py 的tokenize_rounds_example实现训练侧有几个必须注意的约束不加 special token训练与推理中模板文本已含 bos/eos 及|im_start|等自定义标记因此基于 chat_template 的分词始终不添加 special tokenadd_special_tokens恒为Falseconversation必须为两个元素分别对应 User 与 Bot前者在训练中不参与 loss 计算labels 置为-100后者参与 loss 计算截断策略system 文本长度必须小于max_length。当对话轮次只有一轮时按 token 长度截断伪代码为(system_tokens conversation_tokens)[:max_length]多轮时则按对话轮次从后往前截断——从最后一轮向前逐轮累加 token 长度一旦累计超过max_length则丢弃该轮及更早的历史保证至少保留最近一轮完整对话system 不可截断system 文本始终保留在序列最前且不参与 loss 计算。开启 chat_template 后训练评估时eval_with_do_generation会被强制关闭见 llm/run_finetune.py。七、动态 system prompt用 jinja2 变量定制每条数据system字段不仅支持固定文本还支持通过 jinja2 变量占位符在训练/推理时动态调整。步骤如下1. 在模板中引入变量占位符将chat_template.json中的system改为带变量的形式并保留默认值default过滤器例如{ - system: You are a helpful assistant., system: {{system | You are a helpful assistant.}}, conversation: [\n|im_start|user\n{{user}}|im_end|\n|im_start|assistant\n, {{bot}}|im_end|], query: \n|im_start|user\n{{query}}|im_end|\n|im_start|assistant\n, }注意需要开发者手动修改chat_template.json才能实现动态 system prompt。2. 在训练数据中通过 context 字段传入训练文本数据需要配置context字段将system的值传进去。渲染模板时context会被作为 jinja2 的上下文数据使用从而为每条训练数据定制专属 system prompt{src: [user-1, user-2, ..., user-n], tgt: [bot-1, bot-2, ..., bot-n], context: {system: 你是一个擅长做任务的人工智能助手}} ...源码中tokenize_rounds_example会先取出example.get(context, {})并注入is_trainingTrue标记随后作为context_data传给tokenizer.encode_chat_inputs见 llm/utils/data.py底层render_system/render_conversation都会将这份上下文合并进渲染变量见 tokenizer_utils.py。同理推理侧调用apply_chat_template时也可通过context_data参数动态传入 system 内容。八、底层实现原理从模板到训练数据1. ChatTemplate配置的数据结构ChatTemplate类tokenizer_utils.py是chat_template.json的对象映射定义了三个核心属性system: str | Noneconversation: list[str] | None两个元素user 模板与 bot 模板query: str。它通过from_dict/from_file从配置或文件构建核心渲染方法包括render_system、render_conversation逐轮渲染 user/bot 两条模板、render_query渲染最后一轮以及__call__编排完整渲染流程。所有模板片段都经由_compile_jinja_templatetokenizer_utils.py在沙箱化 jinja2 环境中编译并内置了tojson、regex_findall等辅助过滤器保证模板执行的隔离与安全。2. ChatTemplateMixin挂在所有 Tokenizer 上ChatTemplateMixintokenizer_utils.py被混入PretrainedTokenizer基类见 tokenizer_utils.py因此所有 PaddleNLP 预训练 tokenizer 天然具备对话模板能力。它提供的方法链apply_chat_template推理/通用场景输出拼接文本或编码结果encode_chat_inputs训练场景将对话逐轮编码为{system: [...], conversations: [[user_ids, bot_ids], ...]}的结构化输出其中 system 单独编码、每轮 user/bot 分别编码见 tokenizer_utils.pyinit_chat_template/from_pretrained自动加载负责模板的初始化与持久化。3. 训练侧完整数据流多轮对话精调的数据处理链路为chat_template.json │ init_chat_templaterun_finetune.py → paddlenlp.trl.llm_utils ▼ tokenizer.chat_template 就绪 │ convert_example_common → tokenize_rounds_examplellm/utils/data.py ▼ encode_chat_inputs 逐轮编码 → system_ids conversations_ids │ 从后往前按轮截断User 部分 labels-100、Bot 部分参与 loss ▼ input_ids / labels / position_ids → 训练这一链路同时服务于run_finetune.py、自动并行版run_finetune_auto.py与量化训练入口是 PaddleNLP 多轮对话精调的标准前处理路径。九、总结与最佳实践能用现成的就不手写主流模型权重自带chat_template.json加载 tokenizer 后直接调用apply_chat_template即可无需关心各模型的 Prompt 细节自定义模板三字段system固定前缀、不参与 loss、conversation双元素 user/bot 模板、训练推理通用、query最新输入、主要用于推理配合{{user}}、{{bot}}、{{query}}、{{index}}等 jinja2 变量使用训练侧约束conversation必须恰好两个模板分词时add_special_tokens恒为Falsesystem 长度必须小于max_length且不可被截断多轮超长时从后往前按轮截断动态 system在模板中用{{system | 默认值}}占位训练数据通过context.system传入推理通过context_data传入判断模型是否支持查看权重目录下是否下载了chat_template.json文件。若需深入了解可继续阅读仓库中的 英文对话模板文档、多轮对话精调教程、模板实现 tokenizer_utils.py 与训练数据管线 llm/utils/data.py。【免费下载链接】PaddleNLPEasy-to-use and powerful LLM and SLM library with awesome model zoo.项目地址: https://gitcode.com/gh_mirrors/pa/PaddleNLP创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表