图表误读:机器学习中的正确数字,为什么会导向错误判断?

目录
设想一家制造企业正在讨论,是否要上线一套新的设备异常预警系统。生产负责人希望减少非计划停机,维修主管担心误报带来额外巡检,数据团队则拿出一组简化的教学结果,比较两套方案。
方案一在 500 个测试周期上的准确率为 87.2%,方案二为 85.4%。如果会议只看这两个数字,方案一似乎已经胜出。
维修主管追问了一句:“它们各自漏掉了多少次故障?”
把结果拆开以后才发现,方案一虽然准确率更高,却始终预测“正常”,漏掉了全部 64 次故障;方案二产生了更多误报,却识别出了其中 44 次。
在“漏检一次故障的代价是一次误报巡检的 5 倍”这一教学假设下,准确率较低的方案反而具有更低的加权误判成本。87.2% 和 85.4% 都没有算错,问题在于评审者把“更好”的定义交给了单一指标。
本文把这类情况称为“图表误读”:图表中的数字可能完全正确,读者得出的结论却超出了证据范围。常见原因有三类——评估边界设错、算法输出被过度解释,或者汇总指标没有对应真正的决策目标。下文从数据与表示、模型与评估两个层面,分析六类常见图表,以及它们还需要哪些诊断证据。
除 Wine 公共数据集外,本文其余案例均为使用固定随机种子构造的教学实验。所有图表只用于说明诊断方法,不代表工业或零售场景中的普遍表现。
一、先问图表能支持什么判断 #
面对一组实验结果,很多人的第一反应是“该用哪个函数出图”。但真正决定图表是否有用的,不是图形类型,而是这次评审究竟要作出什么判断。
例如,混淆矩阵回答的是“在当前阈值下错了多少次、错在哪里”;PR 曲线回答的是“调整阈值以后,查准率(precision)与查全率(recall)如何权衡”。两张图都可以画得完全正确,却不能替对方回答问题。
所以,看图之前不妨先确认三件事:当前检查的是数据表示还是模型评估?算法实际产出了什么?读者又准备据此作出什么判断?图表本身未必有错,但它只能支持有限的结论。误读往往始于读者拿它回答另一个问题。
为了把这些问题落到具体工作流,本文沿用 scikit-learn 的六类任务划分:预处理、降维、聚类、分类、回归和模型选择[1]。前三项属于数据与表示,后三项属于模型与评估。下表先给出一张阅读地图:每类总览图容易漏掉什么,又需要补充哪些证据。
| 工作类型 | 总览图易漏掉什么 | 补充诊断 |
|---|---|---|
| 预处理 | 统计量泄漏、时间漂移 | 训练集拟合、时间轴、缺失率 |
| 降维 | 量纲主导、参数摆布 | 载荷、解释方差、稳定性 |
| 聚类 | 连续分布被强制切分 | 轮廓分布、簇规模、重采样 |
| 分类 | 少数类漏检、告警负担 | 计数矩阵、PR、校准、成本 |
| 回归 | 局部偏差、滞后贴合 | 残差、业务切片、朴素基线 |
| 模型选择 | 折间反转、选择偏差 | 配对折线、学习曲线、独立测试 |
这张表给出的是看图顺序,不是另一套评分标准:先弄清图表正在回答什么,再检查它遗漏了什么。以分类任务为例,计数混淆矩阵告诉我们当前阈值下漏报了多少次故障、触发了多少次误报;PR 曲线则展示报警阈值变化时,查准率和查全率如何权衡。前者看不到所有阈值下的变化,后者也不能直接给出实际误报数量和成本。
接下来的六个案例涉及不同算法,背后却反复出现同一个问题:图表展示的是算法输出,读者却很容易把它当成完整的决策依据。常见的代码输入、输出形状放在文末附录;正文先从最容易被忽略的数据边界讲起。
二、六类图表误读:从数据表示到模型评估 #
2.1 数据与表示:先分清变换与真实结构 #
模型还没有开始预测,数据的样子其实已经变了。预处理改变刻度,降维改变观察角度,聚类则为样本加上分组标签。如果这些步骤越过训练集与测试集的边界,或把量纲差异和算法分区误当成业务规律,后续评估得到的高分就可能失去意义。
预处理:分布整齐,也可能发生泄漏 #
先看一组存在明显时间漂移的传感器读数。训练期有 300 条记录,测试期有 150 条;测试期读数整体偏高,可以把它理解为设备状态随时间发生了变化。具体来说,训练读数从 \(N(50,8^2)\) 抽取,测试读数从 \(N(60,9^2)\) 抽取。这里的 50、8、60 和 9 是生成分布参数,不是这批有限样本的实际统计量。
数据团队希望先把读数转换到统一尺度。以标准化器 StandardScaler 为例,它用数据估计均值 \(\mu\) 和标准差 \(\sigma\),再将特征变换为 \(z = (x - \mu)/\sigma\)。预处理器还可能估计分位数或缺失值填充值,这些统计量都必须来自训练数据。
两张标准化后的分布图都围绕零点,看起来同样整齐,但其中一张可能已经“偷看”了测试集。均值归零、方差归一,只能说明变换完成了,不能证明评估边界仍然干净。如果先对完整数据表调用 fit_transform,再划分训练集和测试集,测试集的分布信息就会进入训练基准,造成数据泄漏(data leakage)。
固定随机种子生成的这批样本中,训练集均值为 49.67,测试集均值为 60.23,合并后的均值为 53.19。正是这三个实际均值,让两种处理方式的差异变得可见。
上方 A 图在切分前用全部样本拟合缩放参数。测试集的较高读数把全局零点抬高,也将训练集标准化后的均值压低至 -0.37。
下方 B 图只用训练集估计 \(\mu\) 和 \(\sigma\),再把同一把标尺应用到测试集。训练集变换后的均值为 0.00,测试集相对于训练样本实际偏移约 +1.42σ。换言之,全量缩放污染了训练基准,却没有消除训练期与测试期之间的分布错位。

