sklearn——交叉验证
一、简介
在用机器学习训练模型的时候,会将数据集D划分成训练集和测试集,因为如果在相同的数据上训练并测试无法评估模型的效果,常用的划分方法有K折交叉验证、p次k折交叉验证、留出法、留一法、留P法、随机分配、自助法等。另外,在训练模型的时候,经常需要进行调参,当我们有一堆参数的时候,也可以用类似的较差验证的方式依次使用不同的参数建模,最后选择最好的一个参数。在sklearn中要实现主要用sklearn.model_selection包的各种类,下面进行详细介绍。
二、数据集交叉验证方法
1、留出法
留出法的方法很简单,将数据集D分为两个部分,一个作为训练集另一个作为测试集,一般会选择70%的数据作为训练集。
对应的方法:
sklearn.model_selection.train_test_split(*arrays, **options)
- *arrays:数组,可以传入多个,例如同时传入x,y或者传入x,y,z。传入的数据类型为lists,、numpy arrays、scipy-sparse matrices、pandas dataframes。
- test_size:如果是float数据,表示测试集的占比;如果是None则默认取占比0.25;如果是int数据,则表示测试集样本个数。
- train_size:如果是float数据,表示训练集的占比;如果是None则默认取占比0.25;如果是int数据,则表示训练集样本个数。
- random_state:随机数种子。
- shuffle:是否清洗数据,若false,则不清洗。
- stratify:数据是否按Y的class分层的方式来划分,若Ture则是。例如Y的class=[‘a’,'b'],其中a占0.9、b占0.1,则划分训练集与测试集之后,训练集与测试集中a、b的占比不变。
方法return:
一个包含分裂后训练集、测试集的列表,列表形式为[x_train,x_test,y_train,y_test,....]。
代码示例:
from sklearn.model_selection import train_test_split
import numpy as np
x = np.arange(10) # 创建一维属性x
y = np.arange(10) # 创建一维属性y
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) # 划分测试集与训练集
print('x_train:', x_train)
print('x_test:', x_test)
print('y_train:', y_train)
print('y_test:', y_test)
'''
结果:
x_train: [2 0 1 7 5 9 4 8]
x_test: [3 6]
y_train: [2 0 1 7 5 9 4 8]
y_test: [3 6]
'''
2、K折交叉验证
将数据集分为k等份,每次训练时,将其中k-1份用作训练集,单独的那一份用作测试集,依次训练K次,可以选择训练效果较好的那份或者以平均模型优度作为模型优度。
对应的类:
sklearn.model_selection.KFold(n_splits=3, shuffle=False, random_state=None)
-
n_splits:几折,划分为几等分。
-
shuffle:在每次划分时,是否进行洗牌,不洗牌的话则按顺序划分。
-
若为Falses时,其效果等同于random_state等于整数,每次划分的结果相同
-
若为True时,每次划分的结果都不一样,表示经过洗牌,随机取样的
-
-
random_state:随机种子数
类的方法:
- get_n_splits(X=None, y=None, groups=None):获取参数n_splits的值,返回交叉验证的train-test的个数。
- split(X, y=None, groups=None):将数据集划分成训练集和测试集,返回索引生成器(即每一份样本在数据集D中的位置)。
代码示例:
from sklearn.model_selection import KFold import numpy as np x = np.arange(12) #创建一维属性x fold = KFold(3) #创建3折交叉验证 for train_index, test_index in fold.split(x): print('训练集索引:', train_index) print('测试集索引:', test_index) print('-------------------------------') ''' 结果:
训练集索引: [ 4 5 6 7 8 9 10 11] 测试集索引: [0 1 2 3] ------------------------------- 训练集索引: [ 0 1 2 3 8 9 10 11] 测试集索引: [4 5 6 7] ------------------------------- 训练集索引: [0 1 2 3 4 5 6 7] 测试集索引: [ 8 9 10 11] ------------------------------- '''
3、p次K折交差验证
有时候,依次K折交叉验证不能够满足训练要求,可以进行p次,经典的例如10次10折交叉验证。
对应的类:
sklearn.model_selection.RepeatedKFold(n_splits=5,n_repeats=2,random_state =0)
- n_splits:几折,划分为几等分。
- n_repeats:重复的次数,即p次K折的p。
- random_state :随机数种子。
类的方法:
- get_n_splits(X=None, y=None, groups=None):获取参数n_splits的值,即返回交叉验证的train-test的个数。
- split(X, y=None, groups=None):将数据集划分成训练集和测试集,返回索引生成器(即每一份样本在数据集D中的位置)。
注意:
RepeatedKFold在划分的时候,重复p次,每次重复会进行1次k折划分,每次划分都是随机的,这个与KFold默认方式是不同的。
代码示例:
from sklearn.model_selection import RepeatedKFold import numpy as np x = np.arange(12) #创建一维属性x fold = RepeatedKFold(n_splits=3,n_repeats=2) #创建3折交叉验证 for train_index, test_index in fold.split(x): print('训练集索引:', train_index) print('测试集索引:', test_index) print('-------------------------------') '''
结果:
训练集索引: [ 1 2 3 4 5 8 10 11] 测试集索引: [0 6 7 9] ------------------------------- 训练集索引: [ 0 3 4 6 7 9 10 11] 测试集索引: [1 2 5 8] ------------------------------- 训练集索引: [0 1 2 5 6 7 8 9] 测试集索引: [ 3 4 10 11] ------------------------------- 训练集索引: [0 2 3 5 6 7 8 9] 测试集索引: [ 1 4 10 11] ------------------------------- 训练集索引: [ 1 4 6 7 8 9 10 11] 测试集索引: [0 2 3 5] ------------------------------- 训练集索引: [ 0 1 2 3 4 5 10 11] 测试集索引: [6 7 8 9] ------------------------------- '''
4、留一较差验证
留一法就是Kflod的特殊形式k=1,即每次取1个样本作为测试集,其余做训练集。
对应的类:
sklearn.model_selection.train_test_split()
类的方法:
- get_n_splits(X=None, y=None, groups=None):返回较差验证的train-test数据对的个数。
- split(X, y=None, groups=None):将数据集划分成训练集和测试集,返回索引生成器(即每一份样本在数据集D中的位置)。
代码示例:
from sklearn.model_selection import LeaveOneOut import numpy as np x = np.arange(4) # 创建一维属性x leaveoneout=LeaveOneOut() for train,test in leaveoneout.split(x) : # 划分测试集与训练集 print('x_train:', train) print('x_test:', test) print('------------------------------------------------------') ''' 结果: x_train: [1 2 3] x_test: [0] ------------------------------------------------------ x_train: [0 2 3] x_test: [1] ------------------------------------------------------ x_train: [0 1 3] x_test: [2] ------------------------------------------------------ x_train: [0 1 2] x_test: [3] ------------------------------------------------------ '''
5、留P交叉验证
从数据集D中选P个样本作为测试集,其余作为训练集,直到选出所有的可能的P个组合为止。当P=1的时候就是留一交叉验证。
对应的类:
sklearn.model_selection.LeavePOut(p)
- p:测试集的样本数。
类的方法:
- get_n_splits(X=None, y=None, groups=None):返回较差验证的train-test数据对的个数。
- split(X, y=None, groups=None):将数据集划分成训练集和测试集,返回索引生成器(即每一份样本在数据集D中的位置)。
代码示例:
from sklearn.model_selection import LeavePOut import numpy as np x = np.arange(4) # 创建一维属性x leavePout=LeavePOut(2) for train,test in leavePout.split(x) : # 划分测试集与训练集 print('x_train:', train) print('x_test:', test) print('------------------------------------------------------') ''' 结果: x_train: [2 3] x_test: [0 1] ------------------------------------------------------ x_train: [1 3] x_test: [0 2] ------------------------------------------------------ x_train: [1 2] x_test: [0 3] ------------------------------------------------------ x_train: [0 3] x_test: [1 2] ------------------------------------------------------ x_train: [0 2] x_test: [1 3] ------------------------------------------------------ x_train: [0 1] x_test: [2 3] ------------------------------------------------------ '''
6、随机交叉验证
从数据集D中随机选择样本划分训练集与测试集,可以根据需要选择需要重复几次,重复n次能获得n个train-test样本对。
对应的类:
sklearn.model_selection.ShuffleSplit(n_splits=10, test_size=None, train_size=None, random_state=None)
-
n_splits:几折,划分为几等分。
- test_size:如果是float数据,表示测试集的占比;如果是None则自动设置;如果是int数据,则表示测试集样本个数。
- train_size:如果是float数据,表示训练集的占比;如果是None则自动设置;如果是int数据,则表示训练集样本个数。
-
random_state:随机种子数
类的方法:
- get_n_splits(X=None, y=None, groups=None):返回较差验证的train-test数据对的个数。
- split(X, y=None, groups=None):将数据集划分成训练集和测试集,返回索引生成器(即每一份样本在数据集D中的位置)。
代码示例:
from sklearn.model_selection import ShuffleSplit import numpy as np x = np.arange(10) # 创建一维属性x SS=ShuffleSplit(3,test_size=0.2) for train,test in SS.split(x) : # 划分测试集与训练集 print('x_train:', train) print('x_test:', test) print('------------------------------------------------------') ''' 结果: x_train: [5 3 2 9 6 8 1 7] x_test: [4 0] ------------------------------------------------------ x_train: [9 1 5 8 0 3 7 4] x_test: [6 2] ------------------------------------------------------ x_train: [4 9 3 7 5 6 2 8] x_test: [1 0] ------------------------------------------------------ '''
7、其它特殊情况的数据划分方法
1:对于分类数据来说,它们的target可能分配是不均匀的,比如在医疗数据当中得癌症的人比不得癌症的人少很多,这个时候,使用的数据划分方法有 StratifiedKFold ,StratifiedShuffleSplit
2:对于分组数据来说,它的划分方法是不一样的,主要的方法有 GroupKFold,LeaveOneGroupOut,LeavePGroupOut,GroupShuffleSplit
3:对于时间关联的数据,方法有TimeSeriesSplit
三、模型评估交叉验证
对数据集可以进行交叉验证从而建立交叉验证模型,也可以对模型的评估利用交叉验证的方法。sklearn的一些评估方法同样可以用于交叉验证。
1、模型准确度评分交叉验证
对应的方法:
sklearn.model_selection.cross_val_score(estimator, X, y=None, cv=’warn’,......)
- estimator:sklearn的estimator类,即各种模型。
- cv:几折验证,默认是3。
方法return:
一个包含所有交叉验证的score的列表。
代码示例:
from sklearn import datasets from sklearn import svm from sklearn.model_selection import cross_val_score iris = datasets.load_iris() clf = svm.SVC(kernel='linear', C=1) scores = cross_val_score(clf, iris.data, iris.target, cv=5) print(scores) ''' 结果: [0.96666667 1. 0.96666667 0.96666667 1. ]
2、其他评估方法交叉验证
对应方法:
sklearn.model_selection.cross_validate(estimator, X, y=None, scoring=None, cv=’warn’,......)
- estimator:sklearn的estimator类,即各种模型。
- scoring:sklearn的评估方法,可以有很多个,可以用字典或列表方式传入。
- cv:几折验证,默认是3。
方法return:
一个字典,加入有n个方法,则字典的key=['fit_time', 'score_time', 'test_scoringname1', 'test_scoringname2',...,'test_scoringnamen'],表示[训练时间, 测试时间, 测试分1, 测试分2, ..., 测试分n],每个key对应的值为所有交叉验证的值组成的数组。如5折交叉验证,'fit_time'的值为5次交叉验证的训练时间。
代码示例:
from sklearn.model_selection import cross_validate from sklearn.svm import SVC from sklearn.datasets import load_iris iris = load_iris() scoring = ['precision_macro', 'recall_macro'] # 两个方法,非字典 clf = SVC(kernel='linear', C=1, random_state=0) scores = cross_validate(clf, iris.data, iris.target, scoring=scoring, cv=5, return_train_score=False) print(scores.keys()) # 显示dict的key print(scores['test_recall_macro']) # 打印其中一个方法的值 ''' 结果: dict_keys(['fit_time', 'score_time', 'test_precision_macro', 'test_recall_macro']) [0.96666667 1. 0.96666667 0.96666667 1. ] '''
from sklearn.model_selection import cross_validate from sklearn.svm import SVC from sklearn.metrics import make_scorer,recall_score from sklearn.datasets import load_iris iris = load_iris() #传入字典 scoring = {'prec_macro': 'precision_macro','rec_micro': make_scorer(recall_score, average='macro')} clf = SVC(kernel='linear', C=1, random_state=0) scores = cross_validate(clf, iris.data, iris.target, scoring=scoring,cv=5, return_train_score=False) print(scores.keys()) print(scores['test_rec_micro']) ''' 结果: dict_keys(['fit_time', 'score_time', 'test_prec_macro', 'test_rec_micro']) [0.96666667 1. 0.96666667 0.96666667 1. ] '''
3、预测交叉验证
对应方法:
cross_val_predict
说明:
cross_val_predict 和 cross_val_score的使用方法是一样的,但是它返回的是一个使用交叉验证以后的输出值
三、利用交叉验证进行模型调参
通常情况下,有很多参数是需要手动指定的(如k-近邻算法中的K值),这种叫超参数。但是手动过程繁杂,所以需要对模型预设几种超参数组合。每组超参数都采用交叉验证来进行评估。最后选出最优参数组合建立模型。在sklearn中有一个方法GridSearchCV可以用来进行方便的调参,只需要提前设置后要传入的超参数就可以。
对应的类:
sklearn.model_selection.GridSearchCV(estimator, param_grid, scoring=None, n_jobs=None, iid=’warn’, refit=True, cv=’warn’, verbose=0, pre_dispatch=‘2*n_jobs’, error_score=’raise-deprecating’, return_train_score=False)
- estimator:估计器。
- param_grid:估计器参数,用字典的形式传入,字典的key对应估计器的参数,字典key的value对应估计器参数要传入的值。
- scoring:模型评价方法。
- n_jobs:线程数,默认1,-1表示全部线程。
- cv:几折,默认3.
类的方法:
- fit(x,y):传入训练数据。
- score():返回评估准确率。
类的属性:
- bestscore:在交叉验证中验证的最好结果。
- bestestimator:最好的参数模型。
- cvresults:每次交叉验证后的验证集准确率结果和训练集准确率结果。
代码示例:
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split,GridSearchCV from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier # 加载数据 iris = load_iris() # 划分数据集 x_train,x_test,y_train,y_test = train_test_split(iris.data,iris.target,test_size=0.3,random_state=8) # 标准化 transfer = StandardScaler() x_train = transfer.fit_transform(x_train) x_test = transfer.transform(x_test) # 指定算法及模型选择与调优——网格搜索和交叉验证 estimator = KNeighborsClassifier() param_dict = {"n_neighbors": [1, 3, 5]} estimator = GridSearchCV(estimator, param_grid=param_dict, cv=3) # 训练模型 estimator.fit(x_train,y_train) # 模型评估 # 方法一 比对真实值与预测值 y_predict = estimator.predict(x_test) y_test == y_predict # 方法二 计算准确率 estimator.score(x_test,y_test) # 然后进行评估查看最终选择的结果和交叉验证的结果 print("在交叉验证中验证的最好结果:\n", estimator.best_score_) print("最好的参数模型:\n", estimator.best_estimator_) print("每次交叉验证后的准确率结果:\n", estimator.cv_results_)