监督学习性能评估:交叉验证、分类指标、多分类与 ROC 曲线

Hyacehila

模型评估方法

误差与过拟合

把学习器的实际预测输出与样本的真实输出之间的差异称为误差(error)。学习器在训练集上的误差称为训练误差(training error),也叫经验误差(empirical error);在新样本上的误差称为泛化误差(generalization error)。

我们希望得到泛化误差小的学习器,但我们只能尽量去寻找接近泛化误差最小的学习器,具体怎么做,后面会介绍。

这时候就不得不提过拟合和欠拟合。当学习器把训练样本学得太好时,很可能把训练样本自身的一些特点当成了所有潜在样本都会具有的一般性质,导致泛化性能下降,这种现象称为过拟合(overfitting)。与它相对的是欠拟合(underfitting),指对训练样本的一般性质还没有学好。

下面用一个例子说明过拟合。已知数列 31, 28, 31, 30, 31,这其实是一月到五月每个月的天数。要预测下一个数,简单的想法是认出这是月份天数:六月有 30 天。但我们也能找到一个四次多项式:

f(n)=23n4253n3+1093n21913n+66f(n)=\frac{2}{3}n^4-\frac{25}{3}n^3+\frac{109}{3}n^2-\frac{191}{3}n+66

它精确拟合了所有已知值,拿去预测六月、七月、八月,得到的却是 56、143、346 这样离谱的结果。这就是典型的过拟合:把训练样本学得太好,却完全无法泛化。

导致过拟合的因素很多,最常见的是学习能力过强,把训练样本中不太一般的特点也学进去了;欠拟合则通常由学习能力不足造成。

欠拟合比较好处理,比如在决策树学习中扩展分支、在神经网络学习中增加训练轮数等。过拟合相对要麻烦得多,后面会介绍各种处理方法;整个机器学习领域,处理过拟合都是重要的问题。

传统机器学习模型

现实任务中,往往有多种学习算法可供选择,即便用同一个算法,不同的参数配置也会产生不同的模型。该选哪个算法、用哪种参数配置?这就是机器学习中的模型选择(model selection)问题。

模型的参数分两类。一类在训练中自动学习,比如线性回归的系数、神经网络的权重,不需要人为干预;另一类必须在训练开始前手工指定,例如树模型的深度、正则化强度、学习率等,这类参数称为超参数(hyperparameter)。超参数不参与训练过程的学习,只能靠实验尝试来定,因此模型选择往往就变成”在若干超参数配置里挑一个”。下面的划分方法既可以直接评估最终模型,也可以用来在配置之间做选择——但后者会引入乐观偏差,这正是后面”嵌套交叉验证”和”乐观偏差与 Bootstrap 校正”两节要处理的问题。

理想的做法是直接研究泛化误差,可惜我们得不到泛化误差;而训练误差又不能用来衡量泛化性能。下面就来寻找模型评估的方法。

通常,我们通过实验测试来评估学习器的泛化误差并做出选择。为此,需要使用一个测试集(testing set)来测试学习器对新样本的判别能力,再把测试集上的测试误差(testing error)作为泛化误差的近似。测试集应尽可能与训练集互斥。下面介绍从数据集 DD 中划分训练集和测试集的几种方法。

留出法

留出法(hold-out)直接把数据集 DD 划分为两个互斥的集合,一个作为训练集 SS,另一个作为测试集 TT。在 SS 上训练出模型后,用 TT 评估其测试误差,作为泛化误差的估计。

需要注意,训练集和测试集的划分要尽可能保持数据分布一致,避免因划分引入额外偏差而影响最终结果,例如分类任务中至少要保证样本的类别比例相似。

使用留出法时,一般要做若干次随机划分,重复实验评估后取平均值,作为留出法的评估结果。

常见做法是用大约 2/3〜4/5 的样本训练,剩余样本用于测试。

交叉验证法

交叉验证法(cross validation)先把数据集 DD 划分为 kk 个大小相近的互斥子集,每个子集 DiD_i 都尽可能保持数据分布一致,即通过分层采样(stratified sampling)得到。

分层采样的目的是让每个子集的类别比例与整个数据集大致相同,避免某个折恰好只含某一类样本,导致单次评估严重失真。分类任务直接按类别比例分层即可;回归任务没有类别标签,通常先把连续目标值分成若干区间(分箱),再按区间比例分层。

然后,每次用 k1k-1 个子集的并集作为训练集,余下的那个子集作为测试集;这样共得到 kk 组训练/测试集,可以进行 kk 次训练和测试,最终返回 kk 个测试结果的均值。显然,交叉验证评估结果的稳定性和保真性在很大程度上取决于 kk 的取值,因此通常把它称为 k 折交叉验证(k-fold cross validation)。kk 最常用的取值是 10,即 10 折交叉验证;其他常用的还有 5、20 等。

kk 的取值体现的是偏差与方差的权衡:kk 越大,每折的训练集越大,评估偏差越小;但各折训练集重叠越多,折间结果的相关性越高,评估的方差也随之增大,同时要训练的模型更多、开销更大。k=10k=10 是实践中常用的折中,k=5k=5 在数据量小或模型较贵时更划算;数据量很小时还可考虑留一法,但它的方差偏高(见下文)。

与留出法类似,把数据集分成 kk 个子集也有多种划分方式。为减小划分不同带来的差别,kk 折交叉验证通常要随机使用不同的划分重复 pp 次,最终评估结果是这 ppkk 折交叉验证结果的均值,比如常见的 10 次 10 折交叉验证。

