从 GridSearchCV 中检索特定的分类器和数据

Retrieving specific classifiers and data from GridSearchCV

我是运行一个Python服务器上的3分类脚本,代码如下:

# define knn classifier for transformed data
knn_classifier = neighbors.KNeighborsClassifier()

# define KNN parameters
knn_parameters = [{
    'n_neighbors': [1,3,5,7, 9, 11],
    'leaf_size': [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60],
    'algorithm': ['auto', 'ball_tree', 'kd_tree', 'brute'],
    'n_jobs': [-1],
    'weights': ['uniform', 'distance']}]

# Stratified k-fold (default for classifier)
# n = 5 folds is default
knn_models = GridSearchCV(estimator = knn_classifier, param_grid = knn_parameters, scoring = 'accuracy')

# fit grid search models to transformed training data
knn_models.fit(X_train_transformed, y_train)

然后我使用 pickle:

保存 GridSearchCV 对象
# save model
with open('knn_models.pickle', 'wb') as f:
    pickle.dump(knn_models, f)

所以我可以通过 运行:

在本地机器上的较小数据集上测试分类器
knn_models = pickle.load(open("knn_models.pickle", "rb"))
validation_knn_model = knn_models.best_estimator_

如果我只想评估验证集上的最佳估计器,这很好。但我真正想做的是:

GridSearchCV 不包含原始数据(如果包含原始数据,则可以说是荒谬的)。它包含的唯一数据是它自己的簿记,即每次 CV 折叠尝试的详细分数和参数。 best_estimator_ returned 是将模型应用于遇到的任何新数据唯一需要的东西,但是如果像您所说的那样,您想更深入地挖掘细节,完整的结果是 returned 在其 cv_results_ 属性中。

documentation 中的示例与您自己的 knn_parameters 网格适配到 knn 分类器(但删除 n_jobs,这只会影响拟合速度,而不是真正的超参数算法),并为简单起见保持 cv=3,我们有:

from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import GridSearchCV
import pandas as pd

iris = load_iris()
knn_parameters = [{
    'n_neighbors': [1,3,5,7, 9, 11],
    'leaf_size': [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60],
    'algorithm': ['auto', 'ball_tree', 'kd_tree', 'brute'],
    'weights': ['uniform', 'distance']}]

knn_classifier = KNeighborsClassifier()
clf = GridSearchCV(estimator = knn_classifier, param_grid = knn_parameters, scoring = 'accuracy', n_jobs=-1, cv=3)
clf.fit(iris.data, iris.target)

clf.best_estimator_
# result:
KNeighborsClassifier(algorithm='auto', leaf_size=5, metric='minkowski',
                     metric_params=None, n_jobs=None, n_neighbors=5, p=2,
                     weights='uniform')

因此,如前所述,最后一个结果告诉您将算法应用于任何新数据(验证、测试、部署等)所需的全部信息。此外,您可能会发现实际上从 knn_parameters 网格中删除 n_jobs 条目并在 GridSearchCV 对象中请求 n_jobs=-1 会导致 much 更快的 CV 程序。不过,如果您想对最终模型使用 n_jobs=-1,您可以轻松地操纵 best_estimator_ 来实现:

clf.best_estimator_.n_jobs = -1
clf.best_estimator_
# result
KNeighborsClassifier(algorithm='auto', leaf_size=5, metric='minkowski',
                     metric_params=None, n_jobs=-1, n_neighbors=5, p=2,
                     weights='uniform')

这实际上回答了您的第二个问题,因为您也可以类似地操纵 best_estimator_ 来更改其他超参数。

因此,找到最佳模型是大多数人会停下来的地方。但是,如果出于任何原因,您想进一步深入了解整个网格搜索过程的细节,详细结果会在 cv_results_ 属性中 return 编辑,您甚至可以将其导入 pandas 数据框以便于检查:

cv_results = pd.DataFrame.from_dict(clf.cv_results_)

例如,cv_results 数据框包含一列 rank_test_score,正如其名称所暗示的那样,它包含每个参数组合的排名:

cv_results['rank_test_score']
# result:
0      481
1      481
2      145
3      145
4        1
      ... 
571      1
572    145
573    145
574    433
575      1
Name: rank_test_score, Length: 576, dtype: int32

这里的 1 表示最好的,你可以很容易地看到有不止一种组合被评为 1 - 所以实际上我们有不止一种“最佳”模型(即参数组合)!虽然这很可能是由于所用鸢尾花数据集相对简单,但原则上没有理由不能在真实案例中发生。在这种情况下,returned best_estimator_ 只是这些事件中的第一个 - 这里的组合数字 4:

cv_results.iloc[4]
# result:
mean_fit_time                                              0.000669559
std_fit_time                                               1.55811e-05
mean_score_time                                             0.00474652
std_score_time                                             0.000488042
param_algorithm                                                   auto
param_leaf_size                                                      5
param_n_neighbors                                                    5
param_weights                                                  uniform
params               {'algorithm': 'auto', 'leaf_size': 5, 'n_neigh...
split0_test_score                                                 0.98
split1_test_score                                                 0.98
split2_test_score                                                 0.98
mean_test_score                                                   0.98
std_test_score                                                       0
rank_test_score                                                      1
Name: 4, dtype: object

你可以很容易地看到它与我们上面的 best_estimator_ 具有相同的参数。但现在您可以通过以下方式检查所有“最佳”模型:

cv_results.loc[cv_results['rank_test_score']==1]

在我的案例中,产生不少于 144 个模型(在 6*12*4*2 = 576 尝试的模型总数中)!所以,实际上你可以 select 在更多的选择中,甚至使用其他额外的标准,比如 returned 分数的标准差(越少越好,虽然这里是最小值0), 而不是简单地依赖最大平均分数,这是自动程序将 return.

希望这些足以让您入门...