问题不在于缩放公式算错,而在于读者把被全量缩放压平的分布当成“没有漂移”。数字经过了正确计算,评估边界却已经被测试集信息污染。
在数据准备阶段,还有两类与预处理同源的图表假象值得警惕:
- 随机切分可能掩盖时间漂移:当销量或传感器序列存在趋势或季节性时,随机切分可能让训练集和测试集的分布看起来相似;按实际部署时间切分后,训练期与未来测试期的差异才会显现。
- 事后回填字段可能造成泄漏:如果某个特征的单变量 AUC 接近
0.99,或在重要性图中远高于其他特征,应检查它是否在故障发生后才生成或更新,例如维修工单编号、停机复位标志或事后补货量。它可能是泄漏信号,而非可用于预测的“黄金特征”。
怎样核对:按预测时点或实体边界切分数据;只在训练数据上调用 fit,再对验证集和测试集调用 transform。把预处理器和估计器放进同一条 Pipeline,确保交叉验证每一折都只用该折的训练子集估计统计量[2][3]。对于重要性异常突出的特征,还要检查数据血缘,确认字段在预测时点之前已经可用。
降维:高解释方差可能只是量纲效应 #
这一次不是合成数据,而是 scikit-learn 内置的 Wine 公共数据集。它包含 178 个葡萄酒样本,每个样本有 13 项化学或物理指标。麻烦在于,这些指标虽然出现在同一张表里,数值尺度却相差很大:脯氨酸含量(proline)分布在 300 至 1680 之间,样本方差超过 \(9.9 \times 10^4\);非黄烷类酚(nonflavanoid_phenols)和色调(hue)等指标则只在 0.1 到 1.7 之间变化,方差不足 0.06。
数据团队希望用主成分分析(Principal Component Analysis,PCA)压缩这 13 个特征。PCA 在中心化后的数据上寻找方差最大的正交方向;载荷(loading)表示原始特征对某个主成分的贡献,解释方差比例(explained variance ratio)表示该主成分占总方差的比例。需要注意,PCA 默认会对特征做均值中心化(centering),但不会自动统一不同特征的量纲和方差尺度[4]。
“PC1 解释了 99.8% 的总方差”,是那种很容易被放进汇报标题的结果。它听起来像是 PCA 找到了数据中最重要的结构,但这里的“重要”首先只是方差更大。问题是,这个方差来自多维结构,还是来自某个数值尺度特别大的特征?
第 \(j\) 个主成分的解释方差比例为其特征值占总特征值之比:
\[ \mathrm{EVR}_j = \frac{\lambda_j}{\sum_{r=1}^{p} \lambda_r} \]其中 \(\lambda_j\) 是协方差矩阵的第 \(j\) 个特征值,\(p\) 是特征数。
上方 A 图直接对原始特征运行 PCA。PC1 虽解释了 99.8% 的方差,但载荷图显示,proline 的绝对载荷达到 0.9998,其余 12 个特征的载荷都很小。
下方 B 图先对 13 个特征分别进行标准化,再运行 PCA。这个案例只用于展示量纲效应,没有设置预测任务中的训练集与测试集。标准化后,PC1 解释 36.2% 的方差,载荷分布在多个特征上,包括黄酮类(flavanoids,0.42)、总酚(total_phenols,0.39)和稀释酒样光密度(od280/od315,0.38)。