注意:即使已经确定了 kk,仍要进行多次交叉验证实验——单次 kk 折交叉验证只产生一个均值,换一种划分,结果可能差别不小。

重复 pp 次的另一个好处是得到性能分数的分布:除了均值,p×kp\times k 个折分数或 pp 个重复均值都可以用来刻画评估结果自身的波动,比如计算标准差。这组分数也正是后面”交叉验证 t 检验”一节里做成对假设检验的输入。

假定数据集 DD 包含 mm 个样本,若令 k=mk = m,就得到交叉验证法的一个特例:留一法(leave-one-out)。

留一法使用的训练集比初始数据集只少一个样本,因此在绝大多数情况下,留一法实际评估的模型与用 DD 训练出的期望模型很接近,评估结果往往被认为比较准确。

不过留一法也有两个明显的缺陷。其一是计算开销:数据集较大时,需要训练 mm 个模型,计算开销可能难以承受(例如数据集包含 1 百万个样本,就要训练 1 百万个模型),这还没算上算法调参的开销。其二是方差反而偏高,这一点比较反直觉:留一法虽然几乎无偏,但每次测试集只有一个样本、单次结果波动大,且各折训练集高度重叠、mm 个结果互相强相关,平均后的方差并不比折数适中的 kk 折交叉验证小。因此留一法在小样本上估计偏差有价值,但对不稳定算法(如决策树、k 近邻)或大数据集并不划算。

交叉验证用于模型选择:嵌套交叉验证

前面介绍的交叉验证都假设在固定划分上评估最终模型。但实际流程往往是先用交叉验证调超参数或选算法——在若干候选配置里挑出分数最高的一种。一旦这样做,评估结果就带上了乐观偏差:你在大量候选中挑了最好的那个,它的分数天然偏乐观,再用同一份数据评估它。

要得到调参后模型无偏的泛化误差估计,需要使用嵌套交叉验证(nested cross validation):外层把数据划分为若干折、逐折留作评估;内层在每一折的训练部分上再做一次交叉验证来选超参数。内层负责”选”、外层负责”评”,两层使用的数据完全隔离,得到的才是对调参后模型无偏的误差估计。

嵌套交叉验证的开销大得多(外层每折都要跑一遍完整的内层调参)。因此当模型只是用来做最终预测、并不追求对外宣称无偏的误差估计时,通常不必嵌套:直接内层交叉验证选好参数、再用全部数据训练即可。深度学习中更常见的做法是固定的训练/验证/测试三划分(见后文”深度学习模型的评估”一节),本质是把嵌套结构简化成单层。

非独立数据:分组与时间序列交叉验证

前面所有划分方法都隐含一个假设:样本相互独立,可以任意打乱分配。当这个假设不成立时,随机划分会泄漏信息——同一来源的样本被同时分进训练集和测试集,评估结果会虚高。常见两类情形:

  • 分组数据:同一受试者、同一设备或同一次实验会产生多个样本。此时应把整个组划到同一侧,保证同一组样本要么全在训练集、要么全在测试集,即分组交叉验证(group k-fold)。
  • 时间序列:样本按时间排序,未来信息不能用来预测过去。此时应使用按时间顺序前进的划分(前 tt 个时刻训练、t+1t+1 时刻测试),例如滚动预测,而不能随机打乱——否则相当于”偷看了未来”。

Bootstrap

除前面两种外,还有一种用得较少的评估方法。给定包含 mm 个样本的数据集 DD,对它采样产生数据集 DD':每次随机从 DD 中挑一个样本,拷贝放入 DD',再把它放回 DD,使它在下次采样时仍可能被采到;重复 mm 次。

通过自助采样,初始数据集中约有 36.8% 的样本不会出现在采样集 DD' 里。于是可以用 DD' 作为训练集,用未出现在 DD' 中的样本作为测试集。这样,实际评估的模型与期望评估的模型都用了 mm 个训练样本,而测试用的是数据总量约 1/3 的、没有在训练集中出现过的样本。这样的测试结果,也称包外估计(out-of-bag estimate)。

自助法在数据集较小、难以有效划分训练/测试集时很有用;此外,它还能从初始数据集中产生多个不同的训练集,对集成学习等方法有很大好处。

不过,自助法产生的数据集改变了初始数据集的分布,会引入估计偏差。因此,在初始数据量足够时,留出法和交叉验证法更常用。

这里只介绍了自助法作为数据划分与评估策略的用法;它更常见的身份是统计推断工具——用有放回重抽样估计任意统计量的方差、偏差与置信区间,以及与它同族的 Jackknife、Subsampling 方法,详见《统计推断的计算革命:详解 Jackknife, Bootstrap 与 Subsampling》。

乐观偏差与 Bootstrap 校正

前面用留出法、交叉验证拿到的都是”测试集上的误差”,已经和训练分开了。但还有一个更朴素的评估基准值得警惕:直接把训练集上的误差当作模型好坏的指标。这个误差叫表观误差(apparent error),也叫重代入误差(resubstitution error):它在训练数据上测”自己”,天然偏低。表观误差与真误差之间的差距就是乐观偏差(optimism):

optimism=E[表观误差真误差]>0\text{optimism}=\mathbb{E}\big[\text{表观误差}-\text{真误差}\big]>0

