微调数据格式与 Chat Template

微调失败的一半案例来自数据格式与推理格式不一致——训练用一套 prompt,上线用另一套,或 loss 算在了 system/user 前缀上。本篇梳理 TRL SFTTrainer 支持的三种数据形态、Chat Template 的作用,以及保证训练—推理一致性的检查清单。工具细节见 SFTTrainer §三 数据格式

段末注释:Chat Template 是 tokenizer 内置的 Jinja 模板,将 messages 列表渲染为模型预训练时使用的特殊 token 串;不同基座(Llama、Qwen、Gemma)模板不可混用。

系列索引:微调技术路线导读


一、三种 SFT 数据格式

格式 字段 典型场景 SFTTrainer 行为
标准 LM text 续写、CPT 全序列算 loss
对话 messages 多轮指令微调 apply_chat_template + 可选 assistant-only loss
Prompt-Completion prompt + completion 单轮 QA、分类 只对 completion 算 loss(推荐)

1.1 标准 LM(text

1
{"text": "酶催化反应中,活性位点通常包含保守的催化三联体..."}

适用于继续预训练(CPT)或纯续写;全 token 参与 loss。指令微调一般不优先此格式。

1.2 对话(messages

1
2
3
4
5
6
7
{
"messages": [
{"role": "system", "content": "你是情绪分析助手,只输出标签。"},
{"role": "user", "content": "今天天气真好!"},
{"role": "assistant", "content": "joy"}
]
}

多轮对话、工具调用轨迹。需开启 assistant-onlycompletion-only loss,避免模型学习复述 user 内容。

1.3 Prompt-Completion(推荐用于单轮任务)

1
2
3
4
5
6
7
8
9
{
"prompt": [
{"role": "system", "content": "你是情绪分析助手,只输出六类标签之一。"},
{"role": "user", "content": "今天天气真好!"}
],
"completion": [
{"role": "assistant", "content": "joy"}
]
}

语义清晰:prompt = 条件,completion = 监督目标。与 AMD 实战 Step 6 一致。


二、Chat Template 是什么

每个 instruct 模型的 tokenizer 带有 chat_template(Jinja2 字符串),定义 role、特殊 token、换行如何拼接。

1
2
3
4
5
6
7
8
9
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
messages = [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!"},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
print(text)
参数 训练 推理 generate
add_generation_prompt False(含 assistant 内容) True(末尾留 assistant 开头,等待生成)
tokenize SFTTrainer 内部处理 手动 return_tensors="pt"

铁律:训练与推理必须共用同一 tokenizer、同一 template、同一 system prompt 文案


三、Loss 掩码:只对 completion 回传梯度

指令微调若对 prompt 也算 loss,模型会浪费容量「背」user 输入,且与推理时「给定 prefix、生成 suffix」不一致。

SFTConfig 选项 作用
completion_only_loss=True prompt-completion 格式:只训 completion
assistant_only_loss=True messages 格式:只训 assistant 轮

SFTTrainer §五 loss 掩码


四、构造数据:从原始表到 HF Dataset

(text, label) 分类为例(AMD 实战模式):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SYSTEM = "你是情绪分析助手。只输出: sadness, joy, love, anger, fear, surprise 之一。"

def to_sft(example):
return {
"prompt": [
{"role": "system", "content": SYSTEM},
{"role": "user", "content": example["text"]},
],
"completion": [
{"role": "assistant", "content": example["label"]},
],
}

train_ds = raw_ds.map(to_sft, remove_columns=raw_ds.column_names)

评估脚本必须使用相同 SYSTEMapply_chat_template(..., add_generation_prompt=True)


五、训练—推理一致性检查清单

# 检查项 常见错误
1 system prompt 字符串完全一致 训练多一句「只输出标签」
2 tokenizer 与基座匹配 用错 chat 版 / base 版
3 add_generation_prompt 推理为 True 漏加导致生成从错误位置开始
4 padding 方向 decoder-only 生成用 left padding
5 max_length 截断 截断删 system 或 user 尾部
6 特殊 token bos/eos 与模板重复添加
7 评估 parse 规则 strip、lower 与训练标签不一致 → invalid 率虚高

Invalid 率指标见 04-评估指标-12


六、多轮与工具数据(扩展)

场景 格式要点
多轮对话 messages 保留完整历史;assistant_only_loss
Function calling assistant 含 tool_calls;需与基座预训练格式对齐
偏好对齐(DPO) prompt + chosen / rejected 各为 messages 列表

DPO 格式见 偏好对齐选型 §3.2


七、数据量与划分

建议 说明
train / val / test 至少 hold-out test 用于微调前后对比
验证集规模 数百至数千条,视任务而定
shuffle + seed 可复现(见 AMD 实战 Step 3)
勿泄漏 test 实体/ prompt 勿出现在 train

八、常见踩坑

现象 原因 对策
微调后格式乱 template 不一致 打印 train/infer 各一条 tokenized 对比
loss 很低、F1 很低 评估未用 generate 或 prompt 不同 统一 generate() 流程
全 padding loss 未开 completion_only_loss SFTConfig(completion_only_loss=True)
标签带多余空格/标点 数据未 normalize 统一 strip;评估同规则
换模型后全崩 沿用旧 model 的 system 习惯 按新模型 chat 规范重写

九、小结

数据格式选型:单轮任务用 prompt-completion + completion_only_loss;多轮用 messages + assistant_only_loss。Chat Template 是训练与推理的「隐形契约」——改一个字都可能导致指标断崖。

下一步:SFTTrainer 详解04 评估指标系列05-01 偏好对齐选型

-------------本文结束感谢您的阅读-------------