当最大方差方向或二维布局被直接解释成主要业务结构时,误读就发生了。图中的方向本身没有错,问题是读者没有继续检查量纲、载荷和投影稳定性。
如果使用 t-SNE 或 UMAP 等非线性降维方法展示门店或设备数据,还要注意:这类算法主要保留局部邻域关系,二维图中的簇面积和簇间空隙不能直接代表高维空间中的距离。图形也可能随超参数(如 perplexity、n_neighbors)和随机种子变化。
怎样核对:在降维图中注明是否标准化,以及缩放参数用哪些样本拟合。使用 PCA 时,同时展示载荷和累计解释方差比例[5];使用 t-SNE 或 UMAP 时,不要直接根据图上的簇间距离下结论,必要时比较不同超参数和随机种子下的结果。
聚类:算法分区不等于天然分群 #
这一组数据故意没有设置真实分群。它包含 450 个样本,每个样本有 8 个相互独立的特征,各特征都服从均值为 0、标准差为 1 的正态分布。换句话说,数据只是一团连续的八维标准高斯云,没有预设类别,也没有簇标签。
如果团队仍然要求算法把它分成三组,KMeans(n_clusters=3) 就会在原始八维空间中给出三个簇。为了方便展示,我们再用 PCA 把样本投影到 PC1 和 PC2。KMeans 等划分式聚类算法会最小化簇内平方误差和(within-cluster sum of squares);因此,即使数据只是连续分布,只要指定 \(K=3\),算法仍会按 Voronoi 边界划出三个区域,并给每个样本分配一个标签。
问题在于,一旦散点被涂成三种颜色,人眼就会主动寻找三类群体。边界清晰、互不重叠的彩色区块尤其容易让人相信,数据中天然存在“高、中、低”三类客群。下图三幅图依次展示未着色的样本、着上 KMeans 标签的同一批样本,以及各簇的样本级轮廓系数(Silhouette coefficient)。
颜色告诉我们算法如何分组,却没有告诉我们这些组分得是否清楚。轮廓系数正是用样本到本簇和其他簇的距离,衡量当前分组是否合理。对样本 \(i\),令 \(a_i\) 为它到同簇其他样本的平均距离,\(b_i\) 为它到其他各簇的平均距离中的最小值,则:
\[ s_i = \frac{b_i - a_i}{\max(a_i, b_i)} \]轮廓系数的范围是 \([-1, 1]\)。接近 1 表示样本与本簇更接近、与相邻簇更远;接近 0 表示它位于两个簇的交界附近;小于 0 则说明它可能更接近另一个簇。
常见的轮廓图以横轴表示轮廓系数,每条横条对应一个样本。样本先按簇分组,再在组内排序;横条越靠右,当前分组越清晰。每组占据的纵向范围反映的是簇的样本量,不是簇的质量。全局平均线可辅助比较不同簇数,但不能单独决定簇数;还要看各簇的分布、负值样本和业务可解释性。
本例的全局平均轮廓系数约为 0.09,三个簇中的大量样本都靠近零值基线。这说明二维投影上的三种颜色并不意味着数据中存在三类天然群体。

颜色确实对应算法给出的标签,但标签不等于自然群体。这里的误读,是把 KMeans 按照指定簇数完成的分区,当成数据本来就具有的结构。
也要注意轮廓系数的适用边界。低分说明数据在当前特征集、欧氏距离和 KMeans 的簇形状假设下没有形成紧密且彼此分开的簇,但不能排除数据在其他距离度量或局部流形中存在结构。
怎样核对:将聚类散点图标为“低维投影”,不要仅凭颜色分区就断定存在真实群组。报告各簇样本量、样本级轮廓系数分布和全局均值[6];再通过重采样或更换随机种子,计算调整兰德指数(Adjusted Rand Index,ARI),检查分组是否稳定。
2.2 模型与评估:别让总分遮住差异 #
守住数据边界,只能保证模型没有在起跑线上“提前看答案”。误读并不会因此结束:即使输入和训练流程都正确,一个总体分数仍可能遮住真正影响决策的错误。
接下来要拆开的,正是准确率、\(R^2\) 和交叉验证均值背后的差异:类别不平衡下的错误代价、局部残差结构,以及不同验证折之间的波动。
分类:高准确率可能掩盖高代价错误 #
回到开篇的 500 个设备周期:其中 436 个正常,64 个异常。这里暂时不训练端到端模型,只比较两组预先设定的预测结果,把类别不平衡和错误代价的影响单独拆出来看。
在这组数据上,一个准确率高达 87.2% 的方案,为什么反而可能成为更不合适的上线选择?答案不在准确率的计算过程,而在它把哪类错误藏在了平均数里。
要看清准确率隐藏了什么,可以先把它拆开。设测试集中正类(异常故障)占比为 \(\pi\),负类(正常周期)占比为 \(1-\pi\);模型的真阳性率(true positive rate,TPR;本例中即异常召回率 recall)为 \(\mathrm{TPR}\),真阴性率(true negative rate,TNR;本例中即正常识别率 specificity)为 \(\mathrm{TNR}\)。那么:
\[ \mathrm{Accuracy} = \pi \cdot \mathrm{TPR} + (1 - \pi) \cdot \mathrm{TNR} \]在这 500 个周期中,异常比例为 \(\pi=12.8\%\),正常比例为 \(1-\pi=87.2\%\)。因此,一个始终预测“正常”的多数类基线(majority-class baseline)即使完全识别不出故障(\(\mathrm{TPR}=0\)),仍会因为识别了所有正常周期(\(\mathrm{TNR}=1\))而得到 87.2% 的准确率。这个分数掩盖了它漏掉全部 64 次故障的事实(\(\mathrm{FN}=64\))。
上方 A 图只比较两套方案的准确率,多数类基线以 87.2% 排名第一。下方 B 图则展示计数混淆矩阵,并按假设的代价比(漏检一次成本 \(c_{\mathrm{FN}}=5\),误报一次成本 \(c_{\mathrm{FP}}=1\))计算加权误判成本。
预警模型产生 53 次误报(\(\mathrm{FP}=53, \mathrm{TN}=383\)),识别出 44 次故障(\(\mathrm{TP}=44\)),漏掉 20 次(\(\mathrm{FN}=20\)),准确率为 \((383+44)/500=85.4\%\)。按同一代价设定,多数类基线的成本为 \(64\times5=320\),预警模型的成本为 \(20\times5+53\times1=153\),下降了 52.2%。