乐观偏差来自过拟合:训练时拟合得越用力,表观误差越好看,泛化误差却未必更好,两者之差就是乐观偏差。理想的做法是把它估计出来再加回去,即校正后误差 == 表观误差 ++ 乐观偏差。Bootstrap 校正(Efron 1983)的思路就是用重抽样把乐观偏差本身算出来:

  1. 在原始数据 DD 上拟合模型,记下表观误差 AA
  2. 有放回采样一个 bootstrap 样本 DD^{*},在 DD^{*} 上重新拟合模型(要重放全部建模步骤:选特征、调超参数都要在 DD^{*} 内重做,否则偏差被低估),得到 θ^\hat{\theta}^{*}
  3. DD^{*} 上算这个模型的误差(扮演”训练误差”),再在原始 DD 上算它的误差(扮演”测试误差”);
  4. 两者之差 Err(D,θ^)Err(D,θ^)\mathrm{Err}(D,\hat{\theta}^{*})-\mathrm{Err}(D^{*},\hat{\theta}^{*}) 就是本次乐观偏差的估计;
  5. 重复 BB 次(一般 B200B\ge 200)取平均 O^\hat{O},校正后的误差为 A+O^A+\hat{O}

直觉上,每个 bootstrap 样本都是原始数据的一个”超拟合版本”:模型在 DD^{*} 上的误差对应”超过拟合”,在原始 DD 上的误差对应”普通过拟合”,两者之差就模拟了”普通过拟合 − 无过拟合”,也就是乐观偏差本身。这也是为什么原始数据在这里可以兼任”测试集”——每个 bootstrap 样本都是从它生成的。

乐观校正还有一个广为人知的变体,把表观误差和包外误差加权平均:

Err^.632=0.368A+0.632ε(B)\hat{\mathrm{Err}}_{.632}=0.368\cdot A+0.632\cdot\varepsilon^{(B)}

其中 ε(B)\varepsilon^{(B)} 就是前文”Bootstrap”一节用 OOB 样本测出的误差(leave-one-out bootstrap)。表观误差 AA 太乐观,ε(B)\varepsilon^{(B)} 又太悲观——每个测试点平均只出现在约 63.2% 的 bootstrap 样本里,相当于只用约 63.2% 的数据训练;0.632 这个权重恰好来自 1e11-e^{-1}。过拟合严重时,再用 .632+(Efron & Tibshirani 1997) 加重 ε(B)\varepsilon^{(B)} 的权重。

与嵌套交叉验证的简单对比:两者修正的是同一类偏差,也都要求在重抽样内部重放全部建模步骤,但路径不同:

嵌套交叉验证 Bootstrap 乐观校正
偏差来源 用同一份数据调参+评估,在候选里挑最好 表观误差在训练数据上测自己
修正方式 外层折评估、内层折调参,数据隔离 重抽样估计乐观偏差,再回加
是否需要划出测试集 需要(外层折即测试集) 不需要,原始数据兼任测试集
代价 外层每折跑一遍内层调参,开销大 需要 B200B\ge 200 次完整建模
典型场景 要对误差对外声明、做模型选择 样本少、不愿浪费数据(如临床预测模型)

本节讨论的是评估结果的”偏差”,与之相对的”方差”问题,见下一节。

评估结果的偏差与方差

前面几节讲了各种评估方法,但一直没系统讨论”评估结果本身”的统计性质。评估方法输出的数字也是数据的一个函数,同样有偏差和方差,而且不同方法的偏差-方差结构差别很大。实践中很多看似矛盾的结论,其实是把这两种误差混着读了。

训练集评测与交叉验证站在偏差-方差平面的两个对角

偏差 方差
训练集(表观)评测 大:乐观偏差,方向固定偏低 小:用全量数据,无划分随机性
留出法 / 交叉验证 小:近似无偏 大:测试集只占一部分,且划分有随机性

这解释了很常见的一个现象:某个高容量模型(比如 XGBoost)在训练集上远好于其他模型,但在交叉验证下大家没差别。前者是偏差主导——训练集评测测的是”能背多少题”;后者是方差主导——交叉验证测的是”能泛化多少”,而小样本下各模型的分数全被淹没在同一片噪声里,谁也看不出来。

评估估计的方差可以拆成几层,每一层只能由对应的手段压掉:

  1. 数据本身:数据只是总体的一个样本,这部分噪声不可约,只能靠增加数据;
  2. 折划分的随机性:换一种划分,模型和结果都不同,靠重复交叉验证;
  3. 模型拟合的随机性:XGBoost 的 subsample、随机森林的列采样都让每次拟合有随机性;小样本下”差一点数据就换一个模型”,模型本身极不稳定,靠固定种子、多次拟合并平均、加强正则;
  4. 指标在超小测试集上的估计方差:这一层在不平衡数据上最致命。比例估计的方差约为 p(1p)/np(1-p)/n,分母实际上是少数类样本数——假设少数类共 30 个、做 5 折,每折测试集只有约 6 个正例,F1、AUC 在这样的测试集上取值是”跳格”的,单折分数的波动能到 ±0.1 以上,模型间的真实差异(可能只有 0.02~0.05)被彻底淹没。

还有一个隐蔽的方差来源:选择(winner’s curse)。在若干候选模型或超参数配置里挑分数最高的,等于取一堆带噪声估计的最大值,而最大值的期望会比真实最优值高出一截(独立同方差估计的期望最大值约为 σ2lnm\sigma\sqrt{2\ln m}mm 是候选数)。所以在训练集上选模型尤其危险。那是反向挑选乐观偏差最大的模型;即使在交叉验证下比较,只要调参用到了同一份数据,最好配置的分数也带着选择偏差,这正是前文”嵌套交叉验证”一节要剥掉的东西。

重复交叉验证压的是哪一层? 重复 pp 次主要压掉第 2 层,但各次重复共享训练数据,p×kp\times k 个分数并不独立,有效样本数远小于 p×kp\times k,方差压不到 1/pk1/\sqrt{pk}。要比较两个模型,应当让所有模型用同一组折做配对比较——折间的共同波动被差分掉,差异的方差远小于分别评估,这也是”交叉验证 t 检验”一节存在的理由。

