SFT 中为什么要统一 Chat Template?Loss Mask 应该怎么做?
简化版
Chat Template 会把 system、user、assistant 等消息转换成模型真正看到的控制 token 序列,训练和推理格式不一致会明显损害效果。对指令微调,常见做法是只在 assistant 目标 token 上计算损失,把提示部分标签设为忽略值;但是否屏蔽全部非回答 token 要结合模型和训练目标决定。
详细版
训练前应确认:
- 使用与基座或目标部署一致的 Tokenizer 和 Chat Template;
- 角色起止符、轮次结束符、BOS/EOS 不重复也不遗漏;
- 训练完整对话时通常不添加“开始生成回答”的尾部提示;
- 推理时按模板要求添加 assistant 起始提示;
- Label 与 token 精确对齐,padding 和无需训练的 token 使用忽略标签;
- 多轮数据明确哪些 assistant 回合参与损失;
- 截断不能只剩回答或切坏角色边界;
- 用解码后的样本人工检查模板结果。
只对回答计算损失能避免模型把用户问题本身当成需要预测的目标,并把学习重点放在回复上;全序列训练也有适用场景,不能不经验证照搬固定配置。
完整版教学
记忆钩子:Template 规定谁在说话,Loss Mask 规定模型为谁的哪些 token 负责。
一、模型看到的不是消息字典
应用层的消息通常写成 role 和 content,但因果语言模型接收的是一串 token。Chat Template 负责插入角色标记、分隔符和结束符,例如标明用户内容在哪里结束、助手回答从哪里开始。
即使两个聊天模型来自同一基座,它们也可能使用不同控制 token。模板用错时,文字内容没变,模型理解的对话结构却变了。
二、训练与推理必须对齐
如果训练使用一种角色格式,推理换成另一种,模型会遇到分布偏移。训练预处理应调用目标模板,推理服务也应复用同一模板和 Tokenizer 版本。
训练样本已经包含完整回答,一般不需要额外追加等待模型生成的 assistant 起始提示;推理请求则通常需要告诉模型下一段应由 assistant 继续,具体参数以模板定义为准。
三、Loss Mask 的原理
交叉熵损失按 token 计算。许多训练框架用特殊标签值表示“该位置不计入损失”,常见实现是把这些 label 设为 -100。
对 prompt-completion 数据,可屏蔽 prompt,只监督 completion;对多轮对话,可只监督 assistant 消息。模型仍能在前向过程中读取被屏蔽的上下文,只是这些位置不直接贡献训练损失。
四、为什么不能按字符位置随便切
字符经过 Tokenizer 后可能拆成多个 token,控制符也可能是特殊 token。先拼字符串再按字符长度估算 Mask,容易出现偏移,把回答开头屏蔽或把用户文本纳入损失。
更可靠的是让模板返回 assistant mask,或在 token 化时保存每段的精确边界,并用单元测试验证。
五、多轮对话如何处理
可以监督所有 assistant 回合,也可以只监督最后一轮,取决于任务。监督所有回答能利用更多信号,但历史回答若是模型生成或质量较差,也会污染目标。
截断长对话时要保证至少保留有效目标 token。若一个 batch 中所有标签都被屏蔽,损失可能无效甚至出现异常。
六、特殊 token 的常见错误
- 模板已添加 BOS/EOS,外部 Tokenizer 又重复添加;
- 把 padding token 当作训练目标;
- 训练与推理使用不同 EOS;
- 拼接样本时没有正确分隔;
- 新增特殊 token 后忘记调整词嵌入矩阵;
- Mask 把角色结束符全部误删,导致模型学不会停止。
七、如何验收
随机抽取格式化样本,打印 token、解码文本和参与损失的位置,确认角色顺序、特殊 token、目标区间与截断结果。再用一个很小的数据子集做过拟合测试:若几十条样本仍学不会目标格式,优先怀疑模板、标签右移或 Mask,而不是扩大数据。最后把同一消息对象送入训练预处理和线上模板,逐 token 比较结果,防止两套实现悄悄漂移。
八、逐 token 检查一次 Mask
设序列是 <user> 你好 <assistant> 您好 <eos>,token 数分别为 1、2、1、2、1,共 7 个。若目标是只学习助手回答,则 user 角色、用户内容和 assistant 起始标记都应设为 -100,只对“您好”和结束标记计算交叉熵;标签右移后还要再次核对边界。
token: <user> 你 好 <assistant> 您 好 <eos>
loss: × × × × ✓ ✓ ✓
label: -100 -100 -100 -100 您 好 <eos>
| 区域 | 是否计入 Loss | 原因 |
|---|---|---|
| System/User | 通常否 | 作为条件输入 |
| Assistant 内容 | 是 | 目标输出 |
| EOS | 通常是 | 学习停止 |
最可靠的验收不是只看总 loss,而是随机反解若干批次,打印 token、role、label 和 mask 对齐关系。训练与推理必须使用同一 Chat Template;否则模型学到的角色边界、结束符和线上输入协议不一致,即使 loss 正常也会出现续写用户、停不下来等问题。
九、常见误区与追问
- 误区:Chat Template 只影响展示格式。 它决定角色 token、分隔符和结束符,是模型实际看到的训练协议。
- 追问:为什么通常不对用户 token 计算 loss? 目标是学习助手响应;监督用户内容会浪费容量并诱发模型续写用户角色。
- 误区:按字符串长度构造 Mask 足够可靠。 tokenizer 会合并或拆分字符,必须在 token 化后的 role span 上生成并核验 mask。
- 追问:EOS 是否应该计入 loss? 通常应让模型学习何时结束,但要与推理停止条件及模板设计一致。
- 追问:怎样发现模板不一致? 固定样本对训练与线上输入做 token-id diff,并反解 token、role 和 label 逐位检查。
十、加强记忆
Chat Template 决定模型眼中的对话语法与角色边界,Loss Mask 决定哪些 token 产生训练梯度。训练和推理模板必须逐 token 一致,Mask 要在 token 化和标签右移后核验,不能按字符串位置猜。把“协议一致、目标准确、EOS 可学、截断不越界”四项记牢,就能覆盖最常见的静默错误。