模型训完了。目标任务上的类别准确率 95%,超过了 94% 的教师基线;单次推理延迟从 2.3 秒降到 0.4 秒,成本按预期大幅下降。
皆大欢喜——但团队里有人问了一个问题:我们怎么知道它没有在别的地方偷偷变笨?以及更根本的:这个 95% 能不能代表它上线后的真实表现?
5.3 讲的 SFT 教模型「照着这些示范做」。但有一类需求是示范教不了的:当存在多个都不算错的输出时,哪个更好?摘要是详细些好还是简洁些好?语气该多正式?拒绝一个不合理请求时该怎么措辞?
这类偏好难以写成标准答案,却容易做比较——给人看两个输出,问哪个更好,人几乎总能回答。preference alignment(偏好对齐)就是利用这种「比较比生成容易」的不对称性。
对应用层团队的实用判断:绝大多数微调项目只需要 SFT,不需要偏好对齐。当你的任务有明确的对错标准(分类、抽取、结构化输出),SFT 就够了。只有当「什么算好」本身难以定义、只能靠比较来表达时,才值得引入 DPO 这一层。而在那之前,先确认 SFT 的数据质量已经榨干了——多数被归因为「需要对齐」的问题,其实是 SFT 数据不够好。
本章真正的重点在这里。微调模型的验证有三个层次,缺一层就可能上线一个看起来很好的坏模型:
| 层次 | 回答什么问题 | 做法 | 漏掉会怎样 |
|---|---|---|---|
| ① 目标任务 | 训练的这件事做好了吗 | 独立 eval set,按类别分桶看指标 | 被总体指标掩盖的长尾崩塌 |
| ② 能力回归 | 别的能力有没有退化 | 与训练任务无关的通用测试集 | 灾难性遗忘悄悄发生,接入更复杂流程时才爆 |
| ③ 三方对比 | 相比教师和基座各如何 | 学生 / 教师 / 未微调基座,同一 eval set | 不知道提升是否来自微调,也不知道离教师还差多远 |
① 必须按时间切分,不能随机切分。这是最重要的一条。随机切分会让训练集和评估集来自同一时间段的同一批数据——近似重复的样本可能同时出现在两边,评估分数因此被系统性高估。正确做法是用训练数据之后时间段的样本做评估,这同时还检验了模型对分布漂移的适应性。
② 必须覆盖长尾。如果 eval set 按真实分布采样,那么长尾类别可能只有几个样本,指标毫无统计意义。应当对长尾类别过采样,保证每个类别都有足够的样本量,然后分类别报告而不是报一个加权总分。
③ 必须包含已知的难例。把历史上教师模型出过错的样本、人工修正过的样本、业务上最关键的边界情况单独收集成一个子集。这个子集的表现比总体准确率更能预测上线后的真实体验——因为用户不会平均地遇到问题,他们遇到的正是这些难例。
数据泄漏是微调项目最常见的自欺。症状是「离线评估 96%,上线后感觉完全不对」。除了时间切分,还要主动检查评估集与训练集之间的近似重复(用 5.3 的去重方法跨集合跑一遍)。一个被污染的评估集,比没有评估集更危险——因为它给了你虚假的信心。
离线评估再充分,也无法完全预测线上表现。三段式上线是把风险切碎的标准做法:
① 影子模式(shadow mode)。新模型与线上模型并行接收真实流量,但输出不生效,只记录下来做对比。这是最有价值的一步——它用真实的线上分布验证模型,且零风险。运行一到两周,你会发现离线评估集里根本没有的输入形态。
② 灰度发布。从很小的流量比例开始,按业务指标(而非离线指标)逐步放量。关键是监控要能按模型版本分桶,否则新旧模型的表现会被混在一起。
③ 保留回退通道。必须能一键切回教师 API。故事线在 5.1 就把这条写进了退出条件——回退能力不是上线后再补的,是设计时就要保证的(6.5)。
在 DPO 之前,用人类偏好调整模型行为的标准路径是 RLHF:先在偏好数据上拟合一个奖励模型,再用强化学习去最大化这个奖励。这条路径有效,但工程上很重——两个阶段、训练中需要从模型采样、超参数敏感。
Rafailov 等人的贡献是指出:利用奖励函数与最优策略之间的映射关系,这个受约束的奖励最大化问题可以用单阶段的策略训练来求解——本质上变成在偏好数据上解一个分类问题。
这项工作对应用层的意义在于降低了门槛:偏好对齐从「需要一支 RL 团队」变成了「和 SFT 差不多的一次训练」。今天各类微调框架普遍内置 DPO 训练器,一个小团队可以在几天内跑通。
但门槛降低的只是训练那一环——真正的成本转移到了偏好数据的收集上。你仍然需要成对的「A 比 B 好」标注,而这需要明确的偏好标准和一致的标注者。当一项技术的某个环节被大幅简化时,瓶颈通常只是移动到了别处,而不是消失了;此处它移到了数据,这与 5.3 的结论一致。
条件说明:论文的对比在其选定的任务与基线上进行。DPO 与 RLHF 及后续各类变体的优劣仍是活跃的研究领域,不应把「DPO 全面优于 RLHF」当作定论。对应用层团队而言,可迁移的判断是:需要偏好对齐时,DPO 通常是工程上更可行的起点。
① 目标任务:分类别看,而不是看总分。总体准确率 95% 确实超过了教师的 94%。但按类别分桶后暴露了真相——高频类别上学生已经优于教师,而在两个长尾类别上仍明显落后。若只看总分,这个差距会被完全掩盖。
处理:对这两个长尾类别做专门的数据补充(回到 5.3 补样本),并在上线方案中加了一条兜底规则——当模型对这两类的预测置信度低于阈值时,路由回教师 API(2.3 的级联思路)。不是所有差距都必须靠训练填平,架构上绕过去往往更快更稳。
② 能力回归:发现了一处预期外的退化。用一组与工单无关的通用测试样本跑回归,发现模型在多轮对话的指令遵循上有可察觉的下降——它太习惯于「输入一段文本、输出一个 JSON」,对多轮交互的适应变差了。
对当前用途没有影响(这个任务本来就是单轮的),但团队把这一点明确记录在模型卡里:此模型不适用于多轮场景。这条记录后来阻止了另一个团队把它误用在对话产品上。能力回归测试的价值,往往不在于阻止本次上线,而在于划清这个模型的适用边界。
③ 三方对比确认了提升的来源。未微调基座 76%、学生 95%、教师 94%——这组数字同时说明了两件事:提升确实来自微调(而非评估集变简单了),且学生在总体上已达到教师水平。缺少基座这一列,你就无法排除「评估集本身偏易」的可能。
④ 上线:影子模式跑了两周。这一步的收获超出预期——真实流量里出现了离线评估集中完全没有的输入形态:超长工单(用户粘贴了整段日志)、多语言混杂、以及几乎空白的工单。这些在历史日志采样中被过滤掉了,却在真实流量里稳定地出现。团队据此补了输入长度截断和空输入的兜底逻辑,然后才开始灰度。
故事线的结论:这个项目最终成立,靠的不是模型训得多好,而是四件事——① 动手前先把便宜的手段榨干并定下退出条件(5.1);② 选了与任务匹配、留有余量的配置(5.2);③ 把绝大部分精力花在数据而非调参上(5.3);④ 用三层验证和影子模式,在真正影响用户之前发现了三处问题(5.4)。其中任何一步偷懒,结局都可能是一个离线 95%、线上翻车的模型。
DPO 及其变体:HuggingFace trl 提供 DPOTrainer,Axolotl / LlamaFactory(原名 LLaMA-Factory)均已集成。社区还有 ORPO、KTO、SimPO 等变体,各自在数据要求或训练稳定性上有取舍。先用 DPO 跑通,再考虑是否需要变体——变体带来的差异通常小于数据质量带来的差异。
能力回归测试集:可以从公开评测集里取一个小子集(MMLU、IFEval 等各抽一两百条)作为快速回归。不需要跑完整基准——你要检测的是「有没有明显退化」,不是刷榜,几百条足以发现问题且几分钟就能跑完。
实验追踪:Weights & Biases、MLflow 等记录超参、指标、数据集版本的对应关系。「哪个模型是用哪版数据、哪组超参训的」必须可追溯,否则几轮迭代之后你会完全无法归因。
模型卡(model card):为每个微调模型写一份简短说明——训练数据来源与时间范围、目标任务、已知的能力退化与不适用场景、评估结果(分类别)、回退方案。这份文档的主要价值是防止别人误用你的模型,故事线里正是它阻止了一次误用。写它只要半小时。
影子模式的实现:在服务层把请求复制一份给新模型,异步记录输出但不返回给用户。注意成本——影子期间两个模型都在跑,要计入预算;也要注意幂等性,影子调用绝不能触发任何写操作或副作用。
因为随机切分会造成数据泄漏,让评估分数系统性高估。
真实业务数据里存在大量近似重复——同一模板生成的工单、同一用户的连续提交、批量导入的相似记录。随机切分时,一条样本的「近亲」极可能同时落在训练集和评估集里。模型在训练时见过它的近亲,评估时自然答得很好,但这测的是记忆,不是泛化。
按时间切分解决两个问题:① 天然隔离了近似重复——同一批次产生的相似样本会一起落在同一侧;② 额外检验了分布漂移——真实上线面对的永远是「训练之后」的数据,用户行为、产品形态、话题热点都在变。按时间切分的评估更接近上线后的真实条件。
补充措施:即使按时间切了,仍应跨集合跑一次近似去重检查,确认没有漏网的重复。这一步几分钟就能做完,却能避免「离线 96%、上线翻车」这类最令人困惑的失败。
「全面超过」这个判断本身要先被检验,然后才谈上线方式。四点:
① 「全面」是按什么口径算的?总体准确率超过,不等于每个子集都超过。必须按类别、按输入长度、按难度分桶分别对比。故事线里正是分桶后才发现长尾类别仍落后于教师——而总分是超过的。
② 评估集有没有被污染?确认按时间切分,并跨集合做过近似去重检查。一个被污染的评估集会让「超过教师」这个结论毫无意义。
③ 能力回归做了吗?目标任务超过教师,不代表通用能力没退化。至少要跑一组无关的通用测试,并把发现的退化写进模型卡限定适用范围。
④ 直接全量替换是最大的问题。正确的路径是影子模式 → 灰度 → 全量:先并行接真实流量只记录不生效(能发现离线评估集里没有的输入形态,故事线里发现了三类),再小比例放量按业务指标观察,最后全量。并且必须保留一键切回教师 API 的通道。
可以补一句架构上的建议:即使全量切换,对模型置信度低的请求保留回退到教师的级联路径——这比「非此即彼」的替换稳健得多,成本增加也有限(2.3)。
SFT 教「照着这些示范做」。它需要「输入 → 期望输出」的样本对,适用于有明确对错标准的任务:分类、抽取、结构化输出、特定格式或风格的生成。
DPO 教「这两个都不错,但这个更好」。它需要成对的偏好数据,适用于「什么算好」难以写成标准答案、但容易做比较的场景:语气拿捏、详略取舍、拒绝请求时的措辞、开放式生成的整体质量。
核心区别:SFT 从单个正例学习,DPO 从成对的相对比较学习。后者能表达前者表达不了的信息——「A 和 B 都对,但 A 更好」这句话无法用示范样本表示。
什么时候需要 DPO:
• 任务有明确对错 → 只需 SFT,这覆盖了应用层的大多数微调项目;
• SFT 后模型「都对但就是不太好用」,且你能说清哪个更好却写不出标准答案 → 考虑 DPO;
• 但在此之前先确认 SFT 的数据质量已经榨干——很多被归因为「需要对齐」的问题,实际是 SFT 数据里存在不一致的示范。
加分:说清 DPO 相对 RLHF 的工程价值——利用奖励函数与最优策略的映射关系,把「拟合奖励模型 + 强化学习」的两阶段流程压成单阶段的策略训练,无需在微调中采样,稳定且轻量。同时点出门槛降低的只是训练环节,瓶颈转移到了偏好数据的收集与标注一致性上。