准确率回答了“预测对了多少”,却没有回答“错误由谁承担、代价多大”。把前一个问题的答案直接当成业务价值,才是这张图真正容易引发的误读。
当正类比例低至 0.1%~1%(如罕见晶圆缺陷或金融欺诈)时,ROC 曲线(Receiver Operating Characteristic curve)在低假阳性率区域未必能清楚区分实际采用的工作点(operating point)。假阳性率(false positive rate,FPR)为 \(\mathrm{FPR}=\mathrm{FP}/(\mathrm{FP}+\mathrm{TN})\);当真阴性样本很多时,即使误报数不低,FPR 也可能很小。
PR 曲线把两个与正类有关的量放在一起比较:
\[ \begin{aligned} \mathrm{Precision} &= \frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FP}}, \\ \mathrm{Recall} &= \frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FN}} . \end{aligned} \]查准率衡量发出的告警中有多少确实是故障,因此会直接受到误报数量影响;查全率衡量真实故障中有多少被模型发现,因此会直接受到漏报数量影响。
类别不平衡并不会自动抬高 ROC AUC,但即便 AUC 较高,低正类比例下的查准率仍可能很低。因此,评估少数类任务时,应查看查准率—查全率曲线(Precision–Recall,PR)及平均精确率(Average Precision,AP)[7],并结合业务阈值下的误报数量评估告警负担。
怎样核对:同时报告正负类样本数和混淆矩阵原始计数。类别不平衡时,优先查看 PR 曲线,并标出等于正类比例 \(\pi\) 的随机基线;不能只看准确率或 ROC 曲线。若错误代价不对称,只用训练集折外预测(out-of-fold predictions)或独立验证集选择概率阈值,不要根据测试集结果反复调阈值。
回归:高 R² 也可能掩盖局部偏差 #
本例模拟一个连续负荷预测任务:用单个运行指标 \(x\) 预测设备负荷 \(y\)。训练集有 300 个样本,独立测试集有 220 个样本,\(x\) 在 10 到 90 之间均匀取值。负荷总体随 \(x\) 上升,但关系并不是一条直线,而是带有轻微弯曲和随机噪声。
为了便于复核,数据按 \(y=20+1.8x+0.0285(x-50)^2+\varepsilon\) 生成,其中 \(\varepsilon\sim N(0,6.2^2)\)。我们故意用一元普通线性回归去拟合它:模型能抓住总体上升趋势,却无法完整表示弯曲关系。
工程师说模型总在峰值处漏估,评估报表却显示测试集 \(R^2\) 高达 0.88。两边都可能没有说错:散点整体贴近对角线,并不代表每个负荷区间都同样可靠。
回归任务中的决定系数(coefficient of determination,\(R^2\))和平均绝对误差(mean absolute error,MAE)可分别写为:
\[ \begin{aligned} R^2 &= 1-\frac{\sum_{i=1}^{n}(y_i-\hat{y}_i)^2} {\sum_{i=1}^{n}(y_i-\bar{y})^2}, \\ \mathrm{MAE} &= \frac{1}{n}\sum_{i=1}^{n}\lvert y_i-\hat{y}_i\rvert . \end{aligned} \]其中,\(n\) 是测试样本数,\(\bar{y}\) 是测试集真实值的均值。\(R^2\) 衡量模型相对于均值基线的拟合表现,MAE 则概括绝对误差的平均大小。这两个指标都会把全部样本的误差汇总成一个数字,因此无法说明误差集中在哪个负荷区间,也不能单独揭示局部的系统性偏差。
在下图的连续负荷预测实验中,普通线性回归在独立测试集(\(n=220\))上的 \(R^2=0.88\),\(\mathrm{MAE}=13.1\)。上方 A 图对比真实值 \(y\) 与预测值 \(\hat{y}\),整体上看,数据点靠近 \(y=\hat{y}\) 的对角线,容易让人以为线性模型已经足够。但按照 PredictionErrorDisplay 的思路[8],下方 B 图改为绘制残差 \(e=y-\hat{y}\),并叠加局部平滑线,便能看到明显的 U 形趋势:
- 在中等负荷预测区间(\(\hat{y} \in [80, 140]\)),残差持续小于零(\(y - \hat{y} < 0\)),说明模型在该区间系统性高估;
- 在高负荷峰值端(\(\hat{y} > 160\)),残差持续大于
+15(\(y - \hat{y} > 0\)),说明模型在极值区域严重低估(漏估)。
残差随预测值呈现弯曲趋势,说明线性模型可能遗漏了非线性关系,例如二次项(\(x^2\))。这种图形能提示模型设定偏误(model misspecification),但单凭残差图还不能断定真实过程一定由二次项驱动。

