AiSSN.com ©

在线Ai关键词排名GEO优化工具,让你的信息出现在Ai的回答中

多轮对话SFT怎么避免灾难性遗忘:对话打包、截断策略与system指令处理
原始问题:

本文属于《Ai大模型训练教程》系列,详细讲解多轮对话SFT如何避免灾难性遗忘,给出对话打包(packing)防跨对话污染的方法、system优先的截断策略,以及system指令归一化与权重处理的实操流程与排障清单。

背景:为什么多轮对话 SFT 更容易出现灾难性遗忘

在「Ai大模型训练教程」系列里,很多同学做完基础 SFT(单轮指令-回答)后,会很快转到多轮对话 SFT(Supervised Fine-Tuning)。这一步常见的坑是:模型在新数据上对话能力提升了,但旧能力(通用问答、格式遵循、工具调用习惯、系统安全边界等)明显退化,这就是训练中的灾难性遗忘

多轮对话更容易触发遗忘,核心原因通常不是“多轮本身”,而是:

  1. 样本组织方式改变了:由“单轮问答”变为“长上下文序列”,损失函数的分布、有效 token 的比例、梯度方向都变了。
  2. 截断导致关键信息缺失:system 指令被截掉、对话开头被截掉,模型学到的是“没有约束的回答”。
  3. 对话打包(packing)不当:把多个对话硬拼成一个长序列,产生“跨对话污染”,模型学到了错误的上下文关联。
  4. system 指令处理混乱:不同数据源对 system 的写法、位置、权重不一致,导致模型对 system 的遵循能力波动。

本文聚焦三件最影响遗忘与稳定性的实操点:对话打包、截断策略、system 指令处理,并给出可落地的步骤与示例。


一、先定义你要“保住”的能力:用可观测指标约束遗忘

避免遗忘不是玄学,第一步是把“不能退化的能力”变成可测的指标,否则你只会在主观感受里反复摇摆。

建议至少建立三类小型评测集(几十到几百条即可):

1)通用能力回归集(General Regression)

  • 覆盖:常识问答、总结改写、代码片段解释、数学推理(按你原模型常用场景)
  • 目标:新模型相对 base 模型不下降或下降可控

2)指令遵循与格式集(Instruction/Format)

  • 覆盖:严格 JSON 输出、表格、要点列表、拒答/安全边界
  • 目标:system/开发者规范不被“聊天数据”冲掉

3)多轮一致性集(Multi-turn Consistency)

  • 覆盖:上下文指代、约束记忆(用户改名/设定角色)、工具调用触发条件
  • 目标:多轮收益真实存在

有了回归集,你才能验证下面每一种打包/截断/system 处理策略是否在“保住旧能力”的同时提升多轮能力。


二、多轮对话打包(Packing):既要提吞吐,也要防跨对话污染

对话数据通常长度差异很大,直接按样本 padding 会浪费大量 token。Packing 的目的,是把多个短样本拼到同一个序列里提高 GPU 利用率。但多轮对话 Packing 若处理不好,会产生严重的跨对话污染,表现为:

  • 模型把上一个对话的设定带到下一个对话里
  • 角色/口吻随机漂移
  • 在不相关的问题里“延续上一段的结论”

2.1 Packing 的基本原则:边界清晰、损失隔离

你需要同时做到两件事:

  1. 在输入中显式分隔不同对话(让模型知道“新对话开始了”)
  2. 在损失上隔离不同对话(避免上一对话末尾对下一对话首部产生不合理梯度)

一个实用做法:在每个对话开头插入一个特殊分隔,例如:

  • 纯文本:<|conversation_start|>
  • 或沿用 chat 模板 token(不同模型不同)

更关键的是 label mask

  • 对 system 与 user token 通常设为 -100(不计入 loss)
  • 仅对 assistant 的回复 token 计算 loss
  • 对分隔符 token 也设为 -100

这样即便你把两个对话打包在同一序列里,loss 也只在各自 assistant 段落上回传。

2.2 两种常用 packing 策略对比

策略 A:按“对话为单位”打包(推荐优先)

  • 一个对话视为不可拆分的整体
  • 尽量把多个“完整对话”拼到一个 sequence

优点:结构天然完整,system/上下文不会被拆开;对灾难性遗忘更友好。
缺点:对话过长时会造成较多浪费(因为长对话无法与其它对话共同填满)。

适用:你在做对话式助手,且非常在意 system 遵循与多轮一致性。

策略 B:按“轮次/消息”为单位打包(谨慎)

  • 把一个长对话拆成多个片段(chunk),每个片段包含最近 N 轮

优点:更高吞吐,能让长对话覆盖更多训练步。
缺点:很容易把 system/关键信息截断掉;也容易让模型学会“无 system 时也要继续生成”。

适用:长对话占比很高、显存紧张且你有完善的截断与 system 重注入策略(见下文)。

2.3 实操建议:packing 的“安全阈值”

