首页 / 基础与评估 / 模型评估

模型评估:准确率骗了你多少

模型训练完了,怎么知道它到底行不行?「准确率 95%」听起来很好, 可如果 95% 的样本本来就是同一类,这个数字一文不值。 这一页把混淆矩阵、ROC、PR 和交叉验证四样东西摆在一起, 让你拖动阈值、切换场景,亲眼看它们怎么互相打架。

两类样本的分数分布:左边是负例、右边是正例(重叠处就是模型分不清的地方)。 竖线是当前阈值——线右侧全部判为「正」。 拖动阈值,看下面四个格子怎么此消彼长。 (合成数据里两类重叠得多,阈值 0.5 并不是好选择,试着拖到 0.35 附近看看。)
判对了多少,判错了哪种
ROC 曲线
横轴:负例被误判为正的比例。纵轴:正例被抓到的比例。动阈值时圆点沿曲线滑动。
PR 曲线
横轴:召回率。纵轴:精确率。虚线是「瞎猜」的水平 —— 准确率再高也骗不过它。
每一折长什么样
每条横条代表一折,填充长度 = 该折里正例的比例; 灰线是整体的正例比例。如果不分层,数据里恰好又是「同类样本挤在一起」, 就会出现某一折几乎没有正例的情况——那一折的准确率毫无意义。

                
              
①段是你手调阈值的结果,跟着滑杆实时变;②③段是同一件事的 sklearn 写法。 注意 roc_auc_score 不需要传阈值——它把所有阈值都考虑完了。

① 混淆矩阵:四种结果,不是一个数字

分类只有「正/负」两种预测和两种真实,两两组合就是四个格子。 准确率把四个格子揉成一个数,代价是把两类错误的不同后果抹平了:

② 精确率与召回率:一对跷跷板

精确率问「你说是正例的,有多少真是」,召回率问「真正的正例,你抓到了多少」。 把阈值调低,召回率必然上升、精确率必然下降——这不是缺点,是定义决定的。 是两者的调和平均, 用来在必须给一个数的时候兼顾两边。

③ ROC:把阈值从头到尾走一遍

与其纠结一个阈值,不如把阈值从 +∞ 降到 −∞,每走一步记下 (假正率, 召回率):

曲线下的面积就是 AUC,它有一个很好用的概率解释: 随机抽一个正例和一个负例,模型给正例的分数更高的概率。 所以 AUC = 0.5 就是瞎猜,与阈值无关——换阈值只让你在曲线上滑动,不改变面积。

④ PR 曲线:类别不平衡时更敏感

FPR 的分母里有 。负例极多时, 即使冒出一大批假正例, 看起来仍然很小, ROC 曲线照样很漂亮。PR 曲线的分母只有真正例和假正例,藏不住。 所以做稀有病筛查、欺诈检测这类任务,优先看 PR。

⑤ 交叉验证:一次划分不算数

把数据切一份当测试集,得到的分数本身也是随机的。 K 折是把数据切成 K 份,轮流让每一份当验证集、其余当训练集,得到 K 个分数:

重点不是均值,是标准差——它告诉你这个模型的成绩有多不稳。 另外,分类任务要用分层切分:保证每折的类别比例与整体一致, 否则某一折里可能一个正例都没有。

同一份数据、同一个模型、两个场景。 「乳腺癌」和「稀有病」用的是同一组权重、同一个分数公式, 只是后者从这份数据里抽出了患病率 5% 的子集。切过去看看: ROC-AUC 只从 0.986 掉到 0.969(几乎没动), 但精确率从 0.916 腰斩到 0.500——判为「患病」的人里有一半是虚惊; PR-AUC 也从 0.983 掉到 0.919。这就是为什么类别不平衡时要看 PR 而不是 ROC。 想看更纯粹的对照(分数分布完全不变、只改患病率), 用合成数据集拖「正例占比」滑杆。
1. 稀有病那个数据集里,一个「全都判健康」的傻瓜模型准确率有 95%——它到底错在哪?
它一个患者都没抓到,召回率 = 0,精确率按约定记 0,F1 = 0。 准确率高只是因为负例占了 95%,把「全判负例」当成了正确答案。 这正是准确率在类别不平衡时的失效:它给每个样本同样的权重, 而现实中漏掉一个患者的代价远高于误报一个健康人。 切到 PR 曲线看,这个模型的曲线会贴着底部走。
2. 为什么 AUC 与阈值无关,准确率却强依赖阈值?
因为 AUC 对所有阈值积分:它衡量的是分数排序的质量—— 正例的分数是不是普遍比负例高。换阈值只改变你切在哪一刀,排序没变。 准确率则是在某一个具体阈值上数对错,切的位置一变,FP 和 FN 的分配就变。 所以 AUC 是「模型的属性」,准确率是「模型 + 阈值」的共同属性。
3. 乳腺癌上的 AUC 高达 0.986,是不是说明这个模型可以直接用了?
不能只看这一个数。AUC 高说明排序能力好,但实际使用时你必须定一个阈值, 而 0.5 这个默认阈值在这里只给到 94% 准确率、召回率约 92%。 把阈值调到 0.2 左右,召回率能上到 97% 以上(代价是精确率下降、更多健康人被叫回来复查)。 在筛查场景里这个交换通常是值的——阈值的选择是业务决定,不是算法决定。 切到数据集里的乳腺癌,自己拖阈值看这三个数的变化。
4. 非分层 K 折时,某一折里可能只有 1 个正例(稀有病数据集就是这样)。这一折的准确率会怎样?
会失真。若这 1 个正例恰好被判错,这一折的准确率立刻掉到接近 0(如果折里 75 个样本只有 1 个正例, 判错就丢了 1/75);若判对了,准确率又漂亮得看不出问题。 方差被人为放大了,10 折跑出来的标准差会大得吓人——但那是切分方式造成的,不是模型不稳。 这就是分层切分存在的理由:让每折的类别比例都接近整体。 本页的「每一折长什么样」那组横条就是用来看到这一点的。
5. 为什么这一页的 ROC 曲线有几百个点,而不是「阈值从 0 到 1 每隔 0.01 取一个」?
因为 ROC 的定义是沿样本逐个移动阈值:把样本按分数从高到低排列, 每纳进一个样本就记一个点。曲线只会在样本的分数处拐弯, 中间取再多点也只是在同一条线段上插值,白白浪费计算。 sklearn 更进一步:默认还会删掉共线的中间点(drop_intermediate=True), 乳腺癌那 570 个点会被压成 42 个——形状完全一样,只是更省。