机器学习中的交叉验证
总第100篇
本篇讲讲机器学习中的交叉验证问题,并利用sklearn实现。
前言
在说交叉验证以前,我们先想一下我们在搭建模型时的关于数据切分的常规做法[直接利用train_test_split
把所有的数据集分成两部分:train_data
和test_data
,先在train_data
上进行训练,然后再在test_data
上进行测试评估模型效果的好坏]。
因为我们训练模型时,不是直接把数丢进去就好了,而是需要对模型的不断进行调整(比如参数),使模型在测试集上的表现足够好,但是即使模型在测试集上效果好,不一定在其他数据集上效果好,因为这个模型是“专门”针对这个测试集而调整的,你为了测试这个模型真正的效果,你就得找另外的一部分数据,看模型在这些数据上的效果怎么样,只有模型在另外的数据上效果也好,那才可以说明的模型的效果是真的好(泛化能力不错,也可以理解成是举一反三的能力)。
其实这个和我们上学时候的上课—做练习题—考试
的关系很像,我们在每上完每一节课的时候,就会去做练习题来检验这节课的知识点掌握的怎么样,你的练习题几乎全部能做对,可能是因为刚上完这个章节,你一看到题就知道用这节课的知识点,直接把知识点套进去就可以做出来,每章如此,但是,期末考试的时候是把所有的章节结合起来考,不会告诉你用哪个知识点,这个时候就是检验你是否真的把这个知识点学会了的时候,你只有在期末考试的时候考的好,才能说明你是真的学习好。
这样就需要把数据分成三份,一份训练、一份验证、一份测试,先在训练集上训练模型,然后验证模型结果,最后再在测试集上判断模型的真正效果,但是这样做的结果就是大幅降低了数据的使用率,因训练数据不够多而造成欠拟合,并且数据切分的随机性也会对模型的效果有影响,这两个问题可以通过交叉验证(CV)的方式解决。
最基本的方法被称之为:k-折交叉验证。k-折交叉验证将训练集划分为k个较小的集合(其他方法会在下面描述,主要原则基本相同)。每一个 k 折都会遵循下面的过程:
- 将 k-1 份训练集子集作为 training data (训练集)训练模型,
- 将剩余的 1 份训练集子集作为验证集用于模型验证(也就是利用该数据集计算模型的性能指标,例如准确率)。
计算交叉验证指标
使用交叉验证最简单的方法是在估计器和数据集上调用cross_val_score
辅助函数。
下面的例子展示了如何通过分割数据,拟合模型和计算连续 5 次的分数(每次不同分割)来估计 linear kernel 支持向量机在 iris 数据集上的精度:
>>> 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. ])
默认情况下,每个 CV 迭代计算的分数是估计器的score
方法。可以通过使用scoring
参数来改变,scoring参数可选的值有“f1-score,neg_log_loss,roc_auc”等指标,具体值可看:
http://sklearn.apachecn.org/cn/0.19.0/modules/model_evaluation.html
设置方式,如下:
>>> from sklearn import metrics
>>> scores = cross_val_score(
... clf, iris.data, iris.target, cv=5, scoring='f1_macro')
>>> scores
array([ 0.96..., 1. ..., 0.96..., 0.96..., 1. ])
获取交叉验证预测结果
通过cross_val_predict
方法得到交叉验证模型的预测结果, 对于每一个输入的元素,如果其在测试集合中,将会得到预测结果。交叉验证策略会将可用的元素提交到测试集合有且仅有一次(否则会抛出一个异常)。
>>> from sklearn.model_selection import cross_val_predict
>>> predicted = cross_val_predict(clf, iris.data, iris.target, cv=10)
>>> metrics.accuracy_score(iris.target, predicted)
0.973...
交叉验证迭代器
接下来的部分列出了一些用于生成索引标号,用于在不同的交叉验证策略中生成数据划分的工具。
K折(KFold)
KFold 将所有的样例划分为 k 个组,称为折叠 (fold) (如果 k = n, 这等价于 Leave One Out(留一) 策略),都具有相同的大小(如果可能)。预测函数学习时使用 k - 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]
每个折叠由两个 arrays 组成,第一个作为 training set ,另一个作为 test set 。 由此,可以通过使用 numpy 的多维数组索引的方式创建训练/测试集合:
>>> X = np.array([[0., 0.], [1., 1.], [-1., -1.], [2., 2.]])
>>> y = np.array([0, 1, 0, 1])
>>> X_train, X_test, y_train, y_test = X[train], X[test], y[train], y[test]
留一交叉验证 (LOO)
LeaveOneOut (或 LOO) 是一个简单的交叉验证。每个学习集都是通过除了一个样本以外的所有样本创建的,测试集是被留下的样本。 因此,对于 n 个样本,我们有 n 个不同的训练集和 n 个不同的测试集。这种交叉验证程序不会浪费太多数据,因为只有一个样本是从训练集中删除掉的:
>>> 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]
留 P 交叉验证 (LPO)
LeavePOut 与 LeaveOneOut 非常相似,因为它通过从整个集合中删除 p 个样本来创建所有可能的 训练/测试集。对于 n 个样本,这产生了 {n choose p} 个 训练-测试 对。与 LeaveOneOut 和 KFold 不同,当 p > 1 时,测试集会重叠。
>>> from sklearn.model_selection import LeavePOut
>>> X = np.ones(4)
>>> lpo = LeavePOut(p=2)
>>> for train, test in lpo.split(X):
... print("%s %s" % (train, test))
[2 3] [0 1]
[1 3] [0 2]
[1 2] [0 3]
[0 3] [1 2]
[0 2] [1 3]
[0 1] [2 3]
随机排列交叉验证(Shuffle)
ShuffleSplit迭代器将会生成一个用户给定数量的独立的训练/测试数据划分。样例首先被打散然后划分为一对训练测试集合。
可以通过设定明确的random_state
,使得伪随机生成器的结果可以重复。
>>> from sklearn.model_selection import ShuffleSplit
>>> X = np.arange(5)
>>> ss = ShuffleSplit(n_splits=3, test_size=0.25,
... random_state=0)
>>> for train_index, test_index in ss.split(X):
... print("%s %s" % (train_index, test_index))
...
[1 3 4] [2 0]
[1 4 3] [0 2]
[4 0 2] [1 3]
ShuffleSplit
可以替代 KFold
交叉验证,因为其提供了细致的训练 / 测试划分的数量和样例所占的比例等的控制。
基于类标签的交叉验证迭代器
一些分类问题在目标类别的分布上可能表现出很大的不平衡性:例如,可能会出现比正样本多数倍的负样本。可以采用 StratifiedKFold
和 StratifiedShuffleSplit
中实现的分层抽样方法,确保相对的类别频率在每个训练和验证折叠中大致保留。
分层k折
StratifiedKFold
是k-fold
的变种,会返回stratified(分层)的折叠:每个小集合中,各个类别的样例比例大致和完整数据集中相同。
>>> from sklearn.model_selection import StratifiedKFold
>>> X = np.ones(10)
>>> y = [0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
>>> skf = StratifiedKFold(n_splits=3)
>>> for train, test in skf.split(X, y):
... print("%s %s" % (train, test))
[2 3 6 7 8 9] [0 1 4 5]
[0 1 3 4 5 8 9] [2 6 7]
[0 1 2 4 5 6 7] [3 8 9]
分层随机Split
StratifiedShuffleSplit
是ShuffleSplit
的一个变种,会返回直接的划分,比如:创建一个划分,但是划分中每个类的比例和完整数据集中的相同。
>>> from sklearn.model_selection import StratifiedShuffleSplit
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([0, 0, 1, 1])
>>> sss = StratifiedShuffleSplit(n_splits=3, test_size=0.5, random_state=0)
>>> sss.get_n_splits(X, y)
3
>>> print(sss)
StratifiedShuffleSplit(n_splits=3, random_state=0, ...)
>>> for train_index, test_index in sss.split(X, y):
... print("TRAIN:", train_index, "TEST:", test_index)
... X_train, X_test = X[train_index], X[test_index]
... y_train, y_test = y[train_index], y[test_index]
TRAIN: [1 2] TEST: [3 0]
TRAIN: [0 2] TEST: [1 3]
TRAIN: [0 2] TEST: [3 1]
交叉验证在时间序列数据中应用
时间序列数据的特点是时间 (autocorrelation(自相关性))之间是具有相关性的。然而,传统的交叉验证技术,例如 KFold
和 ShuffleSplit
假设样本是独立的且分布相同的,并且在时间序列数据上会导致训练和测试实例之间不合理的相关性(产生广义误差的估计较差)。 因此,对 “future(未来)” 观测的时间序列数据模型的评估至少与用于训练模型的观测模型非常重要。为了达到这个目的,一个解决方案是由TimeSeriesSplit
提供的。
时间序列分割
TimeSeriesSplit
是k-fold
的一个变体,它首先返回k折作为训练数据集,并且 (k+1) 折作为测试数据集。请注意,与标准的交叉验证方法不同,连续的训练集是超越前者的超集。另外,它将所有的剩余数据添加到第一个训练分区,它总是用来训练模型。
这个类可以用来交叉验证以固定时间间隔观察到的时间序列数据样本。
对具有 6 个样本的数据集进行 3-split 时间序列交叉验证的示例:
>>> from sklearn.model_selection import TimeSeriesSplit
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([1, 2, 3, 4, 5, 6])
>>> tscv = TimeSeriesSplit(n_splits=3)
>>> print(tscv)
TimeSeriesSplit(max_train_size=None, n_splits=3)
>>> for train, test in tscv.split(X):
... print("%s %s" % (train, test))
[0 1 2] [3]
[0 1 2 3] [4]
[0 1 2 3 4] [5]
交叉验证实例
上面提到的几种方法都是用来生成train和test的索引编号,而不像train_test_split
方法直接可以生成训练集和数据集,我们只需要利用索引的方式去把对应的train和test索引出来即可,拿最简单的Kfold为例,具体的实现方式如下:
#导入所需要的库
>>> from sklearn import svm
>>> from sklearn.model_selection import KFold
>>> digits = datasets.load_digits() #load scikit-learn库里的实验数据集:digits
>>> X_digits = digits.data #获得样本特征数据部分
>>> y_digits = digits.target #获得样本label
>>> svc = svm.SVC(C=1, kernel='linear') #初始化svm分类器
>>> kf = KFold(n_splits=3)
>>> for train, test in kf.split(X):
>>> #此处train、test里有交叉验证对象中已经初始化好的3组训练样本和测试样本所需的位置标号
>>> [svc.fit(X_digits[train], y_digits[train]).score(X_digits[test], y_digits[test]) for train, test in kfold]
你还可以看:
- Flash/Flex学习笔记(6):制作基于xml数据源的flv视频播放器
- proxy_pass根据path路径转发时的"/"问题记录
- 温故而知新:查看端口占用情况以及DOS中的管道操作/重定向操作
- Android新手之旅(7) RadioButton的自定义
- Flash/Flex学习笔记(4):如何打开网页及Get/Post数据
- Flash/Flex学习笔记(5):捕获摄像头(续)--在线抓屏并保存到客户端本地
- 分布式监控系统Zabbix--使用Grafana进行图形展示
- Silverlight在线创建PDF(支持中文)
- Flash/Flex学习笔记(3):动态添加组件
- Flash/Flex学习笔记(1):Hello World!
- 数据库常规操作
- 不伦不类的Action Script 3.0
- Asp.Net Mvc中的一些初级问题整理
- Pandas Series笔记
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何根据class_code筛选转录本?
- JNI线程相关
- JNI函数加载
- CSS中的传统布局、多列布局、弹性伸缩布局及Emmet工具
- 数据分析可视化(四)|Pyecharts制作地图的几种方法评析
- tensorflow运行提示未编译使用SSE4.1,SSE4.2等问题的解决方法
- [手把手系列之二]实现多层神经网络
- 使用SystemVerilog简化FPGA中的接口
- 想用深度学习谱写自己的音乐吗?这篇指南来帮你!(附代码)
- 你不是说你会aop吗?
- 开源:推荐一个不错的离线IP地址定位库
- 武磊告别西甲!Python带你解读「全村的希望」武磊职业数据
- 谁在崛起,谁在没落?新一线城市竞争力盘点,用Python绘制动态图带你看懂!
- 两数相加
- 这样设置 IDEA,让你爽到飞起!