如果你是第一次把单轮 SFT 升级到多轮,建议:

  • 先采用“对话为单位打包”
  • 最大长度(max_seq_len)先不要追太激进(例如 2048/4096),先把流程跑稳定
  • 确保每个对话开头都有 system,并且 system 不被截断(见第三部分)

当回归集指标稳定后,再逐步引入更激进的 packing(比如更高长度、更细粒度 chunk)。


三、截断策略:决定了模型到底学到“哪段上下文”

多轮对话截断(truncation)是灾难性遗忘的高发区,因为它会改变训练分布:

  • 你以为你在训练“遵循 system 的对话”
  • 实际上大部分样本的 system 被截掉了
  • 模型学会了“system 约束可有可无”

3.1 先理解三种截断位置的后果

假设序列超长,需要截断:

1)截断对话开头(左截断)

  • 常见于“只保留最近 K tokens”
  • 风险最大:system 与早期约束最容易丢
  • 结果:模型更像“无约束闲聊”,system 遵循能力下降

2)截断对话中间

  • 结构破坏:user/assistant 可能断在半句
  • 结果:训练噪声增大,模型学到不完整模式

3)截断对话末尾(右截断)

  • 对多轮学习不利:最后的 assistant 回复可能被切碎
  • 结果:loss 主要来自不完整回复,训练不稳定

因此,多轮对话 SFT 的截断目标通常是:

  • 尽量保住 system
  • 尽量保住最后几轮完整的 user→assistant 对
  • 避免在 assistant 回复中间硬切

3.2 推荐的“system 优先 + 尾部保留”策略(实操版)

给一个可执行的策略,你可以直接照着实现:

步骤 1:将对话拆成结构化消息

每条消息包含:role(system/user/assistant)与 content

步骤 2:固定保留 system(必要时重注入)

  • 如果原始对话没有 system:补一个默认 system(见第四部分)
  • 如果有 system:提取出 system 作为必须保留段

步骤 3:从尾部向前累加轮次,直到接近 max_len

  • 以“轮次”为单位加入(user+assistant 作为一轮),不要只加单条消息
  • 每次加入前先估计 token 长度(可用 tokenizer 预编码)

步骤 4:保证最后一个 assistant 回复尽量完整

  • 如果最后一轮的 assistant 太长:宁可缩短更早的上下文,也优先保住最后一轮完整
  • 若必须截断 assistant:建议在句子边界/段落边界截断(简单做法:按标点或换行做近似切分)

步骤 5:对 user/system 做 label mask

仅对 assistant 内容计算 loss,其他全部 -100。

这种策略的直觉是:

  • system 作为“全局规则”必须稳定出现
  • 最近的对话轮次对当前回答最相关
  • 训练时让模型对“回答部分”负责,减少对 prompt 的过拟合与噪声

3.3 典型坏例子与修复

坏例子:直接 token 级左截断

  • 结果:system 被切掉
  • 模型逐渐不理会 system

修复:

  • system 单独保留,不参与左截断
  • 左截断只作用于“历史轮次块”

坏例子:把超长 assistant 回复切成两段分别作为两条样本

  • 结果:第二段没有对应的 user,模型学到“无提问也要续写”

修复:

  • 若要 chunk:每段都必须带上同一个 user 问题与必要上下文,并明确标识 continuation(不推荐新手这么做)
  • 更简单:降低 max_new_tokens 的期望,清洗掉超长输出样本

四、system 指令处理:统一模板、稳定位置、控制权重

system 指令在多轮对话 SFT 里承担“行为规范”的角色。处理不好会直接导致:

  • 安全/合规能力退化
  • 输出格式不稳定
  • 工具调用/函数调用约束失效

4.1 统一 system 模板:避免多来源数据互相打架

真实数据通常来自多个渠道:客服对话、标注对话、公开指令集、自建角色扮演集等。每个渠道的 system 写法可能不同。

建议做一次“system 归一化(normalization)”:

推荐结构

  • 全局系统规则(Global System):对所有样本一致
  • 任务系统规则(Task System,可选):对特定数据源/任务一致
  • 样本特定补充(Sample System,可选):该对话独有

最终合并为一个 system message,放在对话最前面。

示例(可直接用)

全局 system 可包含:

  • 语言与风格:默认中文、简洁、先结论后解释
  • 安全边界:遇到违法/隐私请求拒答并给替代建议
  • 格式规则:要求输出 JSON 时必须严格 JSON

注意:不要把过多业务细则塞进全局 system,否则会扩大“必须记住的约束集合”,训练更难稳定。

4.2 system 的位置必须稳定:永远在最前

多轮对话数据中,system 偶尔会出现在中间(例如角色切换)。如果你不做处理,模型会学到 system 是“可插入的提示词”,从而削弱它的权威性。

实操建议:

  • 训练用数据:system 固定放最前;中途角色切换改成 user 明确描述(例如“从现在开始你扮演…”),或转为新的 conversation(新对话开始)。
  • 推理用模板:严格遵循同样结构,否则训练-推理分布不一致。

4.3 system 是否参与 loss?一般不参与,但要用“出现频率”保证学习