降方差的实用清单

方差来源 应对手段
数据本身 只能增加数据
折划分 重复分层交叉验证,报告均值 ± 标准差
模型拟合随机性 固定种子、多次拟合并平均、加强正则化
指标估计 用连续指标(log-loss、Brier)替代阈值类指标;不平衡下看 PR-AUC 而非 Accuracy/ROC-AUC
调参/选择 嵌套交叉验证,把”选”与”评”的数据隔离

注:小样本下 log-loss 和 PR-AUC 不稳定,此时谨慎进行交叉验证,可能导致样本继续缩小而估计方差极大。一般建议此时考虑 Bootstrap 以控制方差。

一句话总结:训练集评测(低方差高偏差)和交叉验证(低偏差高方差)测的是不同的量,混着读才会得出”矛盾”的结论。评估一个模型时,先把”我要测的是什么、这个估计的偏差和方差有多大”想清楚——偏差可以校正(见上一节),方差只能靠实验设计降下来。

深度学习模型的评估

在深度学习中,我们把原始数据集直接划分为三个部分:

  • 训练集
  • 验证集
  • 测试集

训练集用于模型学习,验证集用于超参数调节,测试集用来测量模型的泛化性能、评估模型效果。

不要把验证集和测试集合二为一,否则可能产生过拟合(这实际上造成了信息泄露);更应当避免的是从训练集中选取测试集。

基本的工作流程为:训练集 → 模型训练 → 验证集 → 超参数调整 → 最终模型 → 测试集 → 最终性能评估。

从嵌套交叉验证的角度看,这个三划分其实是它的退化形式:验证集承担”内层选择”的角色、测试集承担”外层评估”的角色,但每一层只做一次、没有折。之所以不真正嵌套,是因为深度学习训练成本太高——训练一次模型就够贵,没法像传统机器学习那样在每一折里都完整跑一遍内层调参,能”炼丹”的次数有限,固定三划分是成本约束下的务实选择。如果数据量允许、又需要对外声明无偏的误差估计,仍可退回到前文”嵌套交叉验证”一节的嵌套做法。

各部分比例的控制:

  • 小样本(百万量级):60% 训练集、20% 验证集、20% 测试集
  • 大样本(千万量级):验证集和测试集数量足够即可,比如各固定 10 万
  • 超参数较少:适当缩减验证集,把样本让给训练集
  • 数据极少:用传统 ML 方法,如交叉验证(若调参后还需对外声明无偏的误差估计,用嵌套交叉验证,见前文)

多模型比较检验

直接比较性能度量指标的大小,恐怕不是一个好的判断模型好坏的办法:

首先,我们希望比较的是泛化性能,而实验评估得到的是测试集上的性能,两者的对比结果未必一致;

第二,测试集上的性能与测试集本身的选择关系很大。不同大小的测试集会得到不同结果;即便大小相同,包含的测试样例不同,结果也会不同;

第三,很多机器学习算法本身有一定随机性,即便用相同参数在同一测试集上多次运行,结果也可能不同。

那么,有没有合适的方法来比较学习器的性能呢?

统计假设检验(hypothesis test)为学习器性能比较提供了重要依据。基于假设检验结果,我们可以推断:若在测试集上观察到学习器 A 比 B 好,那么 A 的泛化性能在统计意义上是否真的优于 B,以及这个结论有多大把握。(用多个测试集的做法规避了前两点,用统计假设检验处理了第三点。)

下面先介绍两种最基本的假设检验,再介绍几种常用的机器学习性能比较方法。为便于讨论,本节默认以错误率为性能度量。

假设检验

多次实验会得到多个错误率观测值,而不是只有一个,这就为使用统计假设检验提供了基础。

假设学习器对每个样本犯错的概率为 pp。一次训练与测试中,我们已知测试集的大小和分错的个数,就可以用关于错误率 pp 的假设检验,判断学习器犯错的概率是否为某个给定值(对一次训练与测试做错误率假设检验)。

有时我们不止做一次留出法估计,而是通过多次重复留出法或交叉验证得到多个测试错误率,此时可以用 t 检验对平均错误率做估计和假设检验(多个错误率,本质上是均值的 t 检验)。

交叉验证 t 检验

更多时候,我们希望评估多个学习器之间的性能差别,此时应使用交叉验证 t 检验,也就是成对数据 t 检验:多个学习器对同一次训练与测试都给出了结果,这些数据是成对的。

需要提醒的是,这种检验在理论上并不严格成立:t 检验假设各组观测相互独立,但交叉验证各折的训练集互相重叠,折间结果并不独立,标准交叉验证 t 检验会低估方差、容易高估显著性。实践中常用 5×2 交叉验证(Dietterich 1998)等修正做法缓解:把数据随机对半划分,每份轮流训练一次、测试一次(一个重复内两折各测一次),重复 5 次得到 5 对分数,用这 5 对分数的配对差异构造近似 t 分布的统计量,减少重叠训练集带来的依赖。

McNemar 检验

对于两个算法、二分类的问题,可以列出每个算法的正误列联表;如果假设两个分类算法的性能没有差别,就可以用卡方检验来处理。

Friedman 检验与 Nemenyi 后续检验

它用于一次比较多个算法的整体表现,比交叉验证 t 检验方便,不需要两两分别做 t 检验。

分类模型的性能度量

前面介绍的是估计学习器性能的实验设计方法,现在还需要性能度量的指标。性能度量反映了任务需求。

