【问题标题】: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 不是答案吗?

标签: python scikit-learn


【解决方案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的解决方案。

        但是,你可能

        1. 找到 DataFrame 输出以获得最佳模型,该模型默认跳过计时信息
        2. 找到三个usage examples helpful
        3. 比如较短的核心代码,大约 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 中进行一些小改动。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-23
        • 2022-06-22
        • 1970-01-01
        • 2018-08-28
        • 2017-02-26
        • 2014-03-02
        • 1970-01-01
        相关资源
        最近更新 更多