使用并行管道进行网格搜索以获取最佳模型

6
在sklearn中,可以定义一个串行管道来获取管道中所有连续部分的最佳超参数组合。可以按照以下方式实现串行管道:
from sklearn.svm import SVC
from sklearn import decomposition, datasets
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV

digits = datasets.load_digits()
X_train = digits.data
y_train = digits.target

#Use Principal Component Analysis to reduce dimensionality
# and improve generalization
pca = decomposition.PCA()
# Use a linear SVC
svm = SVC()
# Combine PCA and SVC to a pipeline
pipe = Pipeline(steps=[('pca', pca), ('svm', svm)])
# Check the training time for the 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,
}

但是如果我想为流水线的每个步骤尝试不同的算法怎么办呢?例如,如何在以下算法中进行网格搜索:

主成分分析或奇异值分解和支持向量机或随机森林

这将需要某种第二级或“元网格搜索”,因为模型类型将是超参数之一。在sklearn中是否可能实现?


2
您可以将两种类型的估算器添加到管道中,并在gridSearchCV中将它们设置为None - Vivek Kumar
1
听起来像是一个实用的解决方案。你能否把它集成到上面的示例代码中,并发布为一个答案呢? - Oblomov
1个回答

11

管道(Pipeline)通过在其steps(评估器列表)中支持None,可以关闭管道中的某个部分。

您可以将参数None传递给管道的named_steps,通过在传递给GridSearchCV的参数中设置来停用该评估器。

假设您想使用PCATruncatedSVD

pca = decomposition.PCA()
svd = decomposition.TruncatedSVD()
svm = SVC()
n_components = [20, 40, 64]

svd添加到管道中

pipe = Pipeline(steps=[('pca', pca), ('svd', svd), ('svm', svm)])

# Change params_grid -> Instead of dict, make it a list of dict**
# In the first element, pass `svd = None`, and in second `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']
}]

现在只需将pipeline对象传递给GridSearchCV即可。

grd = GridSearchCV(pipe, param_grid = params_grid)

调用 grd.fit() 会在 params_grid 列表中的所有元素中搜索参数,并逐个使用其中的所有值。

如果参数名称相同则简化

如果你的 "OR" 中的两个估计器具有与此案例中相同的参数名称,例如 PCATruncatedSVD 均具有 n_components(或者你只想搜索此参数),可以进行简化:

#Here I have changed the name to `preprocessor`
pipe = Pipeline(steps=[('preprocessor', pca), ('svm', svm)])

#Now assign both estimators to `preprocessor` as below:
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 will do a permutation such that 
    # (pca OR svd) AND (svm OR rf) will become ->
    # (pca, svm) , (pca, rf) , (svd, svm) , (svd, rf)
    for estimator_names in itertools.product(*steps.values()):
        current_grid = {}

        # Step_name and estimator_name should correspond
        # i.e preprocessor must be from pca and 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':
                    # Set actual estimator in pipeline
                    current_grid[step_name]=[value]
                else:
                    # Set parameters corresponding to above estimator
                    current_grid[step_name+'__'+param]=value
        #Append this dictionary to final params            
        final_params.append(current_grid)

return final_params

并且可以在任意数量的转换器和估计器上使用此函数

# add all the estimators you want to "OR" in single key
# use OR between `pca` and `select`, 
# use OR between `svm` and `rf`
# different keys will be evaluated as serial estimator in pipeline
pipeline_steps = {'preprocessor':['pca', 'select'],
                  'classifier':['svm', 'rf']}

# fill parameters to be searched in this dict
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]
                            }
                  }  


# Call the method on the above declared variables
param_grids_list = make_param_grids(pipeline_steps, all_param_grids)

现在使用上述 pipeline_steps 中使用的名称初始化管道对象。

# The PCA() and SVC() used here are just to initialize the pipeline,
# actual estimators will be used from our `param_grids_list`
pipe = Pipeline(steps=[('preprocessor',PCA()), ('classifier', SVC())])  

现在,最后设置gridSearchCV对象并适配数据

grd = GridSearchCV(pipe, param_grid = param_grids_list)
grd.fit(X, y)

2
我没有注意到你需要在svc和randomforest之间加上“OR”。我可能会编辑这个答案,以便更通用地处理更多的估计器。 - Vivek Kumar
谢谢,我明白了。 - Oblomov
@Vivek,你的回答真是太好了,而且顶部的概括也很棒! - Yev Guyduy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接