对比不同模型的能力时,使用不同的性能度量往往得到不同的评判结果。这意味着模型的好坏是相对的:什么样的模型好,不仅取决于算法和数据,还取决于任务需求。

分类任务中,性能度量研究的是真实类别与预测类别之间的差异。

错误率与准确率

很自然地,可以给出下面两个指标来反映模型好坏:

  • 错误率是分类错误的样本数占样本总数的比例
  • 准确率是分类正确的样本数占样本总数的比例

容易看出两者之和为 1。不过这样的指标并不足够,还需要进一步研究。

基于列联表的度量

错误率和准确率虽常用,但满足不了所有任务需求。比如我们常关心“挑出来的目标里有多少是真正该挑的”,这就不能用前面的指标衡量,因此还需要其他指标。

分别根据真实类标签 DD 和预测类标签 RR 对样本进行划分,可以得到 k×kk\times k 的列联表,一般称为混淆矩阵 N(i,j)N(i,j),其中各位置元素为 nijn_{ij}。后面有二分类混淆矩阵的例子。

类别查准率(Precision)

分类器 MM 关于类别 cic_i 的查准率,定义为被预测为 cic_i 的样本中,预测正确的样本所占的比例:

preci=niimi\mathrm{prec}_i=\frac{n_{ii}}{m_i}

其中 mim_i 是分类器 MM 预测为 cic_i 类的样本数。

分类器的总体准确率统计所有预测正确的样本:

Accuracy=1ni=1knii\mathrm{Accuracy}=\frac{1}{n}\sum_{i=1}^k n_{ii}

这就是前文定义的准确率。需要注意,accuracy 与 precision 不是同一个指标。

类别查全率(Recall)

查全率衡量本应属于类别 cic_i 的样本中,被模型正确识别出来的比例:

recalli=niini\mathrm{recall}_i=\frac{n_{ii}}{n_i}

其中 nin_i 是真实类别为 cic_i 的样本数。

F1

分类器通常面临查准率和查全率之间的权衡,理想状况下我们希望两者都尽可能高。

每个类别的 F-measure 都试图平衡查准率和查全率,它是类别 cic_i 的 precision 与 recall 的调和平均:

Fi=21preci+1recalli=2precirecallipreci+recalli=2niini+miF_i=\frac{2}{\frac{1}{\mathrm{prec}_i}+\frac{1}{\mathrm{recall}_i}}=\frac{2\cdot\mathrm{prec}_i\cdot\mathrm{recall}_i}{\mathrm{prec}_i+\mathrm{recall}_i}=\frac{2n_{ii}}{n_i+m_i} FiF_i 的值越高,分类器就越好。

分类器 MM 的总体 F-measure 是各类 F-measure 的平均值:

F=1ki=1kFiF=\frac{1}{k}\sum_{i=1}^{k}F_i

二分类问题的混淆矩阵

二分类问题中,可以根据样例的真实类别与预测类别的组合,把样例划分为真正例(true positive)、假正例(false positive)、真反例(true negative)、假反例(false negative)四种情形。

令 TP、FP、TN、FN 分别表示对应的样例数,显然有 TP + FP + TN + FN = 样例总数。分类结果的混淆矩阵(confusion matrix)如下:

真实情况 预测结果:正例 预测结果:反例
正例 TP(真正例) FN(假反例)
反例 FP(假正例) TN(真反例)

错误率与准确率(二分类)

错误预测的比例,如前文“错误率与准确率”一节所述:

Error Rate=FP+FNn\mathrm{Error~Rate}=\frac{\mathrm{FP}+\mathrm{FN}}{n}

正确预测的比例就是准确率:

Accuracy=TP+TNn\mathrm{Accuracy}=\frac{\mathrm{TP}+\mathrm{TN}}{n}

查准率与查全率

二分类只是前面类别指标的特例。分别把正类和负类作为目标类别,其查准率为:

precP=TPTP+FP=TPm1;precN=TNTN+FN=TNm2\mathrm{prec}_{P}=\frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FP}}=\frac{\mathrm{TP}}{m_1};\quad\mathrm{prec}_{N}=\frac{\mathrm{TN}}{\mathrm{TN}+\mathrm{FN}}=\frac{\mathrm{TN}}{m_2}

通常讨论查准率与查全率的权衡时,默认关注正类。查准率要求挑出的正例尽可能准确,也就是宁可少选;查全率要求尽可能多地把真实正例挑出来,也就是宁可多选。

敏感性与特异性

敏感性就是正类的查全率,特异性则是负类的查全率:

TPR=recallP=TPTP+FN=TPn1\mathrm{TPR}=\mathrm{recall}_P=\frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FN}}=\frac{\mathrm{TP}}{n_1} TNR=specificity=recallN=TNFP+TN=TNn2\mathrm{TNR}=\text{specificity}=\mathrm{recall}_N=\frac{\mathrm{TN}}{\mathrm{FP}+\mathrm{TN}}=\frac{\mathrm{TN}}{n_2}

假阴性与假阳性

敏感性、特异性分别与 1 做差,可以得到假阴性率和假阳性率:

FNR=FNTP+FN=FNn1=1sensitivity\mathrm{FNR}=\frac{\mathrm{FN}}{\mathrm{TP}+\mathrm{FN}}=\frac{\mathrm{FN}}{n_1}=1-\text{sensitivity} FPR=FPFP+TN=FPn2=1specificity\mathrm{FPR}=\frac{\mathrm{FP}}{\mathrm{FP}+\mathrm{TN}}=\frac{\mathrm{FP}}{n_2}=1-\text{specificity}

综合性的度量方法