在 SFT 中,通常只对 assistant 计算 loss,这是主流做法。

问题是:system 不参与 loss,会不会学不会?

  • 模型学习 system 的方式,是通过“在 system 条件下生成的 assistant 回复”间接学习
  • 因此关键是:system 必须高频、稳定出现且不被截断

如果你发现模型仍然不听 system,优先排查:

  1. system 是否经常被截断
  2. 不同数据源的 system 是否冲突(一个要求简洁,一个要求啰嗦)
  3. 是否大量存在“无 system 的训练样本”(比如把 system 丢了)

在少数情况下,你可以尝试给 system 后面加一个“确认性 assistant 轮次”来强化约束,例如:

  • system: 规则…
  • user: 请确认你理解上述规则
  • assistant: 我已理解,将遵循…

但这会引入额外对话模式,可能影响真实推理体验;建议只在你明确需要增强 system 遵循时使用,并控制比例。

4.4 多 system 合并与冲突解决:给出可执行规则

当一条样本里出现多个 system(例如数据源 A 的全局 system + 标注员额外 system),建议:

  • 以“更严格/更安全”的规则优先
  • 同类规则只保留一条(避免重复)
  • 相互矛盾时,保留更上层(全局)并删除下层冲突项

可以用简单的 YAML/JSON 配置维护规则集合,训练前做合并。


五、把“对话打包 + 截断 + system”串起来的推荐数据流水线

下面给一个从原始对话到训练样本的最小可行流水线(你可以按自己的框架实现):

5.1 数据清洗与结构化

  1. 过滤明显低质样本:空回复、答非所问、重复刷屏、极端超长输出
  2. 统一字段:messages = [{role, content}, ...]
  3. 归一化 role:system/user/assistant(工具调用另算)

5.2 system 归一化与注入

  1. 若无 system:注入默认 global system
  2. 若有多个 system:合并为一个放最前
  3. 统一措辞与格式(例如都用中文、同一套风格约束)

5.3 截断:system 优先 + 尾部轮次保留

  1. system 单独保留
  2. 从尾部向前加入轮次直到接近 max_len
  3. 避免切碎 assistant 回复;必要时按句边界截断

5.4 打包(Packing)

  1. 优先以“完整对话”为单位打包
  2. 对话之间插入 <|conversation_start|> 或等价分隔
  3. labels 仅覆盖 assistant token,其余 -100

5.5 训练时的两个小技巧(降低遗忘)

  • 混合一小部分旧任务数据:例如 5%~20% 的单轮指令/格式数据作为回放(replay),是最直接有效的抗遗忘手段之一。
  • 控制学习率与训练步:多轮对话数据分布更集中,过大 LR 或过多 epoch 更容易把原能力“推走”。建议从更小 LR、更少 epoch 起步,用回归集决定是否加大。

六、一个可复用的多轮样本示例(展示截断与 mask 思路)

假设我们有如下对话(简化示例):

  • system:你是企业知识库助手,回答需引用来源;不确定就说不确定。
  • user:我们产品 A 支持离线模式吗?
  • assistant:支持……(引用文档 1)
  • user:那离线模式有哪些限制?
  • assistant:限制包括……(引用文档 2)

训练时建议构造成“模型输入 = system + 历史 user/assistant + 当前 user”,并对“当前 assistant 回复”计算 loss。

如果对话很长导致超长:

  • 保留 system
  • 优先保留最后一轮(“离线模式有哪些限制?”及其回答)
  • 更早的轮次(是否支持)可选择保留或丢弃

labels 的直觉:

  • system 与 user 都是条件,不让模型去“背诵提示词”
  • assistant 才是监督目标

这会显著降低在多源数据下的过拟合噪声,并提升 system 约束的一致性。


七、排障清单:出现遗忘时优先检查什么

当你发现“多轮更强,但旧能力掉了”,按优先级检查:

  1. system 是否被截断:统计训练样本中 system 完整保留比例(建议接近 100%)。
  2. system 是否冲突:抽样 200 条 system,看是否互相打架(风格、拒答、格式)。
  3. packing 是否跨对话污染:检查对话边界是否有分隔,labels 是否正确 mask。
  4. 无 system 样本占比是否过高:尤其是从日志提取的数据。
  5. 训练超参是否过猛:LR、epoch、warmup、weight decay;优先减小 LR/步数。
  6. 是否缺少回放数据:加入少量通用/格式数据往往立竿见影。

结语:用“结构化样本 + 稳定 system + 合理截断”把多轮收益变成可控工程

多轮对话 SFT 的收益很大,但前提是你把数据工程做到位:

  • 打包要有边界与 loss 隔离
  • 截断要保 system、保完整轮次、少切碎回答
  • system 要统一、稳定、可复用

把这三件事做好,你会发现模型的多轮一致性提升同时,通用能力与指令遵循不再大幅退化,灾难性遗忘从“必然发生”变成“可测、可控、可迭代优化”的工程问题。

多轮对话SFT怎么避免灾难性遗忘:对话打包、截断策略与system指令处理
https://aissn.com/111.html