对多个分类器进行网格搜索
有没有更好的内置方法来进行网格搜索,并在一个流程中测试多个模型呢?当然,不同模型的参数会不一样,这让我觉得有点复杂,不知道怎么做。以下是我尝试的:
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 个回答
另一个选择是使用HyperclassifierSearch (Github)这个包。它的解决方案和上面提到的bmurauer很接近。
不过,你可能会:
- 觉得最佳模型的DataFrame输出很有帮助,因为它默认跳过了时间信息
- 觉得这三个使用示例很有用
- 喜欢大约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
这是解决这个问题的另一个简单方法。
首先,加载所有的估计器。在这里,我主要会使用分类器。
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_) "这段代码会为每个使用的估计器单独创建数据框,可以通过排序来比较,并挑选出每个估计器的最佳参数。
希望这对你有帮助。
这是我怎么做的,没有使用包装函数。你可以评估任意数量的分类器。每个分类器可以有多个参数,用于优化超参数。
得分最高的分类器会使用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')
虽然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
来安装。
与其使用网格搜索来选择超参数,不如试试“hyperopt”库。
可以看看这个页面的第2.2节。在这种情况下,你可以用hp.choice
这个表达式来选择不同的处理流程,然后为每个处理流程单独定义参数表达式。
在你的目标函数中,需要根据选择的处理流程进行检查,并返回所选处理流程和参数的交叉验证得分(可以通过cross_val_score来实现)。
执行结束时的试验对象会显示出最佳的处理流程和参数。