\(R^2\) 对整体拟合的概括没有错,但它不能说明每个区间都同样可靠。误读来自把整体拟合度延伸成“所有区间都可靠”,从而忽略高负荷区域的系统性偏差。
时间序列回归(如门店周销量或轴承温度)还可能出现“折线贴合”的假象。若序列有较强自相关,仅把上一期真实值用作本期预测的滞后一期朴素基线(Naive baseline,\(\hat{y}_t=y_{t-1}\)),画出的预测曲线也可能与真实值很接近。应将它与模型放在同一评估中比较;有周期性时,还要加入季节朴素基线(Seasonal Naive),并按预测步长检查误差,例如使用 MASE。否则,模型可能只是跟随最近的观测值,并没有提供有效的提前量。
怎样核对:在独立测试集上并列展示“真实值—预测值”和“残差—预测值”,标出 \(e=0\) 基线并添加局部平滑线[8]。再按关键业务维度(如高负荷区间、促销期或设备批次)比较残差。时间序列任务应纳入朴素基线;有周期性时,再与季节朴素基线比较,并按预测步长(horizon)拆解误差。
模型选择:平均分第一不等于稳定领先 #
残差图会告诉我们一个模型在哪些区间出错;当候选模型变多时,排行榜又会隐藏另一层不确定性。
本例使用后文完整案例的同一套端到端合成数据:1,500 个训练周期、500 个独立测试周期,两部分数据的异常比例均为 12.8%。每个周期包含 8 个与异常机制有关的核心特征和 256 个噪声特征,不同特征的数值尺度也不一致。本小节先只回答一个问题:怎样用训练集的五折交叉验证,从五个候选值中选择逻辑回归的正则化参数 \(C\)。
本文的 Notebook 使用 scikit-learn 1.9.1,没有显式设置求解器、正则化类型、类别权重或样本权重。因此,LogisticRegression 在本例中采用默认的 lbfgs 求解器和 L2 正则化,每个样本的权重均为 1。在这些条件下,每次模型拟合所求解的目标可等价写为[9]:
其中,\(x_i\) 是经过当前训练折内 StandardScaler 变换后的特征,\(y_i\in\{0,1\}\) 是类别标签,\(w\) 是特征系数,\(w_0\) 是截距,\(n\) 是本次拟合使用的训练样本数。L2 正则化只作用于 \(w\),不作用于截距 \(w_0\)。在本例的五折交叉验证中,每次拟合使用其中四折,即 \(n=1{,}200\);锁定 \(C\) 并使用全部训练数据重新拟合时,\(n=1{,}500\)。
从公式可以直接看出,\(C\) 是逆正则化强度(inverse regularization strength):\(C\) 越小,正则化项前的系数 \(1/(2nC)\) 越大,模型系数受到的收缩越强。scikit-learn 的 lbfgs 实现先计算样本权重总和 sw_sum,再设置 l2_reg_strength = 1.0 / (C * sw_sum)[10];本例没有设置权重,因此 sw_sum 就是 \(n\)。上式是这一实现对应的等价数学表达,而不是对源码语句的逐行翻译。如果使用类别权重、样本权重或其他正则化类型,目标函数也需要相应调整。
排行榜最容易让人忘记:每个均值背后,都有几次结果并不完全一致的验证。五折交叉验证(five-fold cross-validation,5-fold CV)平均分排名第一,只能说明它在这五折上的均值最高,不能保证换一批样本或上线后仍然领先。
K 折交叉验证会将训练集分成 \(K\) 份,轮流用其中一份验证、其余部分训练。因此,五折验证会为每个候选方案产生五个分数,且不同方案的同一折分数可以配对比较。若只把五折均值画成柱状图,再截短纵轴放大差异,就容易造成两种误读:
- 忽略折间波动和配对关系:方案 A 与 B 的五折均值可能只差
0.007,而单个方案的折间标准差达到0.025。没有同折得分对照,就难判断这点差异是否稳定,还是由某一折的数据偶然拉高。 - 把验证分数当成泛化表现:从多个候选超参数中挑出验证均值最高者,本身就是一次选择。最高分会带有选择偏差,通常会高估模型的泛化表现;因此还要用未参与选参的独立测试集评估[11]。
下图比较了产线异常预警模型在五个 \(C\) 候选值 \(\{0.01, 0.03, 0.1, 0.3, 1.0\}\) 下的五折交叉验证结果。
上方 A 图截短纵轴,五个均值分别为 0.784、0.831、0.859、0.866 和 0.862;柱体高度因此会放大候选方案之间的差距。
下方 B 图恢复各折配对得分和 \(\pm1\,\mathrm{SD}\) 波动带,也标出选定 \(C=0.3\) 后的独立测试集得分。单折分数约在 0.740~0.901 之间波动,明显大于 \(C=0.3\) 与 \(C=1.0\) 之间约 0.004 的均值差;独立测试集 ROC AUC 为 0.841,也低于选定候选的五折均值 0.866。

