avatar


7.交叉验证和网格搜索

网格搜索是一种模型调优的方法,通常与交叉验证搭配使用。我们先讨论交叉验证

交叉验证

交叉验证:为了让被评估的模型更加准确可信。

根据之前的知识,我们知道我们会把数据集分为训练集和测试集。
现在把测试集排除,我们对剩下的训练集再进行划分为训练集和验证集。

通过新的训练集和验证集,我们也可以得到一个准确率。

例如:
把数据分成4等份,这个也被成为4折交叉验证。

第一份 第二份 第三份 第四份 结果
验证集 训练集 训练集 训练集 当前情况下,模型的准确率
训练集 验证集 训练集 训练集 当前情况下,模型的准确率
训练集 训练集 验证集 训练集 当前情况下,模型的准确率
训练集 训练集 训练集 验证集 当前情况下,模型的准确率

在每一种的训练集和验证集下,我们都可以一个得到模型的准确率,对准确率求平均。

通过这种方法让模型的评估结果更加准确可信。

就像抽样调查的思想。抽一个样本的调查结果不可靠,那我们就多抽几个样本。

我们在《深度学习初步及其Python实现:5.过拟合》会再次讨论交叉验证,那时候的交叉验证是为了帮助我们检查到底是欠拟合还是过拟合。在这里,交叉验证的作用多次实验求平均,以帮助我们确定模型最佳的参数。

网格搜索的方法

通常情况下,有些参数是需要指定的。例如,上文kNN中的K。
这种参数叫做超参数

网格搜索的过程为:

  1. 对模型预设几组超参数组合
  2. 每组超参数都采用交叉验证进行评估
  3. 最后选出最优组合

有时候超参数不止一个,比如两个

  • a:1 3 5 7 9
  • b:0 2 4 6 8

则需要对两个超参数的网格搜索,即两两组合,共有525^2中组合。

网格搜索的实现

我们以上一章的kNN为例。

1
from sklearn.model_selection import GridSearchCV

其中

  • GridSearch代表网格搜索
  • CVCross Validation,代表交叉验证

参数有:

  • estimator:估计器对象
  • param_grid:估计器参数
    • dict类型数据,比如:{“n_neighbors”:[1,3,5,7,9]}
  • cv:指定几折交叉验证

方法有:

  • fit:输入训练数据
  • score:在测试集上的准确率

返回有:

  • best_score_:最好的结果
  • best_estimator_:最好的参数模型
  • cv_results_:每次交叉验证后测试集准确率结果和训练集准确率结果

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV
import pandas as pd

# 读取数据
data = pd.read_csv('../data/data_train.csv',names = ['id','k1k2','lock','stop','gate','thdv','thdi','label'])
# 取出目标值和特征值
y = data['label']
x = data.drop(['label','id'],axis=1)
# 分割为训练集和测试集
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.25)

# 实际上不做标准化,效果更好。这个应该和原本的数据有关。
# 特征工程
# 对x_train和x_test进行标准化
# std = StandardScaler()
# x_train = std.fit_transform(x_train)
# x_test = std.fit_transform(x_test)

# kNN
# 注意,不要设置参数
# knn = KNeighborsClassifier(n_neighbors=5)
knn = KNeighborsClassifier()

gc = GridSearchCV(knn,param_grid={'n_neighbors':[1,3,5,7,9]},cv=10)
gc.fit(x_train,y_train)

# 预测准确率
print('在测试集上的准确率')
print(gc.score(x_test,y_test))

print('在交叉验证中最好的结果和最好的模型')
print(gc.best_score_)
print(gc.best_estimator_)

print('每个超参数,每次交叉验证的结果')
print(gc.cv_results_)

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
在测试集上的准确率
0.8834619883040936
在交叉验证中最好的结果和最好的模型
0.8911033314173225
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=9, p=2,
weights='uniform')
每个超参数,每次交叉验证的结果
{
'mean_fit_time': array([0.0335788, 0.03926632, 0.03648825, 0.03487246, 0.03542645]),
'std_fit_time': array([0.00248624, 0.00690583, 0.0036206, 0.00357774, 0.00279938]),
'mean_score_time': array([0.15005405, 0.17923295, 0.16199327, 0.16413634, 0.17124434]),
'std_score_time': array([0.00686257, 0.02545098, 0.01455602, 0.0139033, 0.01430368]),
'param_n_neighbors': masked_array(data = [1, 3, 5, 7, 9],
mask = [False, False, False, False, False],
fill_value = '?',
dtype = object),
'params': [{
'n_neighbors': 1
}, {
'n_neighbors': 3
}, {
'n_neighbors': 5
}, {
'n_neighbors': 7
}, {
'n_neighbors': 9
}],
'split0_test_score': array([0.8499922, 0.87665679, 0.88351785, 0.88663652, 0.89006705]),
'split1_test_score': array([0.855138, 0.87977546, 0.88663652, 0.88881959, 0.88991112]),
'split2_test_score': array([0.857477, 0.88117886, 0.88835179, 0.89380945, 0.89287385]),
'split3_test_score': array([0.8509278, 0.87852799, 0.88195852, 0.88460939, 0.88445345]),
'split4_test_score': array([0.85170747, 0.88039919, 0.88289412, 0.88632465, 0.88881959]),
'split5_test_score': array([0.8477854, 0.87461011, 0.88038054, 0.88412352, 0.88646288]),
'split6_test_score': array([0.84887711, 0.87897692, 0.88693075, 0.89051778, 0.89176544]),
'split7_test_score': array([0.85386775, 0.87991266, 0.88973799, 0.89332502, 0.8941048]),
'split8_test_score': array([0.8477854, 0.87741734, 0.88552714, 0.88708671, 0.88630692]),
'split9_test_score': array([0.85386775, 0.87975671, 0.88396756, 0.8878665, 0.88880225]),
'mean_test_score': array([0.85174259, 0.8787212, 0.88499028, 0.88831191, 0.88935674]),
'std_test_score': array([0.00310972, 0.00188214, 0.00280452, 0.0031652, 0.00289555]),
'rank_test_score': array([5, 4, 3, 2, 1], dtype = int32)
}

完整的代码已经PUSH到了我的GitHub上
https://github.com/KakaWanYifan/BaiduPower

文章作者: Kaka Wan Yifan
文章链接: https://kakawanyifan.com/10207
版权声明: 本博客所有文章版权为文章作者所有,未经书面许可,任何机构和个人不得以任何形式转载、摘编或复制。

评论区