本文链接:http://blog.csdn.net/ismarvellous/article/details/78195010
模型调参是一门玄学。为了获得模型最优参数,我们需要不断地尝试不同的参数,这一过程相当繁琐。好在python的sklearn包中为我们提供了GridSearchCV,大大方便了调参过程。本文使用实际例程简单介绍一GridSearchCV的使用,并展示如何使用自定义验证集进行模型调参。
首先解释一下什么叫使用自定义验证集进行模型调参。GridSearchCV默认使用的模型验证方法是KFold交叉验证,但很多时候我们自己已经预先分配好了验证集,我们就要在这个验证集上评价模型好坏(有些任性),所以我们并不需要GridSearchCV为我们自动产生验证集,这就是所谓的使用自定义验证集进行模型调参。好了,我们首先简单认识一下GridSearchCV的使用。
现在假设我有一个训练集,特征为数组train_features
,标签为数组train_labels
。我还有一个测试集,特征为数组test_features
,没有标签。我希望在训练集上学习一个线性SVM,来预测测试集标签。我们知道,SVM有一些超参数需要人工设置,对于线性SVM,最关键的应该就是惩罚参数C。如何找到最优的C呢?通常情况下,我们使用KFold交叉验证。下面就简单介绍一下。
直接上代码:
import numpy as np
from sklearn.grid_search import GridSearchCV
from sklearn.svm import LinearSVC
from sklearn.externals import joblib
train_features = np.load('train_features.npy')
train_labels = np.load('train_labels.npy')
test_features = np.load('test_features.npy')
clf = LinearSVC(random_state=0)
params_search = {'C':[1,10,100,1000]} # 我们想要优化的参数
grid_search_params = {'estimator': clf, # 目标分类器
'param_grid': params_search, # 前面定义的我们想要优化的参数
'cv': 3, # 交叉验证split策略
'n_jobs': -1, # 并行运行的任务数,-1表示使用所有CPU
'verbose': 32} # 输出信息,数字越大输出信息越多
grsearch = GridSearchCV(**grid_search_params)
grsearch.fit(train_features, train_labels)
joblib.dump(grsearch, 'grsearch.model')
bst = grsearch.best_estimator_
preds = bst.predict(test_features)
在上面的代码中,我们使用3-fold交叉验证策略需找C的最优取值,每次的验证集从总的训练集中随机产生。
现在假设我们已经通过某种方式自己定义了训练集和验证集的划分方式,分别为train_features
和val_features
,我们并不想使用随机的划分,这时候要怎么办呢?可以使用PredefinedSplit
。
import numpy as np
from sklearn.grid_search import GridSearchCV
from sklearn.cross_validation import PredefinedSplit
from sklearn.svm import LinearSVC
from sklearn.externals import joblib
train_features = np.load('train_features.npy')
train_labels = np.load('train_labels.npy')
val_features = np.load('val_features.npy')
val_labels = np.load('val_labels.npy')
test_features = np.load('test_features.npy')
# 合并训练集和验证集
train_val_features = np.concatenate((train_features,val_features ),axis=0)
train_val_labels = np.concatenate((train_labels,val_labels ),axis=0)
clf = LinearSVC(random_state=0)
test_fold = np.zeros(train_val_features.shape[0]) # 将所有index初始化为0,0表示第一轮的验证集
test_fold[:train_features.shape[0]] = -1 # 将训练集对应的index设为-1,表示永远不划分到验证集中
ps = PredefinedSplit(test_fold=test_fold)
params_search = {'C':[1,10,100,1000]}
grid_search_params = {'estimator': clf, # 目标分类器
'param_grid': params_search, # 前面定义的我们想要优化的参数
'cv': ps, # 使用前面自定义的split验证策略
'n_jobs': -1, # 并行运行的任务数,-1表示使用所有CPU
'verbose': 32} # 输出信息,数字越大输出信息越多
print train_features.shape
print train_labels.shape
grsearch = GridSearchCV(**grid_search_params)
grsearch.fit(train_test_features, train_test_labels)
joblib.dump(grsearch, model_save_path)
bst = grsearch.best_estimator_
preds = bst.predict(test_features)
这里test_fold
是一个索引list,用于划分数据集。除了上面使用的固定一个验证集,还可以划分多重验证集。加入数据集中有4个样本,那么test_fold = [0, 1, -1, 1]
就表示在第一个验证集包含索引值为0的样本,第二个验证集包含引值为1的样本,以此类推,从而建立自定义的多重验证集。
这里有一段代码展示了这种划分方式:
>>> from sklearn.cross_validation import PredefinedSplit
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([0, 0, 1, 1])
>>> ps = PredefinedSplit(test_fold=[0, 1, -1, 1])
>>> len(ps)
2
>>> print(ps)
sklearn.cross_validation.PredefinedSplit(test_fold=[ 0 1 -1 1])
>>> for train_index, test_index in ps:
... 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 3] TEST: [0]
TRAIN: [0 2] TEST: [1 3]