最高验证均值是选参证据,不是稳定领先的证明,更不是最终泛化能力。把这三种判断压缩成排行榜上的一个名次,才会让很小的均值差异显得比实际更确定。
此外,在诊断模型容量时,切忌只画“训练集得分 vs. 测试集得分”两根孤立的柱子。配合随训练样本量变化的学习曲线(learning curve)和随复杂度超参数变化的验证曲线(validation curve),才能看清当前瓶颈究竟是训练与验证曲线双双偏低(欠拟合,需补充有效特征或提升模型容量),还是训练分数持续高于验证分数,而且两者的差距始终没有收窄(过拟合,需加强正则化或扩充样本)[11]。
怎样核对:展示多模型或多超参数对比时,保留每一折的配对得分连线与 \(\pm 1\,\mathrm{SD}\) 离散带,不单独使用截断纵轴的均值柱状图;明确交代交叉验证采取了分层(StratifiedKFold)、按实体分组(GroupKFold)还是时间滚动(TimeSeriesSplit)切分;超参数一旦根据验证折选定,就只在全程封存的独立测试集上进行一次最终评估,绝不根据测试集分数回头修改候选集。
三、图表交付前:既检查表达,也检查证据 #
走完前面的六个案例,可以看到,误读并不总是从错误计算开始。有时,问题早已藏在实验边界或指标选择里;有时,截断的坐标轴、未经解释的颜色,或者只展示均值的排行榜,又把差异进一步放大。
如果要把图表交给另一个人作决定,最好做两轮检查:先看图有没有在视觉上夸大结论,再看图背后的实验是否经得起追问。下面四条底线检查表达,后面六个问题检查证据。
3.1 四条图表表达底线 #
- 颜色只编码可以直接比较的量:混淆矩阵应说明色阶表示原始计数还是归一化比例。准确率、延迟和模型体积等方向与单位不同的指标,不应共用一套原始数值热力图;可以改用方向一致的标准化得分或分面克利夫兰点图(Cleveland dot plot)。同时采用色盲友好配色,不把红绿作为唯一的优劣信号[12]。
- 柱状图保留零点基准:读者会自然地用柱体高度判断差异。若纵轴只展示
[0.77, 0.88],0.784与0.866的差距就会被明显放大。需要比较小幅差异时,可以改用标注具体数值和误差范围的点图或折线图[13]。 - 排名之外,还要展示波动:榜单第一和第二可能相差 15 个百分点,也可能只差
0.001。只要结果来自多个交叉验证折、随机种子或时间窗口,就应同时呈现均值与离散度,避免把抽样波动包装成稳定领先。 - 先检查工程红线,再讨论综合最优:推理延迟、内存配额和最低故障召回率等约束,决定了模型是否具备上线资格。即使某个模型的平均分高出
0.005,只要突破产线周期限制,就不应再用综合评分掩盖它在工程上不可行。
这四条规则管的是图表怎么说,下面六个问题管的是实验凭什么这样说。前者避免视觉放大,后者避免证据越界。
3.2 六问:实验依据是否充分 #
| 检查层级 | 自检问题 | 通过标准(满足才可交付) |
|---|---|---|
| 数据边界 | 1. 变换与统计量是否严守训练集边界? | 所有 Scaler、Imputer、PCA 均封装在 Pipeline 内,仅对训练折调用 fit,无全量预处理或未来字段泄漏。 |
| 切分策略 | 2. 数据切分是否还原了真实上线场景? | 时序数据采用时间点切分,多周期同一设备/门店数据采用分组切分,未用随机打乱掩盖分布漂移。 |
| 表示结构 | 3. 降维与聚类图是否附带了量纲和分离度证据? | PCA 注明标准化前提并附载荷分布;聚类投影图附样本级 Silhouette 分布及均值,未把算法分割当天然分群。 |
| 代价对齐 | 4. 分类评估是否匹配正类比例与错误代价? | 偏斜数据同时报告正类比例 \(\pi\)、PR 曲线与四格计数混淆矩阵;决策阈值由训练集折外预测结合业务成本矩阵选定。 |
| 残差基线 | 5. 回归评估是否检验了残差结构与朴素基线? | 并列展示“实际—预测”与“残差—预测”图以排查非线性偏置;时序预测至少包含滞后一期或季节朴素基线对照。 |
| 选参隔离 | 6. 验证选参证据与最终测试集是否彻底隔离? | 超参数比较保留多折配对轨迹与误差带;独立测试集只在锁定全部模型与阈值后解封并评估一次。 |
四、完整案例:怎样评审设备异常预警系统 #
这些规则单独看都不复杂,难的是让它们在同一次评审中同时成立。前面的分类图使用固定计数,只为解释准确率与错误代价;模型选择小节的交叉验证图和下面的综合看板则来自同一套端到端合成实验。现在,我们沿着团队作出上线判断的顺序,把训练、选参、定阈值和测试完整走一遍。综合看板的测试集计数不会复现前面的固定示例,但会与模型选择小节的选参结果保持一致。
这里沿用模型选择小节的数据:1,500 个训练周期和 500 个独立测试周期,正常与异常比例均为 87.2% : 12.8%(训练集 1,308 : 192,测试集 436 : 64)。每个周期包含 8 个与异常机制有关的核心特征,例如温度、压力、共模振动、差分振动和高频通道,另外还有 256 个与目标无关的噪声特征。不同特征采用不同数值尺度,这一设置也要求预处理和正则化始终留在正确的评估边界内。
这个案例不是要证明正则化逻辑回归优于深度学习模型,而是要展示一套纵向六面板诊断看板如何逐步建立证据链:先锁定数据边界,再选择模型和阈值,最后才解封测试集,结算错误代价。
先锁定评估边界(面板 ①):团队先明确哪些数据用于训练,哪些数据必须留到最后。本例包含 1,500 个训练周期和 500 个独立测试周期,两组样本遵循同一套生成机制,正类均占
12.8%。图表同时公开绝对计数和87.2%的多数类基线。真实产线数据还应按时间留出未来测试期;如果要评估对未见设备的泛化能力,则应按设备分组隔离。选择模型,但暂时不碰测试集(面板 ②):接下来,团队只在训练集内部比较候选参数。
StandardScaler与LogisticRegression被放进同一条Pipeline,并通过五折分层交叉验证比较五个正则化参数。各候选值保留同折得分,最终选出验证均值约为0.866的 \(C=0.3\)。真实时序数据应改用滚动时间验证,未见设备的泛化评估则应采用分组验证。把业务代价转换成决策阈值(面板 ③):假设模型输出的是已经校准的故障概率 \(p\),而且正确分类的成本为零。此时,发出告警的预期成本为 \(c_{\mathrm{FP}}(1-p)\),不发出告警的预期成本为 \(c_{\mathrm{FN}}p\)。当前者不高于后者时,理论上就应发出告警:
\[ \begin{aligned} c_{\mathrm{FP}}(1-p) &\le c_{\mathrm{FN}}p, \\ \tau^* &= \frac{c_{\mathrm{FP}}}{c_{\mathrm{FP}}+c_{\mathrm{FN}}} = \frac{1}{1+5} = \frac{1}{6}\approx 0.17 . \end{aligned} \]因此,当漏检一次故障的代价是误报一次巡检的 5 倍时,理论阈值会从默认的
\[ \widehat{R}(\tau) = \frac{c_{\mathrm{FN}}\,\mathrm{FN}(\tau) + c_{\mathrm{FP}}\,\mathrm{FP}(\tau)}{N} \]0.50下移至 \(1/6\) 附近。这个结果依赖前面的校准与成本假设,并不直接决定实际阈值。为了适应本例的概率分布,我们使用训练集五折交叉验证产生的折外预测概率(out-of-fold probabilities),按下式在[0.05, 0.85]区间扫描阈值:其中 \(N\) 是折外预测样本数,\(\mathrm{FN}(\tau)\) 与 \(\mathrm{FP}(\tau)\) 是阈值 \(\tau\) 下的漏报与误报数。折外预测的经验成本扫描最终在不触碰测试集的前提下锁定 \(\tau = 0.18\)。
图中为了与测试集规模对应,将折外平均代价换算为每 500 个周期的预期成本;这一线性换算不会改变最优阈值。
解封测试集,核验实际工作点(面板 ④):模型和阈值都固定以后,团队才对 500 个封存测试周期进行一次预测。测试集 ROC AUC 为
0.841,平均精确率(AP)为0.585;在 \(\tau=0.18\) 下,查准率为0.468,异常召回率为0.562。最后结算误报、漏报和总成本(面板 ⑤ & ⑥):校准曲线用于检查前面的校准假设是否大致合理,但测试集只有 64 个异常样本,不能仅凭这条曲线证明模型已经充分校准。最终混淆矩阵为
TN = 395、FP = 41、FN = 28、TP = 36。模型准确率为86.2%,仍低于多数类基线的87.2%;加权误判成本却从320降至181,降幅为43.4%。

