在应用机器学习方法进行模型训练时,需要通过数据切分将数据集分为训练集/验证集/测试集。并且可以通过交叉验证,对模型在验证集上的拟合效果进行预评估,以选择表现最好的模型。此外,可以通过参数网格搜索的方法,确定最优的模型参数。 本篇文章通过具体数据做示例,总结了sklearn中模型选择model_selection模块常用函数的使用。包括: 1. 数据切分:train_test_split 2. K折交叉验证:KFold,StratifiedKFold,cross_val_score 3. 参数网格搜索:GridSearchCV
一、数据导入
导入python自带的wine数据集用于演示。该数据集为一个分类数据集,响应变量y取值为0、1、2.
from sklearn import datasets wine_data = datasets.load_wine() X = wine_data.data y = wine_data.target print(X.shape) # (178, 13)
二、数据切分
sklearn中train_test_split函数用于数据切分。其输入为待切分的特征X 和 相应变量y。输出按顺序为:切分后的训练集特征X_train,测试集特征X_test,训练集响应变量y_train,测试集响应变量y_test。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
train_test_split 函数参数说明:
- train_size:指定训练集大小(整数)/训练集比例(小数)
- test_size:指定测试集大小(整数)/测试集比例(小数)
- shuffle:默认值为True,表示在进行数据分割前,先进行数据打乱重排。
- random_state:随机种子,用来保证每次运行函数时,数据打乱重排的结果是一致的。
- stratify:用于控对制数据按标签类别进行分层切分。默认值为None,表示随机划分标签。当给定一个array时,将对array中的每个类别按相同的比例切分,此时其取值通常为类别响应变量y。(后面将通过具体实例进行演示)
当stratify取默认值None时,运行结果如下:
# 执行如下代码,测试集占比为0.2,查看划分结果 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 查看训练集和测试集大小 print(X_train.shape, X_test.shape) #### 输出:(142, 13) (36, 13) # 查看原始数据中标签类别分布,可知标签中有45个值为0,57值为1,40个值为2 print(sum(y==0), sum(y==1), sum(y==2)) #### 输出:45 57 40 ## 查看划分后的标签类别分布,可知并不是对标签按比例分割 print(sum(y_test==0), sum(y_test==1), sum(y_test==2)) #### 输出:14 14 8
实际使用中,如果对数据进行随机切分,可能导致某些数据量较少的类别全部被划分入训练集,影响模型的效果。所以,可以在数据分割时,按标签类别分层切分(取stratify=y),代码如下。
# 按y分布切分 X_train_1, X_test_1, y_train_1, y_test_1 = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y) #查看测试集中,各类别标签所占比例 print(sum(y_test_1==0)/sum(y==0)) print(sum(y_test_1==1)/sum(y==1)) print(sum(y_test_1==2)/sum(y==2)) #### 输出: #### 0.2033898305084746 #### 0.19718309859154928 #### 0.20833333333333334
可见,对每个类别,按0.2的比例切分到了测试集中。
三、K折交叉验证
K折交叉验证,指将训练数据等分成K个子集,每次选取1个子集作为验证集,其余K-1个子集作为训练集。该过程重复K次。
- KFold
用于定义一个K折交叉验证。参数 n_splits 表示切分份数,其它参数含义同数据切分部分。如定义一个5折交叉验证:
from sklearn.model_selection import KFold,StratifiedKFold n_splits = 5 kf = KFold(n_splits, shuffle=True, random_state=42)
其方法有get_n_splits()、split()。
get_n_splits() 返回切分的数量,split() 返回每个子集中的数据在原数据集中的位置标识。应用方法和结果如下:
kf_n = KFold(n_splits, shuffle=True, random_state=42).get_n_splits(X,y) print(kf_n) #### 输出: 5
kf_array = KFold(k, shuffle=True, random_state=42).split(X,y) for train_index, test_index in KFold(k, shuffle=True, random_state=42).split(X,y): # 打印下标 #print("TRAIN:", train_index, "TEST:", test_index) print("TEST:", test_index) # 提取每次切分后训练集和验证集数据 X_train, X_test = X[train_index], X[test_index] y_train, y_test = y[train_index], y[test_index]
输出的每次验证集下标如下所示。可以看到,每次迭代验证集的数据量是总量的1/5,且5次迭代的验证集下标无重复。
StratifiedKFold
其使用方法同KFold,其作用是,按y各类别的比例切分。同第二节中,train_test_split 的参数 stratify=y 的情况。cross_val_score
该函数用于直接计算K折交叉验证的模型得分。这个得分对所有内置的评分函数,都是得分越高,说明模型拟合越好。该函数的返回值是长度为K的list,为K次迭代在验证集上的模型得分。
如我们构建一个随机森林模型:
from sklearn.ensemble import RandomForestClassifier rf = RandomForestClassifier(random_state=42) #随机种子固定
cross_val_score函数输入参数包括:
- estimator:模型
- X:特征
- y:响应变量
- scoring:评分函数,如果不设置该参数,其默认使用所用模型estimator的score()函数对应的得分。
- cv:(1)其取值可以是一个整数,表示切分的折数,此时对于响应变量 y 为类别变量时,其默认使用StratifiedKFold()进行切分。其它情况时,使用KFold()进行切分。(2)其取值可以是一个CV切分器,如上述代码中的 kf。(3)其取值也可以是包含每折训练集和测试集下标的array,如上述代码中的 kf_array。(4)默认值为5.
from sklearn.model_selection import cross_val_score # 例: kf = KFold(5, shuffle=True, random_state=42) cv_score = cross_val_score(rf, X, y, scoring='accuracy' ,cv = kf) print(cv_score) #### 输出:[0.97222222 0.94444444 0.97222222 0.97142857 1. ] # 例: kf_array = KFold(k, shuffle=True, random_state=42).split(X,y) cv_score_2 = cross_val_score(rf, X, y, scoring='accuracy' ,cv = kf_array)
补充:(1)评分函数score 可以使用内置的评分函数,其可选取值如下所示:
(说明文档链接:https://scikit-learn.org/stable/modules/model_evaluation.html#scoring-parameter)
(2)评分函数score 也可以自行定义。使用 sklearn 的 metrics 模块中,make_scorer() 函数。
from sklearn.metrics import make_scorer # 定义一个自己的函数 # 如均方误差 import numpy as np def my_score_fun(y_true, y_pred): score = -np.abs(y_true-y_pred).max() # 为了满足得分越大越好,取个负值 return score cv_score_3 = cross_val_score(rf, X, y, scoring=make_scorer(my_score_fun, greater_is_better = True) ,cv = 5)
四、参数网格搜索
GridSearchCV 函数,给定模型参数的可选值,对这些可选值所有组合,计算交叉验证的得分,找到最优的参数组合。
# 函数常用参数说明 GridSearchCV(estimator, param_grid, scoring, cv) # estimator:模型 # param_grid:字典格式,‘key’为参数名,‘value’为参数取值的list # scoring、cv:同上述 cross_val_score() 函数
使用示例:
from sklearn.model_selection import GridSearchCV # 定义参数搜索取值的字典 rf_para={'max_depth':[3,5,7],'min_samples_split':[5,10]} # 定义网格搜索 rf_search = GridSearchCV(rf, rf_para, cv=5, scoring='accuracy') # 模型拟合 rf_model = rf_search.fit(X,y) # 获取最优参数 print(rf_model.best_params_) #### 输出:{'max_depth': 3, 'min_samples_split': 5} #### 如果搜索出的最优参数为边界值,可以尝试在超出边界值的一侧增加取值,再进行搜索 # 获取最优模型 rf_best = rf_model.best_estimator_ print(rf_best) #### 输出:RandomForestClassifier(max_depth=3, min_samples_split=5, random_state=42) # 计算最优模型的得分 rf_best.score(X,y) # 使用最优模型进行预测 rf_pred = rf_best.predict(X) ## 实际使用中,此处应该使用划分出的测试集中的 X_test