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

Posted

技术标签:

【中文标题】具有单独训练和验证集的 GridSearchCV 错误地考虑了最终选择最佳模型的训练结果【英文标题】:GridSeachCV with separate training & validation sets erroneously takes also into account the training results for finally choosing the best model 【发布时间】:2019-03-05 20:46:42 【问题描述】:

我有一个包含 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])然后在my_precision 函数中从print(len(y_true)) 打印以下内容:

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) 你的 train_valid_split 函数又是用词不当 - 你实际上是在 合并 集合,当然不是拆分它们。不太确定您到底想在这里完成什么,或者为什么要将GridSearchCV(基于,嗯,CV)与训练/验证拆分方法混合,原则上是完全不同的... @desertnaut, 1) 好的,我修改了它。我希望这会更好! 2) 我只是合并数据集,以便能够在网格搜索中提供它们,并使用 PredefineSplit 我指定在这个合并集中哪个是训练集,哪个是验证集。这是您可以通过GridSearchCV 使用您自己的验证集的唯一方法。 @desertnaut,现在更清楚了吗?顺便说一句,你觉得我的要求(“只根据验证集结果挑选出最好的超参数集”)是绝对不合理的吗? 不仅不是不合理,而且完全是常态,像GridSearchCV这样的函数默认是做什么的!这就是为什么我仍然很困惑为什么你在这里混合东西(即 CV 与培训/验证拆分)...... 【参考方案1】:

我有一个不同的训练集和一个不同的验证集。我想在训练集上训练我的模型,并根据它在不同验证集上的表现找到最佳超参数。

那么你肯定既不需要PredefinedSplit 也不需要GridSearchCV

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 将包含您各自分类器在您的验证集上的性能...

【讨论】:

感谢您的回答(点赞)。但是,请记住,这个小参数网格 (n_estimators = [150, 175, 200]) 仅用于开始,因此我将进行更广泛的网格搜索,从这个意义上说,我认为使用 GridSearchCV 会更好。因此,如果我想将GridSearchCV 与我的单独验证集一起使用,那么我可能不得不使用PredefinedSplit (***.com/questions/31948879/…)。 (抱歉重复)但是问题是,如果您想使用GridSearchCV 进行网格搜索并使用您自己的单独验证集(因此也使用PredefinedSplit)然后看来GridSearchCV 也考虑到了训练集的结果(除非我在上面的评分函数中做了这个技巧,并且我自己将所有的训练结果都设置为0) @PoeteMaudit 顾名思义,GridSearchCV(至少在原则上)适用于预定义的训练/验证集方法(这些方法与简历);即使对于更广泛的网格搜索,您也可以简单地使用嵌套的 for 循环,可能会增加 perf 列表以进行簿记... 嗯,好的,即使使用for 循环就像从头开始编写修改后的GridSearchCV。我认为最快的做法是我对PredefinedSplit 所做的事情,并创建自己的评分函数,只需自己将所有训练结果设置为 0(并保持验证结果不变以进行比较)。 抱歉我的坚持,但我一直想知道高级数据科学家对某些库或算法如何工作等的看法。令人惊讶的是,我可能会想出一些即使是高级数据科学家也不完全清楚的案例遇到(单独的验证集,GridSearchCVPredefinedSplit,训练和验证结果)。无论如何,非常感谢您的宝贵时间:)【参考方案2】:

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

这可能是真的。

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

但这可能不是真的。

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

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

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

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

默认情况下会这样做。

【讨论】:

以上是关于具有单独训练和验证集的 GridSearchCV 错误地考虑了最终选择最佳模型的训练结果的主要内容,如果未能解决你的问题,请参考以下文章

使用 Imblearn 管道和 GridSearchCV 进行交叉验证

在 GridSearchCV 中显式指定测试/训练集

在 gridsearchcv sklearn 中进行训练和验证集

如何在 GridSearchCV 的 keras 模型的超参数优化中使用简单的验证集?

如何使用不同的数据集进行 GridSearchCV 训练和测试?

验证集与测试集的区别