Comment on page
1.5 Performance Evaluation
为了衡量不同模型的效果,我们通常需要在测试数据利用特定的评价标准进行评价。那么我们需要定义用什么数据进行测试,也就是如何进行数据划分;用什么指标进行模型评估,也就是模型评价指标。
当数据量非常大时(在深度学习中比较常见),常见的做法是将数据集划分为三部分:

Datasets in Machine Learning
- Training Set:在调整超参数的过程中用于训练模型的数据集
- 在调参的过程中用于评估超参数效果的数据集: 有的文献中称为validation set,有的文献中称为testing set,上图中标注为testing set
- 用来最终测试模型的泛化能力的数据集:有的文献中称为testing set,有的文献中称为validation set,上图中标注为validation set
- 在数据集划分的过程中,我们通常不希望某个label只在特定一个数据集中出现。一般而言这种情况出现的可能性不大,但是当样本的类别比较多,或者某些类别的样本数量特别少时,也是容易发生的。这是我们通常会采取分层划分的方式,即对于每个类别的样本,分别进行数据集划分,这样就能排除上述的可能性。
- 超参数是模型不能直接从数据中学习得到,而需要事先人为给定的参数。所以为了选择合适的超参数(即所谓的"调参"),需要用一个外部的数据集上的分类效果作为挑选的指标。调参的过程属于一种模型选择(model selection)。
- 不同领域的文献中对数据集的描述可能会有一些出入。在传统机器学习的文献中,测试集(test set)指的是在性能评估前不能见到label的样本(即最终用于性能评估的样本),验证集(validation set)指的是用来调整超参数的数据集;但是有些临床研究中会把最终用于性能评估的样本称为"验证集"。这需要大家结合具体的语境进行区分,例如上图给出的数据集命名方式对应后者。
- 拟合最终用于评估模型泛化能力的模型时,所有可以用到的数据有时被称为"discovery set"。用来最终测试模型的泛化能力的数据集,剩下的数据都属于discovery set。在上图中,training set+testing set = discovery set
- 上图涉及了两类性能评估:
- 一是评估超参数的分类效果:我们选择不同的超参数组合,用training set拟合模型,用上图的"testing set"的标签作为指标,挑选出最大化分类性能的超参数组合
- 二是评估模型最终的泛化能力: 挑选出我们认为最合适的超参数组合后,我们用这组超参数在所有可用数据,即discovery set上拟合模型,再用一些从未见到过标签的数据(上图的"validation set")评估模型的泛化能力,作为分类效果的最终度量
由于我们在调参的过程中最大化的是上图中的testing set上的分类效果,所以在testing set上最好的一组超参数对应的分类效果是高估的,不能最终用于模型的性能评估。
如果我们不去调整模型的超参数,直接使用一个独立于数据的经验值,则不需要上图中的"testing set"。这时training set等价于discovery set,剩下的样本可全部用来评估模型的泛化能力
当数据量比较少时(在传统机器学习的应用场景中比较常见),我们通常会多次重复这一过程,取平均值以降低最终汇报的结果受随机因素的影响。具体的做法可以大致分为三类,我们以下逐一进行介绍:
- 多次random split
- 交叉验证(Cross-Validation)
- bootstrapping
相当于把我们在1a) 中介绍的方法重复很多次,每次都能得到一个评估泛化能力的指标,这样得到的分布就考虑到了随机性的影响
- K-fold Cross-validation
- 在K折交叉验证中,数据集被均匀地划分为个部分(folds)。在每轮验证中,把个folds当做discovery set,在剩下的一个fold上评估模型分类效果。
- K折交叉验证确保训练样本和测试样本之间没有重叠,K轮结束后,每个样本会被设置为测试样品一次。最后,模型平均表现是在轮次中计算指标的平均值得到的。

k-fold cross-validation
- 如果希望在交叉验证的过程中同时对模型超参数进行调节,一种常见的做法被称为"nested cross validation",即在每个划分出的discovery set上,对给定的每一组超参数组合,都进行一轮交叉验证,用平均的分类效果当做为这一个discovery set挑选超参数的指标,再用表现最好的超参数组合在整个discovery set上拟合模型。sklearn的文档提供了一个很好的例子,请大家参考plot nested cross validation iris
- K折交叉验同样可以重复多次,以考虑随机性的影响。
- Leave-one-out Cross-validation
Leave-one-out Cross-validation (LOOCV)是一种极端的K折交叉验证,对应把k设成样本总数的情形。这就是说每次进行只留下一个样本用作测试集,其余m-1全为训练集,进行m次训练,取m次的评估结果的平均值进行模型选择。
LOO的好处在于有确定的结果,缺点在于计算量比较大,而且无法反映分类效果受随机因素的影响。
当数据集较小,难以有效划分Training/Test时,可以采用**Bootstraping(自助采样)的方法。给定包含m个样本的数据集D,我们对它进行采样产生数据集 D′:每次随机从D中挑选出一个样本,将其拷贝放入D′, 然后再将该样本放回初始数据集D中,使得该样本在下次采样时仍有可能被采样到;这个过程重复执行m次后,我们就得到可包含m个样本数据的数据集D′,这就是Bootstraping(自助采样)**的结果。样本在m次采样中始终不被采到到概率为:
初始数据集D中约有36.8%的样本未出现在采样数据集D′中。于是我们可将D′ 用于模型拟合,D-D′用于评估泛化能力。
由于bootstraping进行的是有放回抽样,同一个样本在discovery set中可能会出现多次。
简单起见,我们暂时不考虑调参的问题。
[sklearn] package在sklearn.model_selection子模块实现了很多种数据集划分的方式。如果理解了上述的数据集划分具体在做什么事情,直接用numpy也可以非常容易的实现。
sklearn.model_selection.train_test_split(*arrays, test_size=None, train_size=None, random_state=None, shuffle=True, stratify=None)
# train_size, test_size定义了两个数据集所占的样本比例
# random_state: 如果给定一个固定的整数作为random seed,可以保证结果的可重复性
# stratify: 用于分层划分的变量,可以是样本标签
- 多次random split: 我们可以在一个循环里多次调用train_test_split函数。除此之外,sklearn还提供了ShuffleSplit和StratifiedShuffleSplit两个类,分别针对需要分层抽样课不需要分层抽样的情形,可以简化这一过程的实现
import numpy as np
from sklearn.model_selection import StratifiedShuffleSplit,ShuffleSplit
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([0, 0, 0, 1, 1, 1])