CH1 统计学习方法概论|1.5.2交叉验证《统计学习方法》-学习笔记

文章原创,最近更新:2018-06-11

1.什么是交叉验证法?
2.为什么用交叉验证法?
3.交叉验证主要有哪些方法?优缺点?
4.各方法应用举例?

参考链接:
1、 为什么要用交叉验证?
2、机器学习中的交叉验证
3、K折交叉验证的前生今世
4、统计挖掘那些事:分层抽样与交叉验证
5、交叉验证,模型选择(cross validation)
6、10折交叉验证的例子
前言:通过网上找的文章,通过归纳总结具体如下:

1.什么是交叉验证法?

在说交叉验证以前,我们先想一下我们在搭建模型时的关于数据切分的常规做法[直接利用train_test_split把所有的数据集分成两部分:train_datatest_data,先在train_data上进行训练,然后再在test_data上进行测试评估模型效果的好坏。

因为我们训练模型时,不是直接把数丢进去就好了,而是需要对模型的不断进行调整(比如参数),使模型在测试集上的表现足够好,但是即使模型在测试集上效果好,不一定在其他数据集上效果好,因为这个模型是“专门”针对这个测试集而调整的,你为了测试这个模型真正的效果,你就得找另外的一部分数据,看模型在这些数据上的效果怎么样,只有模型在另外的数据上效果也好,那才可以说明的模型的效果是真的好(泛化能力不错,也可以理解成是举一反三的能力)。

其实这个和我们上学时候的上课—做练习题—考试的关系很像,我们在每上完每一节课的时候,就会去做练习题来检验这节课的知识点掌握的怎么样,你的练习题几乎全部能做对,可能是因为刚上完这个章节,你一看到题就知道用这节课的知识点,直接把知识点套进去就可以做出来,每章如此,但是,期末考试的时候是把所有的章节结合起来考,不会告诉你用哪个知识点,这个时候就是检验你是否真的把这个知识点学会了的时候,你只有在期末考试的时候考的好,才能说明你是真的学习好。

简单通俗的表达:


为了方便调试超参数,从训练集中划分一部分作为验证集,但是由于划分出一个验证集调节的参数可能受到验证集数据影响有偏差不够准确,于是进行交叉,如图,其中fold1-fold5可以进行任意4个组合作为训练集,剩下一个作为验证集,这样得到不同的训练模型,然后对最终预测结果进行求平均,这样就消除了只取一次验证集调节参数带来的“偏见”.

总结:
它的基本思想就是将原始数据(dataset)进行分组,一部分做为训练集来训练模型,另一部分做为测试集来评价模型。

2.为什么用交叉验证法?

这样就需要把数据分成三份,一份训练、一份验证、一份测试,先在训练集上训练模型,然后验证模型结果,最后再在测试集上判断模型的真正效果,但是这样做的结果就是大幅降低了数据的使用率,因训练数据不够多而造成欠拟合,并且数据切分的随机性也会对模型的效果有影响,这两个问题可以通过交叉验证(CV)的方式解决。

总结:

  • 1.交叉验证用于评估模型的预测性能,尤其是训练好的模型在新数据上的表现,可以在一定程度上减小过拟合。
  • 2.还可以从有限的数据中获取尽可能多的有效信息。

3.交叉验证主要有哪些方法?优缺点?

3.1简单交叉验证(hold-outcross validation)

简单交叉验证方法是:首先随机地将已给数据分为两部分,一部分作为训练集,另一部分作为测试集(例如,70%的数据为训练集,30%的数据为测试集);然后用训练集在各种条件下(例如,不同的参数个数)训练模型,从而得到不同的模型;在测试集上评价各个模型的测试误差,选出测试误差最小的模型.

缺点:
在于得到的最佳模型是在70%的训练数据上选出来的,不代表在全部训练数据上是最佳的。还有当训练数据本来就很少时,再分出测试集后,训练数据就太少了。
其实严格意义来说Hold-OutMethod并不能算是CV,因为这种方法没有达到交叉的思想,由于是随机的将原始数据分组,所以最后验证集分类准确率的高低与原始数据的分组有很大的关系,所以这种方法得到的结果其实并不具有说服性.

3.2 k折交叉验证(k-fold cross validation)

进一步对简单交叉验证方法再做一次改进,如下:

  • 第一步,不重复抽样将原始数据随机分为 k 份。
  • 第二步,每一次挑选其中 1 份作为测试集,剩余 k-1 份作为训练集用于模型训练。
  • 第三步,重复第二步 k 次,这样每个子集都有一次机会作为测试集,其余机会作为训练集。
  • 在每个训练集上训练后得到一个模型,
  • 用这个模型在相应的测试集上测试,计算并保存模型的评估指标,
  • 第四步,计算 k 组测试结果的平均值作为模型精度的估计,并作为当前 k 折交叉验证下模型的性能指标。

k 一般取 10,
数据量小的时候,k 可以设大一点,这样训练集占整体比例就比较大,不过同时训练的模型个数也增多。
数据量大的时候,k 可以设小一点。

通俗的表达:
一般我们常用十折交叉检验,不妨我们设定k=10,具体如下:

  • 首先我们把总数据集划分为10份,分别成D1,D2,… …,D10;
  • 首先选择D1数据集作为测试集,D2,…D10作为训练集。在训练集上构建模型,在测试集上进行模型评估,得到评估记过O1;
  • 之后选择D2数据集作为测试集,D1,D3,…D10作为训练集。在训练集上构建模型,在测试集上进行模型评估,得到评估记过O2;
  • 分别抽去D3,D4,…,D10作为测试集,一共重复10次,并得到10个结果:O1,O2,…,O10;
  • 将得到10个结果:O1,O2,…,O10加和取平均,作为最终评估结果O。



以上过程,我们称之为10折交叉检验。一般而言,在平常的使用中,10折交叉检验比较常见,当然也会有5折交叉检验,3折交叉检验。

此方法称为k-fold cross validation(k-折叠交叉验证)说白了,这个方法就是将简单交叉验证的测试集改为1/k,每个模型训练k次,测试k次,预测误差为k次的平均。K一般大于等于2,实际操作时一般从3开始取,只有在原始数据集合数据量小的时候才会尝试取2.

K-CV可以有效的避免过学习以及欠学习状态的发生,最后得到的结果也比较具有说服性.一般讲k取值为10。这样数据稀疏时基本上也能进行。

缺点:
缺点就是训练和测试次数过多。

案例:
关于10折交叉验证的例子,具体如下:

  • 第1步,将数据等分到10个桶中。
    我们会将50名篮球运动员和50名非篮球运动员分到每个桶中。每个桶当中放入了100人的信息。
  • 第2步,下列步骤重复10次。

(1)每一次迭代中留存其中一个桶。第一次迭代中留存桶1,第二次留存桶2,其余依此类推。
(2)用其他9个桶的信息训练分类器(第一次迭代中利用从桶2到桶10的信息训练分类器)。
(3)利用留存的数据来测试分类器并保存测试结果。在上例中,这些结果可能如下:35个篮球运动员被正确分类;29个非篮球运动员被正确分类。

  • 第3步,对上述结果汇总。

通常情况下我们会将结果放到与下表类似的表格中:


在所有500名篮球运动员中,有372人被正确分类。可能需要做的一件事是将右下角的数字也加上去,也就是说1000人当中有652(372+280)人被正确分类。因此得到的精确率为65.2%。与2折或3折交叉验证相比,基于10折交叉验证得到的结果可能更接近于分类器的真实性能。之所以这样,是因为每次采用90%而不是2折交叉验证中仅仅50%的数据来训练分类器。

3.3 留一交叉验证(leave one out cross validation)

它是指,我们令样本划分次数k等于数据集合D的样本数量n,即对样本集合D划分为n份子集,每份子集只包含一个样本。

优缺点:

留一法的主要不足在于计算的开销很大。考虑一个包含1000个实例的中等规模的数据集,需要一分钟来训练分类器。对于10折交叉验证来说,我们将花费10分钟用于训练。而对于留一法来说,训练时间需要16个小时。如果数据集包含百万样本,那么花费在训练上的总时间将接近两年。我的天哪!

3.4注意事项

交叉验证使用中注意的事项:

  • 训练集中样本数量要足够多,一般至少大于总样本数的50%。
  • 训练集和测试集必须从完整的数据集中均匀取样。均匀取样的目的是希望减少训练集、测试集与原数据集之间的偏差。当样本数量足够多时,通过随机取样,便可以实现均匀取样的效果。(随机取样,可重复性差)

4.各方法应用举例?

4.1留出法 (holdout cross validation)

下面例子,一共有 150 条数据:

>>> import numpy as np
>>> from sklearn.model_selection import train_test_split
>>> from sklearn import datasets
>>> from sklearn import svm

>>> iris = datasets.load_iris()
>>> iris.data.shape, iris.target.shape
((150, 4), (150,))

用 train_test_split 来随机划分数据集,其中 40% 用于测试集,有 60 条数据,60% 为训练集,有 90 条数据:

>>> X_train, X_test, y_train, y_test = train_test_split(
...     iris.data, iris.target, test_size=0.4, random_state=0)

>>> X_train.shape, y_train.shape
((90, 4), (90,))
>>> X_test.shape, y_test.shape
((60, 4), (60,))

用 train 来训练,用 test 来评价模型的分数。

>>> clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train)
>>> clf.score(X_test, y_test)                           
0.96...

