带有单独的训练和验证集的GridSeachCV错误地考虑了训练结果,最终选择了最佳模型

时间:2018-09-30 15:25:42

标签: python machine-learning scikit-learn cross-validation grid-search

我有一个3500个观测值x 70个特征的数据集,这是我的训练集;我还有一个600个观测值x 70个特征的数据集,这是我的验证集。目标是将观察结果正确分类为0或1。

我使用Xgboost,我的目标是在分类阈值= 0.5时达到最高的精度。

我正在进行网格搜索:

import numpy as np
import pandas as pd
import xgboost

# Import datasets from edge node
data_train = pd.read_csv('data.csv')
data_valid = pd.read_csv('data_valid.csv')

# Specify 'data_test' as validation set for the Grid Search below
from sklearn.model_selection import PredefinedSplit
X, y, train_valid_indices = train_valid_merge(data_train, data_valid)
train_valid_merge_indices = PredefinedSplit(test_fold=train_valid_indices)

# Define my own scoring function to see
# if it is called for both the training and the validation sets
from sklearn.metrics import make_scorer
custom_scorer = make_scorer(score_func=my_precision, greater_is_better=True, needs_proba=False)

# Instantiate xgboost
from xgboost.sklearn import XGBClassifier
classifier = XGBClassifier(random_state=0)

# Small parameters' grid ONLY FOR START
# I plan to use way bigger parameters' grids 
parameters = {'n_estimators': [150, 175, 200]}

# Execute grid search and retrieve the best classifier
from sklearn.model_selection import GridSearchCV
classifiers_grid = GridSearchCV(estimator=classifier, param_grid=parameters, scoring=custom_scorer,
                                   cv=train_valid_merge_indices, refit=True, n_jobs=-1)
classifiers_grid.fit(X, y)

............................................... .............................

train_valid_merge-指定我自己的验证集:

我想用我的训练集(data_train)对每个模型进行训练,并使用我的独特/单独的验证集(data_valid)对超参数进行调整。因此,我定义了一个名为train_valid_merge的函数,该函数将我的训练和验证集连接起来,以便可以将它们输入到GridSeachCV中,并且我还使用了PredefineSplit来指定哪个是训练和这是此合并集上的验证集:

def train_valid_merge(data_train, data_valid):

    # Set test_fold values to -1 for training observations
    train_indices = [-1]*len(data_train)

    # Set test_fold values to 0 for validation observations
    valid_indices = [0]*len(data_valid)

    # Concatenate the indices for the training and validation sets
    train_valid_indices = train_indices + valid_indices

    # Concatenate data_train & data_valid
    import pandas as pd
    data = pd.concat([data_train, data_valid], axis=0, ignore_index=True)
    X = data.iloc[:, :-1].values
    y = data.iloc[:, -1].values
    return X, y, train_valid_indices

............................................... .............................

custom_scorer-指定我自己的得分指标:

我定义了自己的评分函数,该函数仅返回精度,以查看是否同时针对训练和验证集调用了该精度:

def my_precision(y_true, y_predict):

    # Check length of 'y_true' to see if it is the training or the validation set
    print(len(y_true))

    # Calculate precision
    from sklearn.metrics import precision_score
    precision = precision_score(y_true, y_predict, average='binary')

    return precision

............................................... .............................

运行整个程序(对于parameters = {'n_estimators': [150, 175, 200]}时,将在print(len(y_true))函数中从my_precision打印以下内容:

600
600
3500
600
3500
3500

,这意味着训练和验证集都会调用评分功能。但是我已经测试过,不仅可以调用计分功能,还可以使用训练集和验证集的结果来确定网格搜索中的最佳模型(即使我已将其指定为仅使用验证集的结果)。

例如,使用我们的3个参数值('n_estimators': [150, 175, 200]),它同时考虑了训练和验证集(2套)的得分,因此得出(3个参数)x(2套)= 6个不同的网格结果。因此,它会从所有这些网格结果中挑选出最佳的超参数集,因此最终可能会从训练集中的结果中挑选出一个,而我只想考虑验证集(3个结果)。

但是,如果我在my_precision函数中添加类似内容来规避训练集(通过将其所有精度值都设置为0):

# Remember that the training set has 3500 observations
# and the validation set 600 observations
if(len(y_true>600)):
    return 0

然后(据我测试),我当然可以为我的规范找到最佳模型,因为训练集的精度结果太小,因为它们全都为0至。

我的问题如下:

为什么自定义评分功能同时考虑了训练和验证集以选择最佳模型,而我已train_valid_merge_indices指定网格搜索的最佳模型应仅是根据验证集选择的?

完成模型的选择和排名后,如何使GridSearchCV仅考虑验证集和模型的得分?

2 个答案:

答案 0 :(得分:1)

  

我有一套独特的训练套和一套独特的验证套。我想在训练集上训练我的模型,并根据我在不同验证集上的性能找到最佳超参数。

那么您最肯定不需要PredefinedSplitGridSearchCV

import pandas as pd
from xgboost.sklearn import XGBClassifier
from sklearn.metrics import precision_score

# Import datasets from edge node
data_train = pd.read_csv('data.csv')
data_valid = pd.read_csv('data_valid.csv')

# training data & labels:
X = data_train.iloc[:, :-1].values
y = data_train.iloc[:, -1].values   

# validation data & labels:
X_valid = data_valid.iloc[:, :-1].values
y_true = data_valid.iloc[:, -1].values 

n_estimators = [150, 175, 200]
perf = []

for k_estimators in n_estimators:
    clf = XGBClassifier(n_estimators=k_estimators, random_state=0)
    clf.fit(X, y)

    y_predict = clf.predict(X_valid)
    precision = precision_score(y_true, y_predict, average='binary')
    perf.append(precision)

perf将包含验证集上各个分类器的性能...

答案 1 :(得分:0)

<块引用>

这意味着对训练集和验证集都调用了评分函数...

这可能是真的。

<块引用>

...但我已经测试过,评分函数不仅被调用,而且来自训练集和验证集的结果用于从网格搜索中确定最佳模型(即使我已指定它仅使用验证集结果)。

但这可能不是真的。

有一个参数return_train_score;当 True 时,对训练数据进行评分并将其作为 cv_results_ 属性的一部分返回。在 v0.21 之前,此参数的默认值为 True,而在 False 之后。但是,这些分数用于确定最佳超参数,除非您有客户 scoring 方法将它们考虑在内。 (如果您认为自己有反例,请提供 cv_results_best_params_。)

<块引用>

为什么自定义评分函数要同时考虑训练和验证集来挑选最佳模型,而我已经用我的 train_valid_merge_indices 指定网格搜索的最佳模型应该只根据验证集?

它(可能)不是,见上文。

<块引用>

如何让 GridSearchCV 在模型的选择和排名完成时只考虑验证集和模型的分数?

它默认这样做。