作者通过 18 次盲测发现,纯噪声标签的训练 loss 反而下降 62%,证明 per-sample loss 排序无法有效发现数据损坏。
几乎每个从业者都带有这样一个未经审视的假设:如果你的数据集里有坏样本,loss 会告诉你。损坏的行会突增。坏掉的会很显眼。按样本 loss 排序,看列表顶部,垃圾就在那里。
我也这么相信过。我自己工作中的两次独立失败表明这个假设是错的,而且它们以同样的方式失败——悄无声息的。
在验证 trainproof 时,我进行了一项受控的故障注入研究:一个基础配置,Qwen2.5-3B QLoRA,六种运行方式,三种随机种子,共十八次运行。所有日志都随仓库发布,所以结论可以被验证而不是被盲目相信。
其中一种配置将数据集中的标签打乱成纯噪声。标签与输入完全不再对应。这不是一个难题数据集或噪声数据集。这是一个无法学习的数据集,因为其中没有任何可学习的关系存在。
该运行将 loss 降低了 62%。
在其自身的曲线图上,这是教科书级别的健康——干净的下降斜率,没有突增,没有平台期,没有任何人类或规则会标记的东西。它没有学到任何有用的东西。它只是在记忆噪声的统计数据,任何足够大的网络都会乐意这么做。仅从单次运行的 loss 曲线来看,它与真实运行无法区分。
这就是我的假设崩塌的地方。不是「loss 对此是弱信号」。在孤立情况下,loss 对此根本不是信号。
第二次失败来自真实工作而非实验,这是我思考更多的一个。
构建一个大约 110,000 条录音的文本转语音语料库时,少量文件是纯响亮白噪声。不是文件格式意义上的损坏——它们可以正常打开、正常播放、拥有有效的头信息和有效的时长。它们只是不包含任何语音。只是噪声,而且音量很大。
它们没有作为高 loss 异常值浮现出来。
对于这个的认知状态保持精确很重要:那些训练日志已经不存在了。我无法展示那些数字,也不打算从记忆中重建它们。这是一个生产环境的观察,而非一次测量。把它当作促使我开始寻找的轶事,而把上面的故障注入研究当作承载证据的部分。
但两者吻合,这就是把它写下来的原因。
两个案例的机制相同,一旦你看到了,惊讶就消失了。
我们把「噪声」当作「不可预测」的同义词,然后悄悄地假设模型会在其上严重失败。白噪声在 loss 函数关心的意义上并不是不可预测的。它是平稳且均匀的。它的分布简单且一致。网络快速且廉价地拟合那个分布——而拟合它被记录为 loss 下降。
标签打乱是另一个方向的相同故事。映射被摧毁了,所以没有可泛化的东西残留,但网络仍然可以记忆目标的边缘统计量。Loss 下降。没有突增。
在两种情况下模型都学到了一些东西。只是不是你要的那个东西。Loss 函数对两者中学了哪个没有意见。
与基线比较;不要孤立判断。 标签打乱的运行在它与已知良好的运行并排放置时立即变得明显——去看每个运行达到的相对底部,损坏的运行完全处于不同的 regime。单次运行会说谎;两次运行并排会说出真相。这就是 trainproof 有一个比较模式的全部原因:不是作为便利,而是因为单运行规则可以被证明无法捕获这类失败。
在训练前将数据作为数据来检查。 这是我犯错时间比我想承认的更长的部分。我从训练曲线推断数据集质量,这是间接测量的间接测量。对于音频,检查既廉价又直接:频谱平坦度、动态范围、静音比率、语音频段能量。纯白噪声文件可以 trivially 与语音分离——如果你去看音频的话。通过看 loss 是无法分离的。
这一点超出了我的两个案例,值得明确陈述:
Loss 曲线测量的是模型是否在拟合某些东西。它不测量那个东西是否是你的任务。
任何用模型可以拟合的分布替换你的信号的损坏,都会通过你拥有的每一个基于 loss 的检查。截断的样本、静音、重复的行、标签噪声、错误语言的文本、空目标——这些都不能保证产生突增。有些被保证不会。
烧掉最多 GPU 时间的那种失败不是那些崩溃的。它们是那些看起来完全像成功的。
故障注入日志都在 trainproof 仓库中,包括标签打乱的运行和一次 9.8 小时的 Coqui XTTS 微调——它自行发散,没有人动过它。
如果你遇到过确定性检查本可以捕获的失败模式,在仓库上告诉我——会放进去,并注明来源。