这里仍基于二分类的混淆矩阵。当存在类不平衡时,混淆矩阵中的绝对数可能不太有用,因此引入下面的方法,它们旨在给出更综合的分类模型性能评价。关于不平衡场景下的处理手段,参见样本不均衡的实践处理

PR 曲线

定义正类的查准率 PP 和查全率 RR

P=TPTP+FP,R=TPTP+FN.\begin{aligned}P&=\frac{TP}{TP+FP},\\\\R&=\frac{TP}{TP+FN}.\end{aligned}

很多情形下,可以根据学习器的预测结果对样例排序,排在前面的被认为是最可能的正例,排在最后的是最不可能的正例。让分类阈值从高到低移动,每个阈值都会得到一组 PPRR。以查全率为横轴、查准率为纵轴作图,就得到 P-R 曲线。

若一个学习器的 P-R 曲线被另一个学习器的曲线完全包住,就可以断言后者的性能优于前者。

P-R 曲线下方的面积通常记为 PR-AUC,可以把整条曲线汇总成一个数值。

F Measure

完全包住的情况很少,曲线交叉才是常态。如果需要比较某个分类阈值下的单个工作点,可以使用 F1 度量。这里沿用前文“F1”一节的记号:

F1=2×P×RP+R=2×TP样例总数+TPTNF_1=\frac{2\times P\times R}{P+R}=\frac{2\times TP}{\text{样例总数}+TP-TN}

它就是查准率与查全率的调和平均。

有些应用中,对 PPRR 的重视程度不同,因此还有广义的 F1 度量,称为 FβF_\beta

Fβ=(1+β2)×P×R(β2×P)+RF_{\beta}=\frac{(1+\beta^{2})\times P\times R}{(\beta^{2}\times P)+R} β\beta 用来调节两者的重要性:β>1\beta>1 时更看重查全率,0<β<10<\beta<1 时更看重查准率。它的本质是加权调和平均。

对于多分类问题,往往转化为多个二分类问题;此时有多个混淆矩阵,可以计算 TP、FP、TN、FN 的平均值,再计算 F1 度量。

ROC 与 AUC

很多学习器是为测试样本产生一个实值或概率预测,再把这个预测值与分类阈值(threshold)比较,大于阈值判为正类,否则判为反类。

这个实值或概率预测结果的好坏,直接决定了学习器的泛化能力。

因此,预测排序的质量体现了学习器在不同阈值下的期望泛化性能,ROC 曲线正是从这个角度研究泛化性能的有力工具。

与 PR 曲线类似,根据预测结果对样例排序,逐个把样本当作正例预测,每次算出两个关键量的值,分别以它们为横、纵坐标作图,就得到 ROC 曲线。

ROC 曲线的纵轴是真正例率(True Positive Rate,简称 TPR),横轴是假正例率(False Positive Rate,简称 FPR):

TPR=TPTP+FNFPR=FPTN+FP\begin{aligned}\text{TPR}&=\frac{TP}{TP+FN}\\\\\text{FPR}&=\frac{FP}{TN+FP}\end{aligned}

比较学习器时,与 P-R 图类似,若一个学习器的 ROC 曲线被另一个学习器的曲线完全包住,可以断言后者的性能优于前者;

若两条 ROC 曲线发生交叉,则难以一般性地断言谁优谁劣。此时若一定要比较,较合理的判据是比较曲线下的面积,即 AUC(Area Under ROC Curve):

AUC=12i=1m1(xi+1xi)(yi+yi+1)\mathrm{AUC}=\frac{1}{2}\sum_{i=1}^{m-1}(x_{i+1}-x_{i})\cdot(y_{i}+y_{i+1})

一般来说,AUC 的解释如下:

  • AUC = 0.5:表示模型没有分类能力,相当于随机猜测。
  • 0.5 < AUC < 0.7:表示模型有一定分类能力,但效果一般。
  • 0.7 ≤ AUC < 0.9:表示模型有较好的分类能力。
  • AUC ≥ 0.9:表示模型有非常好的分类能力。

ROC-AUC 是最常用的分类性能评价指标,很实用,对类别不均衡的情况也不敏感。

不平衡场景下的指标选择

类别分布严重失衡时,上述指标各有各的盲区,实践中的取舍要点如下:

指标 回答什么问题 在不均衡下何时会误导
Accuracy 总体预测对了多少 正例极少时会被多数类”冲高”
AUROC 正例是否整体排在负例前面 负例极多时可能看起来很好,但业务上仍会产生大量误报
PR 曲线 / AUPRC 预测为正的样本里有多少是真的,以及召回损失如何变化 基线随 prevalence 改变,跨数据集比较需报告先验
Balanced Accuracy 各类 recall 的平均值 不反映概率校准,也不反映误报成本
MCC 混淆矩阵四格整体是否平衡 仍无法替代 per-class 指标和阈值分析
Macro-F1 tail 类是否也被看见 对阈值和小类样本数敏感
Brier / ECE 概率输出是否可信 排序差的模型即便校准后也不一定可用

在强不均衡二分类里,PR 曲线往往比 ROC 更能体现实际压力:ROC 把大量真负例也算进来,很多模型会显得”挺不错”,而 PR 空间更直接地暴露误报成本。不过 PR 的基线受 prevalence 约束,跨数据集比较 AUPRC 必须同时报告正例比例。

多分类长尾任务里,Micro 指标会天然贴近 head class 的整体表现,容易把问题藏起来。要判断 tail class 是否真的被改善,至少要同时给出 macro 指标、per-class 指标、混淆矩阵以及 head/medium/tail 分层结果。更系统的处理手段见样本不均衡的实践处理

