在sklearn中,可以定义一个串行管道来获取管道中所有连续部分的最佳超参数组合。串行管道可以按以下方式实现:
from sklearn.svm import SVCfrom sklearn import decomposition, datasetsfrom sklearn.pipeline import Pipelinefrom sklearn.model_selection import GridSearchCVdigits = datasets.load_digits()X_train = digits.datay_train = digits.target#使用主成分分析来降低维度#并改善泛化能力pca = decomposition.PCA()#使用线性SVCsvm = SVC()#将PCA和SVC组合成一个管道pipe = Pipeline(steps=[('pca', pca), ('svm', svm)])#检查SVC的训练时间n_components = [20, 40, 64]params_grid = {'svm__C': [1, 10, 100, 1000],'svm__kernel': ['linear', 'rbf'],'svm__gamma': [0.001, 0.0001],'pca__n_components': n_components,}
但是,如果我想为管道的每个步骤尝试不同的算法呢?例如,我如何对以下内容进行网格搜索?
主成分分析 OR 奇异值分解 AND 支持向量机 OR 随机森林
这将需要某种第二级或“元网格搜索”,因为模型的类型将是超参数之一。在sklearn中这是可能的吗?
回答:
Pipeline在其steps
(估算器列表)中支持None
,通过这种方式可以关闭管道的某些部分。
您可以通过向传递给GridSearchCV的参数中设置named_steps
的None
参数来不使用该估算器。
假设您想使用PCA
和TruncatedSVD
。
pca = decomposition.PCA()svd = decomposition.TruncatedSVD()svm = SVC()n_components = [20, 40, 64]
在管道中添加svd
pipe = Pipeline(steps=[('pca', pca), ('svd', svd), ('svm', svm)])# 更改params_grid -> 改为列表中的字典**# 在第一个元素中,传递`svd = None`,在第二个元素中传递`pca = None`params_grid = [{'svm__C': [1, 10, 100, 1000],'svm__kernel': ['linear', 'rbf'],'svm__gamma': [0.001, 0.0001],'pca__n_components': n_components,'svd':[None]},{'svm__C': [1, 10, 100, 1000],'svm__kernel': ['linear', 'rbf'],'svm__gamma': [0.001, 0.0001],'pca':[None],'svd__n_components': n_components,'svd__algorithm':['randomized']}]
现在只需将管道对象传递给gridsearchCV
grd = GridSearchCV(pipe, param_grid = params_grid)
调用grd.fit()
将会在params_grid
列表的两个元素上搜索参数,每次使用其中一个的所有值。
如果参数名称相同时的简化
如果您在“OR”中的两个估算器具有相同的参数名称,如本例中的PCA
和TruncatedSVD
都有n_components
(或者您只想搜索这个参数),可以简化为:
#在这里我已将名称更改为`preprocessor`pipe = Pipeline(steps=[('preprocessor', pca), ('svm', svm)])#现在将两个估算器都分配给`preprocessor`,如下所示:params_grid = {'svm__C': [1, 10, 100, 1000],'svm__kernel': ['linear', 'rbf'],'svm__gamma': [0.001, 0.0001],'preprocessor':[pca, svd],'preprocessor__n_components': n_components,}
此方案的泛化
我们可以创建一个函数来自动填充我们的param_grid
,以便将其提供给GridSearchCV
使用适当的值:
def make_param_grids(steps, param_grids): final_params=[] # Itertools.product将执行排列,使得 # (pca OR svd) AND (svm OR rf) 将变为 -> # (pca, svm) , (pca, rf) , (svd, svm) , (svd, rf) for estimator_names in itertools.product(*steps.values()): current_grid = {} # 步骤名称和估算器名称应对应 # 即preprocessor必须来自pca和select。 for step_name, estimator_name in zip(steps.keys(), estimator_names): for param, value in param_grids.get(estimator_name).iteritems(): if param == 'object': # 在管道中设置实际的估算器 current_grid[step_name]=[value] else: # 设置上述估算器对应的参数 current_grid[step_name+'__'+param]=value #将此字典追加到最终参数中 final_params.append(current_grid)return final_params
并在任意数量的转换器和估算器上使用此函数
# 将所有您想要“OR”的估算器添加到单个键中# 在`pca`和`select`之间使用OR,# 在`svm`和`rf`之间使用OR# 不同的键将被评估为管道中的串行估算器pipeline_steps = {'preprocessor':['pca', 'select'], 'classifier':['svm', 'rf']}# 在此字典中填写要搜索的参数all_param_grids = {'svm':{'object':SVC(), 'C':[0.1,0.2] }, 'rf':{'object':RandomForestClassifier(), 'n_estimators':[10,20] }, 'pca':{'object':PCA(), 'n_components':[10,20] }, 'select':{'object':SelectKBest(), 'k':[5,10] } } # 在上述声明的变量上调用该方法param_grids_list = make_param_grids(pipeline_steps, all_param_grids)
现在使用上面pipeline_steps
中使用的名称初始化一个管道对象
# 这里使用的PCA()和SVC()只是为了初始化管道,# 实际的估算器将从我们的`param_grids_list`中使用pipe = Pipeline(steps=[('preprocessor',PCA()), ('classifier', SVC())])
现在,最终设置我们的gridSearchCV对象并拟合数据
grd = GridSearchCV(pipe, param_grid = param_grids_list)grd.fit(X, y)