4.2 k 折交叉验证(k-fold cross validation)

最简单的方法是直接调用 cross_val_score,这里用了 5 折交叉验证:

>>> from sklearn.model_selection import cross_val_score
>>> clf = svm.SVC(kernel='linear', C=1)
>>> scores = cross_val_score(clf, iris.data, iris.target, cv=5)
>>> scores                                              
array([ 0.96...,  1.  ...,  0.96...,  0.96...,  1.        ])

得到最后平均分为 0.98,以及它的 95% 置信区间:

>>> print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))
Accuracy: 0.98 (+/- 0.03)

我们可以直接看一下 K-fold是怎样划分数据的:
X 有四个数据,把它分成 2 折,
结果中最后一个集合是测试集,前面的是训练集,
每一行为 1 折:

>>> import numpy as np
>>> from sklearn.model_selection import KFold

>>> X = ["a", "b", "c", "d"]
>>> kf = KFold(n_splits=2)
>>> for train, test in kf.split(X):
...     print("%s %s" % (train, test))
[2 3] [0 1]
[0 1] [2 3]

同样的数据 X,我们看 LeaveOneOut后是什么样子,
那就是把它分成 4 折,
结果中最后一个集合是测试集,只有一个元素,前面的是训练集,
每一行为 1 折:

>>> from sklearn.model_selection import LeaveOneOut

>>> X = [1, 2, 3, 4]
>>> loo = LeaveOneOut()
>>> for train, test in loo.split(X):
...     print("%s %s" % (train, test))
[1 2 3] [0]
[0 2 3] [1]
[0 1 3] [2]
[0 1 2] [3]