在多个分类器上进行网格搜索

Posted

技术标签:

【中文标题】在多个分类器上进行网格搜索【英文标题】:grid search over multiple classifiers 【发布时间】:2014-05-27 12:58:50 【问题描述】:

有没有更好的内置方法在单个管道中进行网格搜索和测试多个模型?当然模型的参数会有所不同,这对我来说很复杂。这是我所做的:

from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.naive_bayes import MultinomialNB
from sklearn.grid_search import GridSearchCV


def grid_search():
    pipeline1 = Pipeline((
    ('clf', RandomForestClassifier()),
    ('vec2', TfidfTransformer())
    ))

    pipeline2 = Pipeline((
    ('clf', KNeighborsClassifier()),
    ))

    pipeline3 = Pipeline((
    ('clf', SVC()),
    ))

    pipeline4 = Pipeline((
    ('clf', MultinomialNB()),
    ))
    
    parameters1 = 
    'clf__n_estimators': [10, 20, 30],
    'clf__criterion': ['gini', 'entropy'],
    'clf__max_features': [5, 10, 15],
    'clf__max_depth': ['auto', 'log2', 'sqrt', None]
    

    parameters2 = 
    'clf__n_neighbors': [3, 7, 10],
    'clf__weights': ['uniform', 'distance']
    

    parameters3 = 
    'clf__C': [0.01, 0.1, 1.0],
    'clf__kernel': ['rbf', 'poly'],
    'clf__gamma': [0.01, 0.1, 1.0],

    
    parameters4 = 
    'clf__alpha': [0.01, 0.1, 1.0]
    

    pars = [parameters1, parameters2, parameters3, parameters4]
    pips = [pipeline1, pipeline2, pipeline3, pipeline4]
    
    print "starting Gridsearch"
    for i in range(len(pars)):
        gs = GridSearchCV(pips[i], pars[i], verbose=2, refit=False, n_jobs=-1)
        gs = gs.fit(X_train, y_train)
        print "finished Gridsearch"
        print gs.best_score_

但是,这种方法仍然在每个分类器中提供最佳模型,而不是在分类器之间进行比较。

【问题讨论】:

没有自动的方法来做到这一点。 yet ;) [问题是我们不能设置管道的“步骤”,对吧?] 那么你不能使用参数网格来切换管道步骤。 此功能是否已更改/更新? this 不是答案吗? 【参考方案1】:

虽然 dubek 的解决方案更直接,但它对分类器之前的管道元素参数之间的交互没有帮助。所以我写了一个helper class来处理,可以包含在scikit的默认Pipeline设置中。一个最小的例子:

from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import StandardScaler, MaxAbsScaler
from sklearn.svm import LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
from pipelinehelper import PipelineHelper

iris = datasets.load_iris()
X_iris = iris.data
y_iris = iris.target
pipe = Pipeline([
    ('scaler', PipelineHelper([
        ('std', StandardScaler()),
        ('max', MaxAbsScaler()),
    ])),
    ('classifier', PipelineHelper([
        ('svm', LinearSVC()),
        ('rf', RandomForestClassifier()),
    ])),
])

params = 
    'scaler__selected_model': pipe.named_steps['scaler'].generate(
        'std__with_mean': [True, False],
        'std__with_std': [True, False],
        'max__copy': [True],  # just for displaying
    ),
    'classifier__selected_model': pipe.named_steps['classifier'].generate(
        'svm__C': [0.1, 1.0],
        'rf__n_estimators': [100, 20],
    )

grid = GridSearchCV(pipe, params, scoring='accuracy', verbose=1)
grid.fit(X_iris, y_iris)
print(grid.best_params_)
print(grid.best_score_)

它也可以用于管道的其他元素,而不仅仅是分类器。 代码在github,如果有人想查看的话。

编辑:如果有人感兴趣,我已经在 PyPI 上发布了这个,只需使用 pip install pipelinehelper 安装 ti。

【讨论】:

【参考方案2】:

您可以使用'hyperopt' library,而不是使用网格搜索来选择超参数。

请查看this page 的第 2.2 节。在上述情况下,您可以使用hp.choice 表达式在各种管道中进行选择,然后分别为每个管道定义参数表达式。

在您的目标函数中,您需要根据所选管道进行检查,并返回所选管道和参数的 CV 分数(可能通过cross_cal_score)。

执行结束时的试验对象,将指示总体上最佳的管道和参数。

【讨论】:

【参考方案3】:

这就是我在没有包装函数的情况下所做的。 您可以评估任意数量的分类器。每一个都可以有多个参数用于超参数优化。

得分最高的将使用pickle保存到磁盘

