JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

错误分析与模型调试

一个测试分数只告诉你模型表现如何,却从不告诉你它为什么会失败。这篇导读教你做侦探:去读模型真正犯下的错误,按子群体把分数切开来看,并判断你面对的究竟是偏差问题、方差问题,还是数据问题——这样你才能修对地方。

走出计分板:真正去看那些错误

到现在为止,你已经能在留出集上算出一个干净的测试分数,并且信任它,因为前面几阶教过你诚实的验证和混淆矩阵。但"91%"这样的数字是一个*判决*,而不是一个*解释*。它告诉你模型*多久*错一次;却从不告诉你错的是*哪一种*,或*为什么*。错误分析就是这样一个习惯:关掉电子表格,亲眼一个一个地去读模型真正答错的那些案例。

下面是从业者真正在用的具体做法,它比听起来要朴实得多:拉出 100 到 150 个被模型分错的样本,放进一张表里,给每一个标上一个简短的原因——"照片模糊"、"反讽"、"标签本身就错了"、"罕见犬种"。然后数一数这些标签。错误几乎总会成簇出现:也许 40% 的失误来自某一个你一下午就能修好的原因,外加一条长长的、各式各样只出现一次的怪例——后者你眼下应当忽略。这一个小时的阅读,常常胜过一周盲目的超参数调优。

切片:一个数字底下藏着十个故事

一个汇总分数是一个*平均值*,而平均会通过抹平来说谎。一个整体 92% 的模型,可能在简单、常见的案例上是 97%,而在某个虽小却重要的子群体上只有 61%——如果这个子群体恰好是夜间拍摄的照片,或是某种少数方言的说话者,那么这个平均值就悄悄掩埋了一个在实际部署中极其要紧的失败。切片的意思,就是在数据有意义的子集上分别计算你的指标,而不是只在整体上算。

选择那些对人类有意义的切片:按类别(你前一阶讲过的类别不平衡已经说明了为什么稀有类别值得单独占一行)、按输入属性(长度、图像尺寸、一天中的时段)、按数据来源,以及——这点至关重要——当决策会影响到人时,按人口统计群体来切。读切片也是你抓到模型依赖伪相关的途径:如果一旦你把那种"泄底"的伪影不存在的案例切出来、准确率就崩塌了,那说明模型一直在骑着那个伪影,而不是在学习真正的任务。

偏差对方差:最核心的诊断

一旦你知道了*哪些*错误最伤人,下一个问题就是模型*为什么*会犯它们——而几乎每一种原因,最终都归结为你在偏差-方差权衡中见过的两种诊断之一。高偏差欠拟合)意味着模型太简单或太懒,连训练数据都拟合不了:它连自己已经见过的东西都答错。高方差过拟合)则意味着它把训练集的怪癖都背了下来,无法泛化:训练集上很棒,测试集上很差。

区分它们的办法,是去比较两个误差和一个参照。先挑一个目标:人类水平的表现,或一个已有的基线,给出你现实中能够够到的下限。然后看从这个目标到你*训练*误差之间的差距,再看从训练误差到*验证*误差之间的差距。第一个差距大,是偏差;第二个差距大,是方差。那第二个差距,正是你学过要盯紧的训练-测试差距——在这里它成了一个方向盘,而不只是一盏警示灯。

target (human/baseline) = 2%
train error             = 3%   -> bias gap  = 1%  (small)
val   error             = 12%  -> var. gap  = 9%  (big!)

=> low bias, HIGH variance: model overfits.
   fix: more data, regularize, simpler model, augment.

( if train error were 11% instead:
  bias gap = 9% (big) -> underfitting;
  fix: bigger model, train longer, better features. )
两个差距的诊断法。哪个差距更大,就为你的问题命名,并指向相应的修法。

两边的修法是相反的,这正是为什么必须先做诊断。对高偏差,你要增加模型容量、训练更久,或设计更好的特征。对高方差,你要反过来做:收集更多数据、加正则化、简化模型,或做数据增强。把一个治方差的招用到偏差问题上,只会越弄越糟——更小的模型欠拟合得更厉害。这也是为什么没有免费午餐的直觉成立:不存在万能旋钮,只有针对*这个*具体失败的那个对的旋钮。

学习曲线:看着差距移动

调试中信息量最大的一张图,就是学习曲线:随着你喂给模型越来越多的数据(或越来越长的训练时间),把训练误差和验证误差画出来。两条曲线会把整个故事讲清楚,不用你去猜。如果两条曲线都在一个偏高的误差处变平、并且彼此靠得很近,那你被偏差卡住了——再加数据也没用,因为模型连手头已有的都拟合不了;你需要一个更强的模型。

如果情况相反,存在一道又宽又持久的竖直缝隙——训练误差很低,验证误差却顽固地居高不下——那道缝隙就是方差,而曲线的形状会告诉你:再加数据能不能把它合拢(验证误差仍在往下走),还是你已经触顶了(它已经平了)。这正是对那个老问题"我该不该多收集些数据"的诚实答案。别去争辩;把曲线画出来,看就是了。

调试是一个循环,而不是一时灵感

把这些拼起来,[[model-debugging|模型调试]]就不再是神秘的瞎鼓捣,而成了一个有纪律的循环。新手常犯的错误是一次改五样东西,然后搞不清是哪一样起了作用;而纪律在于:从你的错误分析中提出*一个*假设,做*一个*改动,再到*同一个*固定的验证集上重新测量。分数动了或没动——无论哪样,你都学到了一件真东西。

  1. 读错误。抽样 100 个以上的错误,给每个标上原因,数标签,按影响排序。
  2. 切分分数。把指标按类别、来源、群体拆开;找出那些远低于平均的切片。
  3. 诊断偏差还是方差。比较 目标 -> 训练 -> 验证 的差距,并读学习曲线。
  4. 只改一样。施加诊断所指向的修法——偏差就加容量,方差就加数据/正则化。
  5. 在同一个集合上重新测量,记录你尝试了什么,然后循环,直到达到误差预算——而不是直到你累了。

最后留两句诚实的提醒。第一,当你实在想不通模型为什么在某个案例上失败时,就借助后面某一阶的可解释性工具——一份特征重要性读数或一张显著性图,可能揭示出模型盯的是水印,而不是肿瘤。第二,要警惕你针对验证集调试了太多次,以至于悄悄开始对它过拟合;这正是为什么最终那句不带偏的话,永远属于一个你只碰过一次的测试集。错误分析能让模型变好,却从不能让它无所不知——一个被调试好的模型,是一个你终于*理解*了其余下失败的模型,而不是一个毫无失败的模型。