多分类学习

对于多分类问题,之前没有系统性地介绍过如何构建多分类模型,在 Logistic 回归里只简单提到了多项式 Logit。这里介绍最常用的思路:把多分类问题拆解成多个二分类问题来处理

One vs. One(OvO) 把 NN 分类数据集两两配对,产生 N(N1)/2N(N-1)/2 个分类器分别训练。预测时把新样本提交给所有分类器,得到 N(N1)/2N(N-1)/2 个二分类结果,最终结果通过投票产生。

One vs. Rest(OvR) 每次将一个类的样例作为正例、其余所有类的样例作为反例,训练 NN 个分类器。测试时若仅有一个分类器预测为正类,就以对应的类别作为最终结果;若有多个分类器预测为正类,则比较各分类器的预测置信度,取置信度最大的类别作为分类结果。

Many vs. Many(MvM)每次将若干个类作为正类、若干个其他类作为反类,OvO 与 OvR 都是它的特例。构造正反类划分最常用的技术是纠错输出码(Error Correcting Output Codes,简称 ECOC),它有一定的纠错能力。ECOC 分两步:

  • 编码:对 NN 个类别做多次划分,每次划分将一部分类别划为正类、一部分划为反类,从而形成一个二分类训练集;共产生 MM 个训练集,训练出 MM 个分类器。
  • 解码:MM 个分类器分别对测试样本预测,预测标记组成一个编码,与每个类别各自的编码比较,返回距离最小的类别作为最终预测结果。

一种常用的编码矩阵(coding matrix)形式为:

多分类编码矩阵示意

注意:无论 OvR 还是 MvM,拆分出的二分类任务都可能重新引入类别不平衡,处理方式见样本不均衡的实践处理

上面的拆分思路是经典的通用框架,但如今主流库大多走原生多分类路线,并不真的训练若干独立的二分类模型:

  • 树模型(决策树、随机森林)用多类不纯度直接分裂,天然多分类;
  • GBDT(XGBoost 的 multi:softprob、LightGBM 的 multiclass)每轮为每个类各训练一棵树,用 softmax 交叉熵联合优化,一个集成直接输出 KK 类概率;
  • 逻辑回归的多项式形式就是 softmax 回归,单模型、KK 组权重;
  • 神经网络用 softmax 输出层加交叉熵损失。

仍然保留拆分思路的场合是核 SVM(libsvm 内部做 OvO)和 sklearn 的 OneVsRest/OneVsOne 包装器。不过拆分视角在多分类评估时依然重要——下面一节对 ROC 的扩展就是逐类 OvR 展开的。

多分类下的性能评估

对多分类问题,混淆矩阵、准确率、逐类指标、PR/ROC 都可以用,但都需要做适当的扩展。下面按指标说明多分类下怎么算、怎么看。

混淆矩阵K×KK\times K 的:第 ii 行第 jj 列是真实类别为 ii、预测为 jj 的样本数,对角线是各类预测正确的数目。看的时候重点找非对角线上数值大的位置——那就是最容易互相混淆的类别对。

**准确率(Accuracy)**就是对角线元素之和除以样本总数。盲区在于类别不均衡时会被多数类主导,少数类全错也可能拿到很高的 Accuracy。

逐类 Precision / Recall / F1:把每个类轮流当作”正类”、其余类当作”反类”(OvR 视角),每个类就有一组 precision、recall 和 F1。这是判断 tail 类是否被照顾的基础。

宏平均、微平均与加权平均——把逐类指标聚合成一个数时的三种方式:

  • 宏平均(Macro):直接对各类指标求平均,每类等权,对样本少的类更敏感;
  • 微平均(Micro):把所有类的统计量合并成一张表再统一计算,等于按样本量加权,天然贴近样本多的类(对多分类来说 Micro-F1 恰好等于 Accuracy);
  • 加权平均(Weighted):按各类样本占比对逐类指标加权,介于两者之间(sklearn 的 average='weighted')。

多分类长尾任务里 Micro 指标通常很漂亮,但往往只是 head 类表现好;要看 tail 类是否真的被改善,需要同时报告 Macro 指标、逐类指标和混淆矩阵。

ROC / AUC:逐类做 OvR 展开,每类得一条 ROC 曲线,AUC 再取宏平均或按样本数加权平均。类别不均衡时,逐类 OvR 后的 PR 曲线往往比 ROC 更能反映少数类的实际表现,具体取舍见前面”不平衡场景下的指标选择”一节。

回归模型性能度量

回归模型度量的基本思路

回归任务中,性能度量衡量的是预测值 f(xi)f(\boldsymbol{x}_i) 与真实值 yiy_i 的差距,设数据集 DD 包含 mm 个样本。常用指标大致分三类:一类看绝对误差,与 yy 同量纲、数值直观,但对不同样本的惩罚方式不同;一类看相对基线的解释力或百分比,消除量纲、便于横向比较;还有一类针对量级差异大、分布右偏的数据专门设计。下面按组介绍。

MSE、MAE 与 RMSE

这三个指标是回归最基础的误差度量,思路相同:把每个样本的误差聚合成一个数,表示模型整体差了多少。区别在于聚合的方式。

E(f;D)=1mi=1m(f(xi)yi)2E(f;D)=\frac{1}{m}\sum_{i=1}^{m}\left(f\left(\boldsymbol{x}_{i}\right)-y_{i}\right)^{2}

