交叉验证

  1. 不论是回归模型还是分类模型,机器学习要达到的都是好的泛化能力.而泛化能力是由测试误差估计的泛化误差来评判的.
    其中测试误差是测试集的预测值和真实值的统计量,也就是以上谈到的各种指标.

  2. 我们不妨设 $\mu$ 为测试误差的均值, $\sigma $为测试误差的标准差, $\alpha$ 为显著性水平,一般是5%.
    假设测试误差和泛化误差服从正态分布,置信度95%的区间为 $( \mu - Z_{0.025} * \sigma, \mu + Z_{0.025} * \sigma) $,
    其中$Z_{\alpha}$表示符合标准正态分布的X变量$\alpha$上分位点,也就是$P(X>Z_{\alpha}) = \alpha$

  3. 我们认为A模型比B模型好,是A模型的误差区间上界小于B模型的误差区间下界.参看樱花书.机器学习模型基础部分.
  4. 所以我们不仅要关注各个指标的均值,也要关注方差,因为方差反映了模型泛化能力随着训练集改变的程度.
  5. 交叉验证帮助我们在数据集上多次获得模型的误差

基本算法

不妨假设有n个样例

k-折交叉验证(K-Fold)

最常用的方法被称之为k-折交叉验证.k-折交叉验证将数据集划分为k个较小的集合,成为子集,其验证流程是:

  1. 将k-1份子集作为训练集训练模型

  2. 将剩余的1份子集作为测试集用于估计该模型的泛化误差.
    一共可以重复k次

留P交叉验证(LPO, Leave P Out)

除了将数据集按份平分,还可以每次留下P样例作为测试集,剩下的作为训练集.
一共可以重复$\frac{!n}{!{n-p}!p}$次,其中$!n$表示n的阶乘.

留一交叉验证 (Leave One Out, LOO)

当P=1时,留P交叉验证就称为留一交叉验证.当然,也是k=n时的k-折交叉验证.
留一交叉验证获得的泛化误差往往方差比较大.

作为一般规则,大多数作者和经验证据表明, 5- 或者 10-折交叉验证应该优于 LOO.

时间序列分割

时间序列分割是k-fold的一个变体,用于时间序列模型。因为时间序列模型是用之前的数据来预测之后的数据,会考虑数据之间在时间上的依赖关系,所以不会出现用之后的数据来预测之前数据的情况。

它的实现步骤是

  • 按照时间间隔将数据从前到后分为k份,
  • 在第i次实验,用前i份作为训练集, $i+1$份作为测试集
  • 重复2步$k-1$次

sklearn中的交叉验证接口

与交叉验证相关的接口包括:

数据集交叉验证拆分接口

  • model_selection.KFold([n_splits, shuffle, …])|K折交叉验证
  • model_selection.LeaveOneOut()|留一交叉验证
  • model_selection.LeavePOut(p)|留P交叉验证
  • model_selection.TimeSeriesSplit([n_splits, …])|时间序列交叉验证

模型验证器接口

  • model_selection.cross_validate(estimator, X)|通过交叉验证来评估度量,并记录适合度/评分时间
  • model_selection.cross_val_predict(estimator, X)|为每个输入数据点生成交叉验证估计
  • model_selection.cross_val_score(estimator, X)|通过交叉验证评估分数
  • model_selection.validation_curve(estimator, …)|验证曲线

例:对iris数据交叉验证svm模型

最基础的交叉验证,只定义分成5块


In [15]:
import numpy as np
from sklearn import datasets
from sklearn import svm
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import cross_val_predict
from sklearn import metrics

In [10]:
iris = datasets.load_iris()

In [11]:
clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5)
scores


Out[11]:
array([ 0.96666667,  1.        ,  0.96666667,  0.96666667,  1.        ])

In [12]:
print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))


Accuracy: 0.98 (+/- 0.03)

通过交叉验证获取预测


In [14]:
predicted = cross_val_predict(clf, iris.data, iris.target, cv=10)

In [16]:
metrics.accuracy_score(iris.target, predicted)


Out[16]:
0.97333333333333338

指定特定的交叉验证方法


In [17]:
from sklearn.model_selection import KFold

In [21]:
kf = KFold(n_splits=20)

In [22]:
scores = cross_val_score(clf, iris.data, iris.target, cv=kf)

In [23]:
scores


Out[23]:
array([ 1.        ,  1.        ,  1.        ,  1.        ,  1.        ,
        1.        ,  1.        ,  1.        ,  0.875     ,  0.875     ,
        0.85714286,  1.        ,  1.        ,  1.        ,  1.        ,
        1.        ,  1.        ,  0.85714286,  1.        ,  1.        ])