from sklearn.svm import SVC
from operator import itemgetter
from sklearn.utils import shuffle
from sklearn.pipeline import Pipeline
from sklearn.naive_bayes import MultinomialNB
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.feature_extraction.text import TfidfVectorizer
import operator
#pipeline parameters
    parameters = \
        [ \
            
                'clf': [MultinomialNB()],
                'tf-idf__stop_words': ['english', None],
                'clf__alpha': [0.001, 0.1, 1, 10, 100]
            ,

            
                'clf': [SVC()],
                'tf-idf__stop_words': ['english', None],
                'clf__C': [0.001, 0.1, 1, 10, 100, 10e5],
                'clf__kernel': ['linear', 'rbf'],
                'clf__class_weight': ['balanced'],
                'clf__probability': [True]
            ,

            
                'clf': [DecisionTreeClassifier()],
                'tf-idf__stop_words': ['english', None],
                'clf__criterion': ['gini','entropy'],
                'clf__splitter': ['best','random'],
                'clf__class_weight':['balanced', None]
            
        ]

    #evaluating multiple classifiers
    #based on pipeline parameters
    #-------------------------------
    result=[]

    for params in parameters:

        #classifier
        clf = params['clf'][0]

        #getting arguments by
        #popping out classifier
        params.pop('clf')

        #pipeline
        steps = [('tf-idf', TfidfVectorizer()), ('clf',clf)]

        #cross validation using
        #Grid Search
        grid = GridSearchCV(Pipeline(steps), param_grid=params, cv=3)
        grid.fit(features, labels)

        #storing result
        result.append\
        (
            
                'grid': grid,
                'classifier': grid.best_estimator_,
                'best score': grid.best_score_,
                'best params': grid.best_params_,
                'cv': grid.cv
            
        )

    #sorting result by best score
    result = sorted(result, key=operator.itemgetter('best score'),reverse=True)

    #saving best classifier
    grid = result[0]['grid']
    joblib.dump(grid, 'classifier.pickle')

【讨论】:

【参考方案4】:

问题的另一个简单解决方案。

首先加载所有估算器。在这里,我将主要使用分类器。

logi=LogisticRegression(penalty="elasticnet",l1_ratio=0.5,solver="saga", random_state=4, n_jobs=-1)
rf=RandomForestClassifier(random_state=4, n_jobs=-1, max_features="auto", warm_start=True)
gb=GradientBoostingClassifier(random_state=4, subsample=0.8, max_features="auto", warm_start=True)
svc=SVC(random_state=4, kernel='rbf')
ex=ExtraTreesClassifier(random_state=4, n_jobs=-1, max_features="auto", warm_start=True)

然后创建一个分类器列表:

ensemble_clf=[rf, ex, gb, svc] 

现在,为每个分类器/估计器创建所有参数:-

params1="max_depth": range(5,30,5), "min_samples_leaf": range(1,30,2),
         "n_estimators":range(100,2000,200)
params2="criterion":["gini", "entropy"],"max_depth": range(5,30,5), 
         "min_samples_leaf": range(1,30,2), "n_estimators":range(100,2000,200)
params3="learning_rate":[0.001,0.01,0.1], "n_estimators":range(1000,3000,200)
params4="kernel":["rbf", "poly"], "gamma": ["auto", "scale"], "degree":range(1,6,1)

现在创建一个列表:

parameters_list=[params1, params2, params3, params4]

现在,最重要的部分来了: 为所有模型/分类器或估计器创建一个字符串名称: 这用于为 comparison

创建 Dataframes
model_log=["_rf", "_ex", "_gb", "_svc"]

现在运行一个 for 循环并使用网格搜索:

for i in range(len(ensemble_clf)):
    Grid=GridSearchCV(estimator=ensemble_clf[i], param_grid=parameters_list[i], 
                      n_jobs=-1, cv=3, verbose=3).fit(TrainX_Std, TrainY)
    globals()['Grid%s' % model_log[i]]=pd.DataFrame(Grid.cv_results_)  

"globals()['Grid%s' % model_log[i]]=pd.DataFrame(Grid.cv_results_) " 将为使用的每个估算器单独创建数据帧,并且可以用于通过排序进行比较,并挑选每个估算器的最佳参数。

希望这会有所帮助。

【讨论】:

喜欢这种方法 - 非常整洁。但是,我会使用模型的字典来使其更具可读性,而不是为每个模型网格搜索创建数据框,在 for 循环中我会做一个Grid.best_estimator_ 来获得为特定型号:) scikit-learn.org/stable/modules/generated/…【参考方案5】:

另一种选择是使用HyperclassifierSearch (Github) 包。接近bmurauer above的解决方案。

但是,你可能

    找到 DataFrame 输出以获得最佳模型,该模型默认跳过计时信息 找到三个usage examples helpful 比如较短的核心代码,大约 100 行

我基于David Batista 的代码开发了HyperclassifierSearch package(以pip install HyperclassifierSearch 开头),我喜欢代码的简洁性。

1.详解,超分类器evaluate_model函数的使用:

search = HyperclassifierSearch(models, params)
best_model = search.train_model(X, y)
search.evaluate_model(sort_by='mean_test_score', show_timing_info=False) # default parameters explicitly given

【讨论】:

如何指定评分指标? 感谢分享。回复@Maths12,您可以像在 sklearn gridsearchcv 中一样将scoring 传递给train_model 方法,例如scoring=["f1", "precision"]。如果你传递一个字符串,它会正常工作,但如果你想传递一个列表(如我的示例中),那么代码需要在evaluate_model 中进行一些小改动。

以上是关于在多个分类器上进行网格搜索的主要内容,如果未能解决你的问题,请参考以下文章

如何提供分类器以在 Tensorflow 上进行训练

Lesson 9.3 集成算法的参数空间与网格优化和使用网格搜索在随机森林上进行调参

尝试使用管道和网格搜索运行随机森林分类器时出错

如何使用多输出分类器实现网格搜索 cv?

OpenCV和SVM分类器在自动驾驶中的车辆检测

在管道中的分类器之后使用度量