交叉验证及并行搜索

1.简单交叉验证

简单交叉验证方法是:首先随机地将已给数据分为两个部分,一部分作为训练集,一部分作为测试集;其中最常见的分割方式就是70%作为训练集,30%作为测试集;然后用训练集在各种不同模型下(参数不同)进行训练,从而得到不同的模型;最后选出测试误差最小的模型,作为最终模型.

举例: 例如用支持向量机(用法戳此处)来对iris数据集进行分类.一开始时我们不知道用多项式核函数好,还是径向基核函数好(或者是其它),于是我们就用简单交叉验证来选择模型.

第一步:将数据集以7:3分割;
第二步:分别对两个不同的模型进行训练;
第三步:选择得分高(误差小)的模型;

from sklearn.datasets import load_iris
from sklearn.cross_validation import train_test_split
from sklearn.svm import SVC

datas=load_iris()
# Step 1.
X_train,X_test,y_train,y_test=train_test_split(datas.data,datas.target, test_size=0.3,random_state=5)
# random_state 是随机种子值,给一个定值的作用就是每次都选取同样的验证集和测试集;不然每次运行选择的验证集和测试集都不一样

# Step 2.
svc_pol=SVC(kernel='poly',degree=3)
svc_pol.fit(X_train,y_train)
print svc_pol.score(X_test,y_test)

svc_rbf=SVC(kernel='rbf')
svc_rbf.fit(X_train,y_train)
print svc_rbf.score(X_test,y_test)

# Step 3.
# 以下为评分:
0.955555555556
0.977777777778
# 可以看出选择径向基核函数的效果更佳

虽然通过简单交叉验证可以对模型的泛化能力进行评估;但通过这一验证方法优化的模型性能也不稳定,原因在于对验证集集合随机采样的不确定性.换句话说,可能只是因为你的划分方式导致了一个比较好的结果.因此,更加高级的方式应该是:K折交叉验证(当k=1时就是简单交叉验证了)

2.K折交叉验证

K折交叉验证可以理解为从事了多次简单交叉验证的过程.其思想是:将数据集平均分割成K个互不相交的子集;然后每次将其中的K-1个子集作为训练集,余下的作为测试集;重复进行K次,最后选出次中平局误差最小的模型.

这里写图片描述

举例:在上面的例子当中,我们知道选择径向基核函数的效果更佳;但是在这种情况下(选择径向基核函数),还有其它的参数我们选择的是默认值(比如惩罚系数C=1为默认),难道选择默认值的效果就是最好的? 接下来,我们就对不同的模型,采用6折交叉验证来进行评估.

from sklearn.datasets import load_iris
from sklearn.cross_validation import cross_val_score
from sklearn.svm import SVC

datas=load_iris()
X=datas.data
y=datas.target


rbf_c1=SVC(kernel='rbf',C=1.0)
rbf_c2=SVC(kernel='rbf',C=2.0)

result1=cross_val_score(rbf_c1,X,y,cv=6)
print result1
print result1.mean()

result2=cross_val_score(rbf_c2,X,y,cv=6)
print result2
print result2.mean()

#以下为输出结果
[ 0.96296296  1.          1.          0.91666667  1.          1.        ]
0.979938271605

[ 0.96296296  1.          0.95833333  0.95833333  1.          1.        ]
0.979938271605

result表示进行6次简单交叉验证所得到的评分.我们可以看到两者6次的平均值都是一样的,也就是说选哪个模型都行了.

K折交叉验证很好的帮我们解决了”参数”选择的问题,问什么要加引号呢? 是因为我们此时仅仅只是徘徊在2个(或者几个)参数的选择上,所以可以这样手动的进行模型选择.试想一下,如果有大量的参数呢? 例如上面的支持向量机中,最基本的就有 C = 1.0 , g a m m a = a u t o 这两个参数,如果C和gamma的取值范围为都为1-10,那么这时就有100个模型,但总不能一个一个的来训练吧. 所以此时就要用到并行搜索.

3.并行搜索

由于超参数的空间是无尽的,因此超参数的组合配置只能是”更优”解,没有最优解.通常情况下我们依靠并行搜索对多种超参数组合进行暴力搜索.

举例:使用支持向量机,对20newsgroups进行分类

第一步:导入20newsgroups,将数据集分割成训练集和测试集;
第二步:并且使用TFidf进行向量化;
第三步:设置参数列表,并开始训练模型
第四部:选择评分最高的模型(参数)

注:此处的测试集不用于训练模型,在于测试模型的泛化能力

# Setp 1.
news=fetch_20newsgroups()
X_train,X_test,y_train,y_test=train_test_split(news.data,news.target,test_size=0.25,random_state=3)

# Setp 2.
tfidf=TfidfVectorizer(analyzer='word',stop_words='english')
X_train_tfidf=tfidf.fit_transform(X_train)
X_test_tfidf=tfidf.transform(X_test)

# Setp 3.(由于数据集特别大,花了19分钟才训练完成)
parameters={'C':np.logspace(-1,1,2),'gamma':np.logspace(-2,1,3)}

svc=SVC()
gs=GridSearchCV(svc,parameters,n_jobs=-1,cv=5,verbose=1)
gs.fit(X_train_tfidf,y_train)

注意:

(1).参数列表就是模型对应的参数名,如此处:

SVC(self, C=1.0, kernel='rbf', degree=3, gamma='auto',coef0=0.0, shrinking=True, probability=False,tol=1e-3, cache_size=200, class_weight=None,verbose=False,max_iter=-1,decision_function_shape=None,random_state=None)

(2).GridSearchCV()
n_jobs默认值为1,表示只启动一个核进行运算;但由于K次交叉验证都是相互独立的,所以可以同时一起并行计算,n_jobs=-1表示所有核一起运算.
cv=5表示5折交叉验证

#Step 4.
print gs.best_score_
print gs.best_params_

# 以下为输出结果
# Fitting 5 folds for each of 6 candidates, totalling 30 fits
# [Parallel(n_jobs=-1)]: Done  30 out of  30 | elapsed: 19.0min finished
# 0.898055391868
# {'C': 10.0, 'gamma': 0.31622776601683794}

从上面我们可以看出,在 C = [ 0.1 , 10. ] , g a m m a = [ 0.01 , 0.3162 , 10. ] 这6种组合中,当 C = 10 , g a m m a = 0.316 时,模型的分类能力最强.我们可以用下面的方法来验证:

svc1=SVC(C=0.1,gamma=0.01)
svc2=SVC(C=10.0,gamma=0.316)
svc1.fit(X_train_tfidf,y_train)
svc2.fit(X_train_tfidf,y_train)
print svc1.score(X_test_tfidf,y_test)
print svc2.score(X_test_tfidf,y_test)

# 输出结果
#0.0922587486744
#0.914457405444

相差了不止一点点

运行环境 python2.7 scikit-learn-0.18
源码
参考:

  • Python机器学习及实践
  • 统计学习方法

猜你喜欢

转载自blog.csdn.net/the_lastest/article/details/79834388