监督信号与评估指标不匹配导致模型表面指标漂亮但实际无效,作者用三个失败案例说明为何要直接监督最终评估目标。
过去一年,我一直在对开放的视觉-语言模型进行微调——从 9B 稠密模型到 35B 专家混合模型——使用监督微调和 GRPO 风格的强化学习,以可验证的奖励为信号。我学到的大部分东西与算法无关,而是关于训练过程看起来健康却什么都没做、或者崩溃原因与你的代码毫无关系的种种方式。
三个失败案例,按它们迷惑我的时间长度排序。
我运行了一次 18 小时的监督微调,报告的 token 准确率稳步攀升至 99%。看起来像教科书般的运行。但真正的评估指标——多项选择题的准确率——从未动过。
原因是我的监督目标与评估目标不匹配。训练 loss 基于自由文本推理痕迹计算;而评估却对一个提取出的答案字母打分。模型变得极其擅长复现训练文本的形态——因此 token 准确率达到 99%——但这并没有迁移到我真正关心的决策上。
Token 准确率是一个代理指标,而代理指标恰恰在你停止检查时会偏离目标。修复是结构性的,而非调超参:监督你评估的那个东西。如果交付物是一个受限的答案,训练信号必须到达那个答案,而不只是它周围的文本。
我总结的通用规则:任何训练指标只要不是你的评估指标,就是关于相关性的一个假设,而你应该在花 GPU 日之前验证这种相关性。
9B 视觉模型的 GRPO 训练器在前向传播中崩溃了,深陷旋转位置编码代码中。我的训练代码没有任何改动。
诊断花了一些时间,因为 bug 活在组件边界上:文本序列长度源自 token 类型 id,而视觉序列长度来自图像网格——并且图像填充 token 被重复计算了两次。同一技术栈的两个部分各自内部一致,却对输入长度产生了分歧。
对于同一家族的 35B MoE 变体,一个等效的 rope bug 可以通过猴子补丁修复模型的位置 id 计算来解决。我发布了补丁,同时附带了一个无需 GPU 的回归测试:一个构建精确失败输入形状、仅在 CPU 上运行位置 id 路径的小脚本。它几秒内跑完,不需要集群,如果上游更新重新引入 bug 会大声失败。
两个教训。第一,当你在模型家族工具支持的边缘进行微调时,遇到的 bug 是集成 bug,栈追踪指向的是受害者,而非罪魁祸首。第二,每个猴子补丁都应该有一个零成本的回归测试——否则下一次库升级会静默地让你的修复失效。
在另一个项目中,我用强化学习微调一个 9B 模型,奖励来自真实世界的实际结果,而非标注数据集。很长一段时间里训练信号是平的——没有发散、没有崩溃、就是平,这是信息量最少的失败。
两个叠加的问题。其一是奖励管道中的标签噪声:一些结果被归因到了错误的决策上,这稀释了任何梯度。另一个更糟:符号错误导致部分优势信号被反转了。模型被轻轻地向远离有效行为的方向推。
没有任何崩溃。每批都正常处理。每行日志看起来都像一次正常的训练。唯一的症状是学习的缺失,而我发现它的唯一方法是从"留出指标现在应该动了"往回倒推,手动审计奖励计算的每个阶段。
两个修复之后,我在那个任务上得到了第一个真正单调递增的学习曲线。我仍然只把训练信号当参考——决策者是相对于基模型的留出评估,我不报告只存在于训练曲线中的改进。
这些规则出自上述失败加上跨越 70+ 视觉-语言模型的基准测试项目。它们很无聊,但它们是数字与噪声之间的区别。
提交算力之前先冒烟测试。用两个数字做五步 GRPO 运行:PPO 风格的 clip ratio 和可解析输出的比例。如果 clip ratio 退化或可解析性低,完整运行会在五步就已经揭示的方式下产出垃圾。
运行门控 fail-closed。除非评估阶段实际打出了分数,否则不发布任何结果。"评估崩了但训练完成了"不是结果;这是一次未打分的运行,未打分的运行必须不可能被误认为已打分的。
基础设施失败和差性能是不同的列。不可解析的输出、OOM、内核崩溃——这些是异常,其计数必须恰好为零。弱模型产生零异常,只是得分低。如果质量阈值可以吸纳基础设施失败,一次完全崩掉的运行也能通过你的门控。
留出指标是唯一的决策者。训练曲线、token 准确率、奖励趋势——所有这些都是遥测数据。如果留出数字没动,什么都没发生。
这些没有一条是新奇的。但它们是我信任的运行和我损失的那 18 小时之间的全部区别。
我写关于 ML 评估、世界模型以及测量如何悄然失效的内容。更多见 dev.to/rickeshtn。