均方误差(MSE)把误差平方后取平均,属于 L2 度量。平方会放大较大的误差,所以只要有几个样本偏差很大,MSE 就会被明显抬高,对异常值敏感;反过来,训练时大误差也会被重点修正。平方还让函数处处可导、凸性良好,是优化起来最方便的损失函数。需要注意 MSE 的单位是 yy 的平方,数值本身不直观。

MAE=1mi=1mf(xi)yi\mathrm{MAE}=\frac{1}{m}\sum_{i=1}^{m}\left|f(\boldsymbol{x}_i)-y_i\right|

平均绝对误差(MAE)对误差取绝对值后取平均,属于 L1 度量,每个样本的误差等权看待。个别异常值只会把整体数值抬一点,因此比较鲁棒;缺点是不会突出大误差,作为损失函数在零点不可导、梯度大小恒定,收敛上略逊于 MSE。它的单位和 yy 一致,报告平均差多少最直观。

RMSE=1mi=1m(f(xi)yi)2\mathrm{RMSE}=\sqrt{\frac{1}{m}\sum_{i=1}^{m}\left(f(\boldsymbol{x}_i)-y_i\right)^{2}}

均方根误差(RMSE)就是 MSE 开根号,把单位从 y2y^2 拉回 yy,读起来像平均来看误差大约有多大,是报告里常用的一种形式。它的惩罚特性与 MSE 完全一致,对异常值同样敏感。如果某个模型的 MAE 和 RMSE 数值差距明显,通常说明误差里混着少量大的离群点。

同一类里还有两个更极端的变体:最大误差(Max Error)取所有样本误差的最大值,反映最坏情况,适合判断模型在关键样本上是否稳定;中位绝对误差(MedAE)取误差的中位数,比 MAE 更鲁棒,几乎不受极端异常值影响。

R² 与调整 R²

R2=1i=1m(f(xi)yi)2i=1m(yiyˉ)2R^2=1-\frac{\sum_{i=1}^{m}\left(f(\boldsymbol{x}_i)-y_i\right)^{2}}{\sum_{i=1}^{m}\left(y_i-\bar{y}\right)^{2}}

其中 yˉ\bar{y} 是真实值的均值。决定系数(R²)的思路是先找一个最朴素的对手:什么都不学,把预测一律定成均值 yˉ\bar{y}。分母就是这个基线的误差平方和,分子是模型的误差平方和,两者一比,R² 度量的就是模型比基线少犯了多少误差,或者说解释了多大比例的方差。等于 1 表示完美拟合,等于 0 表示和直接预测均值差不多,比基线还差就变成负值。它没有量纲,可以在不同任务之间比较,是回归报告里几乎必给的一个数。缺点是不反映绝对误差大小:目标值很大的任务,预测差得远 R² 也可能不低;而且在训练集上特征越多 R² 只升不降。

Rˉ2=1(1R2)m1mp1\bar{R}^2=1-\left(1-R^2\right)\frac{m-1}{m-p-1}

其中 pp 是特征数量。调整决定系数(Adjusted R²)在 R² 的基础上按特征数做了修正:每多一个特征,除非它真能解释额外的方差,否则调整后的值反而会下降。因此比较特征数量不同的模型,或者做特征选择时,应该看调整后的 R²,避免靠堆特征刷高数值。

MAPE 与 SMAPE

MAPE=1mi=1myif(xi)yi×100%\mathrm{MAPE}=\frac{1}{m}\sum_{i=1}^{m}\left|\frac{y_i-f(\boldsymbol{x}_i)}{y_i}\right|\times 100\%

平均绝对百分比误差(MAPE)把每个样本的误差除以真实值再取平均,得到百分比。它的想法是平均看错了几成,报告给非技术读者最直观,也天然消除了量纲,可以比较目标尺度完全不同的任务。缺点有两个:真实值接近 0 时,误差除以一个很小的数会爆炸甚至无定义;而且同一个绝对偏差,对数值小的样本惩罚更重,高估和低估并不对称。

SMAPE=1mi=1mf(xi)yi(yi+f(xi))/2×100%\mathrm{SMAPE}=\frac{1}{m}\sum_{i=1}^{m}\frac{\left|f(\boldsymbol{x}_i)-y_i\right|}{\left(\left|y_i\right|+\left|f(\boldsymbol{x}_i)\right|\right)/2}\times 100\%

对称平均绝对百分比误差(SMAPE)把分母换成真实值和预测值的平均,缓解了 MAPE 中真实值接近 0 时爆炸的问题,也把高估、低估的惩罚拉得对称一些。代价是不如 MAPE 直观,预测值接近 0 时仍会不稳定,使用时需要留意。

RMSLE

RMSLE=1mi=1m(log(f(xi)+1)log(yi+1))2\mathrm{RMSLE}=\sqrt{\frac{1}{m}\sum_{i=1}^{m}\left(\log\left(f(\boldsymbol{x}_i)+1\right)-\log\left(y_i+1\right)\right)^{2}}

均方根对数误差(RMSLE)先对预测值和真实值取对数(加 1 是为了让 0 也能取对数),再在取对数后的尺度上计算 RMSE。它的想法是把绝对误差换成相对误差:目标值大时允许的绝对误差也大,目标值小时要求更严。因此它适合目标量级差异很大、右偏严重的数据,比如房价、销量这类跨度几个数量级的任务。对同一目标,低估受到的惩罚比高估更重,模型会被推向宁肯多猜也不漏猜;同时要求预测值为非负。

  • 标题: 监督学习性能评估:交叉验证、分类指标、多分类与 ROC 曲线
  • 作者: Hyacehila
  • 创建于 : 2025-10-02 08:04:00
  • 链接: https://hyacehila.github.io//blog/2025/10/02/supervised-learning-model-evaluation/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论