综合看板得到的计数与前面固定混淆矩阵不同,却支持同一个判断:准确率本身不能决定哪类错误更值得避免。在异常比例为 12.8%、阈值 \(\tau=0.18\)、漏检与误报成本比为 5:1 的条件下,端到端模型的准确率略低于多数类基线,加权误判成本却更低。如果故障代价或异常率变化,团队就应重新评估成本与阈值,而不是沿用这次结论。
五、结语:别让图表替你作决定 #
回头看这六个案例,问题都不是数字算错了,而是局部证据承担了它无法承担的结论。准确率只能概括预测是否命中,不能替团队决定漏检和误报谁更昂贵;二维投影只能展示某种表示,不能证明业务群体天然存在;交叉验证均值可以帮助选参,也不能替代独立测试。
所以下次看到一张“表现很好”的图,不妨先追问三件事:它使用了什么评估边界?图中的颜色、距离或分数究竟代表什么?如果据此采取行动,哪类错误会由谁承担?这三个问题往往比再增加一个综合评分更有价值。图表的作用不是替结论增添说服力,而是让决策者看清证据及其适用边界。
实验看板和模型报告越来越自动化,图表也会生成得更快、更整齐。但团队真正需要保留的,不只是最终图片,还包括产生图片的预测明细、切分规则、阈值依据和成本假设。只有这些信息仍可复核,图表才是在帮助团队判断,而不是替团队制造确定感。
如果你只想掌握判断框架,读到这里已经足够;下面的附录用于对照代码和常见数据结构。
附录:六类任务的常见数据形状 #
为了便于对照代码和 Notebook,下表汇总了 scikit-learn 六类任务中常见的数据形状。这里,\(n\) 表示样本数,\(p\) 表示原始特征数,\(p'\) 表示变换后的特征数,\(G\) 表示类别数,\(K\) 表示簇数,\(k\) 表示保留的降维维度,\(o\) 表示回归输出维度,\(m\) 表示候选参数组数,\(F\) 表示交叉验证折数。
| 层次 | 工作类型 | 常见输入或输出张量形状 | 常见汇总指标与诊断对象 |
|---|---|---|---|
| 数据与表示 | 预处理 | 原始矩阵 (n, p);变换后矩阵 (n, p');每列拟合统计量 (p,) | 无独立预测得分;需对比训练集与测试集变换前后的分位数、缺失率与零点偏移 |
| 数据与表示 | 降维 | 低维坐标 (n, k);PCA 载荷矩阵 (k, p);解释方差比例 (k,) | 累计解释方差比、各主成分载荷分布、重构误差或不同超参数下的邻域保持度 |
| 数据与表示 | 聚类 | 簇标签 (n,);簇中心 (K, p);样本级轮廓系数 (n,) | 平均轮廓系数、各簇样本量向量 (K,)、多随机种子或重采样下的 ARI 稳定性 |
| 模型与评估 | 分类 | 真实与预测标签 (n,);正类概率或决策分数 (n,);多分类概率 (n, G) | accuracy、ROC AUC、AP 等标量;PR 曲线与校准曲线序列;混淆矩阵 (G, G) |
| 模型与评估 | 回归 | 真实目标与预测值 (n,);多输出预测 (n, o);逐样本残差 (n,) | MAE、RMSE、\(R^2\) 等标量;残差对预测值散点、局部平滑偏差线与分位数误差 |
| 模型与评估 | 模型选择 | 候选数 × 交叉验证折数的得分矩阵 (m, F);cv_results_ 字典 | 配对折得分矩阵、折间均值与标准差、拟合耗时、学习曲线及独立测试集单次得分 |
scikit-learn 提供的评估与可视化接口,如 ConfusionMatrixDisplay、PrecisionRecallDisplay、CalibrationDisplay 和 PredictionErrorDisplay,可以把样本级预测结果转换为诊断图[14]。工程上应保存预测明细和折外概率,再根据具体决策问题绘图;这比只记录一个 best_score_ 更有助于复核模型表现。
参考文献 #
[1] scikit-learn developers. scikit-learn: Machine Learning in Python. https://scikit-learn.org/stable/
[2] scikit-learn developers. Getting Started: Transformers and pre-processors. https://scikit-learn.org/stable/getting_started.html
[3] scikit-learn developers. sklearn.preprocessing.scale. https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.scale.html
[4] scikit-learn developers. sklearn.decomposition.PCA. https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html
[5] scikit-learn developers. Importance of Feature Scaling. https://scikit-learn.org/stable/auto_examples/preprocessing/plot_scaling_importance.html
[6] scikit-learn developers. Selecting the number of clusters with silhouette analysis on KMeans clustering. https://scikit-learn.org/stable/auto_examples/cluster/plot_kmeans_silhouette_analysis.html
[7] T. Saito and M. Rehmsmeier. The precision-recall plot is more informative than the ROC plot when evaluating binary classifiers on imbalanced datasets. PLOS ONE, 10(3): e0118432, 2015. https://doi.org/10.1371/journal.pone.0118432
[8] scikit-learn developers. Visual evaluation of regression models. https://scikit-learn.org/stable/modules/model_evaluation.html#visual-evaluation-of-regression-models
[9] scikit-learn developers. Logistic regression. scikit-learn 1.9.1 User Guide. https://scikit-learn.org/1.9/modules/linear_model.html#logistic-regression
[10] scikit-learn developers. sklearn.linear_model._logistic source code. scikit-learn 1.9.1. https://github.com/scikit-learn/scikit-learn/blob/1.9.1/sklearn/linear_model/_logistic.py#L466-L580
[11] scikit-learn developers. Validation curves: plotting scores to evaluate models. https://scikit-learn.org/stable/modules/learning_curve.html
[12] F. Crameri, G. E. Shephard, and P. J. Heron. The misuse of colour in science communication. Nature Communications, 11, 5444, 2020. https://doi.org/10.1038/s41467-020-19160-7
[13] Office for National Statistics. Simple comparisons: Bar chart. Data visualisation service manual. https://service-manual.ons.gov.uk/data-visualisation/chart-types/bar-charts
[14] scikit-learn developers. Metrics and scoring: quantifying the quality of predictions. https://scikit-learn.org/stable/modules/model_evaluation.html