在GridSearchCV中为Pipeline交替使用不同的模型

12

我希望在sklearn中构建一个Pipeline,并使用GridSearchCV测试不同的模型。

这只是一个例子(请不要关注选择了哪些特定的模型):

reg = LogisticRegression()

proj1 = PCA(n_components=2)
proj2 = MDS()
proj3 = TSNE()

pipe = [('proj', proj1), ('reg' , reg)]

pipe = Pipeline(pipe)

param_grid = {
    'reg__c': [0.01, 0.1, 1],
}

clf = GridSearchCV(pipe, param_grid = param_grid)

如果我想尝试不同的降维模型,我需要编写不同的管道并手动进行比较。是否有简便的方法呢?

我想到的一个解决方案是定义一个从基本估计器派生的自己的类:

class Projection(BaseEstimator):
    def __init__(self, est_name):
        if est_name == "MDS":
            self.model = MDS()
        ...
    ...
    def fit_transform(self, X):
        return self.model.fit_transform(X)

我认为这会奏效,我只需创建一个投影对象并将其传递给Pipeline,使用评估器名称作为其参数。

但对我而言,这种方式有些混乱且不可扩展:每次想比较不同模型时,都需要定义新的类。另外,为了继续这个解决方案,可以实现一个执行相同工作但具有任意一组模型的类。这对我来说似乎过于复杂。

什么是比较不同模型最自然和Pythonic的方法?


1
您可以直接将估算器用作参数。 - Vivek Kumar
2个回答

20

假设你想使用PCA和TruncatedSVD作为您的降维步骤。

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

你可以这样做:

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

# Change params_grid -> Instead of dict, make it a list of dict
# In the first element, pass parameters related to pca, and in second related to svd

params_grid = [{
'svm__C': [1, 10, 100, 1000],
'svm__kernel': ['linear', 'rbf'],
'svm__gamma': [0.001, 0.0001],
'reduction':pca,
'reduction__n_components': n_components,
},
{
'svm__C': [1, 10, 100, 1000],
'svm__kernel': ['linear', 'rbf'],
'svm__gamma': [0.001, 0.0001],
'reduction':svd,
'reduction__n_components': n_components,
'reduction__algorithm':['randomized']
}]

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

grd = GridSearchCV(pipe, param_grid = params_grid)

调用grd.fit()会遍历params_grid列表中的所有元素,依次使用one中的所有值搜索参数。

请查看我的其他答案以获取更多详细信息:"Parallel" pipeline to get best model using gridsearch


我是否正确理解,每个param_grid的两个元素中也需要包含“reduction:algo_name”?否则,在训练分类器时将不使用svd(如果我正确地理解了您的其他答案并且它对我有效)。 - sooobus
@sooobus 啊,是的,那是我的错误。现在已经更正了。谢谢。 - Vivek Kumar
@VivekKumar 很棒的回答!顺便提一下,在scikit-learn 0.23(以及可能在早期版本中),参数网格中的单个值需要用一个元素的列表进行包装,否则会出错。因此,您必须使用'reduction':[pca]和'reduction':[svd]才能使其正常工作。 - Kevin Markham

6

一种不需要在参数网格中添加估计器名称前缀的替代方案如下:

from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression

# the models that you want to compare
models = {
    'RandomForestClassifier': RandomForestClassifier(),
    'KNeighboursClassifier': KNeighborsClassifier(),
    'LogisticRegression': LogisticRegression()
}

# the optimisation parameters for each of the above models
params = {
    'RandomForestClassifier':{ 
            "n_estimators"      : [100, 200, 500, 1000],
            "max_features"      : ["auto", "sqrt", "log2"],
            "bootstrap": [True],
            "criterion": ['gini', 'entropy'],
            "oob_score": [True, False]
            },
    'KNeighboursClassifier': {
        'n_neighbors': np.arange(3, 15),
        'weights': ['uniform', 'distance'],
        'algorithm': ['ball_tree', 'kd_tree', 'brute']
        },
    'LogisticRegression': {
        'solver': ['newton-cg', 'sag', 'lbfgs'],
        'multi_class': ['ovr', 'multinomial']
        }  
}

你可以定义:

from sklearn.model_selection import GridSearchCV

def fit(train_features, train_actuals):
        """
        fits the list of models to the training data, thereby obtaining in each 
        case an evaluation score after GridSearchCV cross-validation
        """
        for name in models.keys():
            est = models[name]
            est_params = params[name]
            gscv = GridSearchCV(estimator=est, param_grid=est_params, cv=5)
            gscv.fit(train_features, train_actuals)
            print("best parameters are: {}".format(gscv.best_estimator_))

基本上运行不同的模型,每个模型通过一个字典引用自己的优化参数集。当然,不要忘记将模型和参数字典传递给fit函数,以防它们不是全局变量。请查看此GitHub项目以获取更完整的概述。

gscv.fit(train_actuals, train_features) <-- 我认为顺序错了 - Maths12
当我使用网格搜索循环遍历所有模型以找到最佳超参数后,我是否使用得分最高的模型作为最终选择?例如,如果在上述过程中发现随机森林的最佳得分为0.8,而逻辑回归的最佳得分为0.9,那么应该选择逻辑回归吗? - Maths12
原则上是的,您会拿那个模型并在整个数据集上进行训练。请注意,交叉验证分数仅是针对未知测试数据可能遇到的错误的指示。 - gented
我认为这不是一个完整的答案,因为它没有展示如何嵌套管道的其他步骤。我已经进行了一些测试,但无法通过您的语法找出答案。 - OuttaSpaceTime

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