对多个分类器进行网格搜索

45 投票
5 回答
34670 浏览
提问于 2025-04-18 02:35

有没有更好的内置方法来进行网格搜索,并在一个流程中测试多个模型呢?当然,不同模型的参数会不一样,这让我觉得有点复杂,不知道怎么做。以下是我尝试的:

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_

不过,这种方法还是只在每个分类器内部找出最佳模型,并没有在不同的分类器之间进行比较。

5 个回答

3

另一个选择是使用HyperclassifierSearch (Github)这个包。它的解决方案和上面提到的bmurauer很接近。

不过,你可能会:

  1. 觉得最佳模型的DataFrame输出很有帮助,因为它默认跳过了时间信息
  2. 觉得这三个使用示例很有用
  3. 喜欢大约100行的简短核心代码

我开发了HyperclassifierSearch包(可以用pip install HyperclassifierSearch来安装),这个包的代码是基于David Batista的代码,我喜欢它的简洁性。

关于第1点,使用hyperclassifier的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
6

这是解决这个问题的另一个简单方法。

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

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]

接下来,最重要的部分来了: 为所有的模型/分类器或估计器创建字符串名称: 这将用于创建数据框以便于比较

model_log=["_rf", "_ex", "_gb", "_svc"]

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

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_) "这段代码会为每个使用的估计器单独创建数据框,可以通过排序来比较,并挑选出每个估计器的最佳参数。

希望这对你有帮助。

8

这是我怎么做的,没有使用包装函数。你可以评估任意数量的分类器。每个分类器可以有多个参数,用于优化超参数。

得分最高的分类器会使用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')

24

虽然dubek提供的解决方案比较直接,但它没有解决分类器之前的管道元素之间参数的相互影响。因此,我写了一个辅助类来处理这个问题,可以在scikit的默认管道设置中使用。下面是一个简单的例子:

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来安装。

11

与其使用网格搜索来选择超参数,不如试试“hyperopt”库

可以看看这个页面的第2.2节。在这种情况下,你可以用hp.choice这个表达式来选择不同的处理流程,然后为每个处理流程单独定义参数表达式。

在你的目标函数中,需要根据选择的处理流程进行检查,并返回所选处理流程和参数的交叉验证得分(可以通过cross_val_score来实现)。

执行结束时的试验对象会显示出最佳的处理流程和参数。

撰写回答