使用 cross_val_score 和 GridSearchCV 时如何评估模型是过拟合还是欠拟合?

机器算法验证 机器学习 交叉验证
2022-04-20 09:21:04

这是已经被广泛写过的东西,但我只是对一些我没有找到明确解释的特定事情感到困惑。

当不使用交叉验证时,可以将数据拆分为训练和测试,并在训练集上进行训练。然后可以在两组上评估该模型,目标是在任何一组上都具有相似的性能,这意味着既不过度拟合也不过度拟合。

据我了解,当使用交叉验证时,这消除了拆分为训练集和测试集的需要,因为 CV 有效地执行了多次拆分(由折叠数定义)。但是,从交叉验证中获得的平均分数只返回一个分数。这应该被解释为前一个案例的训练或测试分数吗?或者都不是?我们如何判断模型是过拟合还是欠拟合?

我想知道这如何适应GridSearchCV,因为我已经读到您应该将数据拆分为训练集和验证集,以确认您的性能指标保持大致相同。这是必要的吗,因为我们可以假设模型没有过度/欠拟合,因为我们允许GridSearchCV选择最佳超参数?

此外,我在“Python 机器学习简介”中读到了一些令人困惑的内容,其中说数据应该分为 3 部分:训练、验证和测试。模型在训练集上进行训练,并在验证集上进行评估以选择最佳超参数,然后在 train+val 上训练最佳超参数,并在测试上进行评估。

2个回答

您需要检查每个折叠结果的训练集和测试集之间的准确度差异。如果您的模型为您提供高训练准确度但测试准确度低,那么您的模型过度拟合。如果您的模型没有提供良好的训练准确性,您可以说您的模型拟合不足。

GridSearchCV 正在尝试为您的模型找到最佳的超参数。为此,它将数据集分成三部分。它使用训练集作为训练部分,然后使用验证集测试您的数据,并根据验证集结果调整参数。最后,它使用测试集来获取最终的模型精度。

from sklearn.model_selection import KFold

kf = KFold(n_splits=5,random_state=42,shuffle=True)


# these are you training data points:
# features and targets
X = ....
y = ....

accuracies = []

for train_index, test_index in kf.split(X):

    data_train   = X[train_index]
    target_train = y[train_index]

    data_test    = X[test_index]
    target_test  = y[test_index]

    # if needed, do preprocessing here

    clf = LogisticRegression()
    clf.fit(data_train,target_train)

    test_preds = clf.predict(data_test)
    test_accuracy = accuracy_score(target_test,test_preds)

    train_preds = clf.predict(data_train)
    train_accuracy = accuracy_score(target_train, train_preds)

    print(train_accuracy, test_accuracy, (train_accuracy - test_accuracy) )

    accuracies.append(accuracy)

# this is the average accuracy over all folds
average_accuracy = np.mean(accuracies)

我试图回答你问的每一个问题。

.......但是,从交叉验证中获得的平均分数只返回一个分数。这应该被解释为前一个案例的训练或测试分数吗?或者都不是?

我不确定您使用哪个库或包进行交叉验证。我假设您正在使用cross_val_score方法(因为它在教程中被广泛使用)。该方法将训练集分成 k 折。在 k-1 折上进行训练,剩余的折用作测试集来计算性能因此,从某种意义上说,您没有为模型选择进行交叉验证,您从交叉验证中获得的平均准确度是对测试准确度的估计,您可以将此称为测试分数,而不是训练分数。

.....上一个案例的得分?或者都不是?我们如何判断模型是过拟合还是欠拟合?

我认为您关于模型过度拟合的问题与交叉验证有关。通过比较测试和训练的准确性,您可以轻松了解您的模型是否过度拟合。但是,对于 k 折中的每一个,cross_val_score为您提供测试准确度,而不是训练准确度。因此,您应该使用 sklearn 的cross_validate,它返回一个包含 test-score 和 others 的字典如果您还想获得训练分数,您只需将return_train_score参数值设置为True

代码片段如下:

scores = cross_validate(rdn_forest_clf, train_features, train_lables, cv=5, scoring='f1', return_train_score=True)
print(scores.keys())  # Will print dict_keys(['fit_time', 'score_time', 'test_score', 'train_score'])
print(scores["train_score"])  # Will print your training score
print(scores["test_score"])  # Will print test score

.........确认您的绩效指标保持大致相同。这是必要的,因为我们可以假设模型没有过度/欠拟合,因为我们允许 GridSearchCV 选择最佳超参数?

超参数网格上的交叉验证并不能保证您不会过度拟合。因此,要检查GridSearchCV找到的模型是否过拟合,可以使用cv_results_属性GridSearchCVcv_results_是一个字典,其中包含参数网格中给出的每个参数组合的详细信息(例如 mean_test_score、mean_score_time 等) 。要获得与训练分数相关的值(例如 mean_train_score、std_train_score 等),您必须 return_train_score = True 通过默认值 false

这是一段代码,用于获取每个参数组合的平均训练和测试精度。

param_grid = {'n_estimators': [4, 5, 10, 15, 20, 30, 40, 60, 190, 500, 800], 'max_depth': [3, 4, 5, 6]}
grid_search_m = GridSearchCV(clf, param_grid, cv=5, scoring='f1', return_train_score=True)
grid_search_m.fit(train_features, train_lables)
print(grid_search_m.cv_results_.keys())
print(grid_search_m.cv_results_["mean_train_score"].shape)  # n_estimators: 11 values, max_depth: 4 values. Thus shape, 11*4=44
print(grid_search_m.cv_results_["mean_test_score"].shape)

print(grid_search_m.cv_results_["mean_train_score"])
print(grid_search_m.cv_results_["mean_test_score"])

然后,比较训练和测试的准确性,您可以确定您的模型是否过度拟合。您还可以检查不同的 SE 问题以找到此比较的策略。

此外,我读过一些令人困惑的东西......模型在训练集上进行训练,并在验证集上进行评估以选择最佳超参数,然后在 train+ 上训练最佳超参数val,并在测试中评估。

这也可以在处理 ML 模型期间完成,即使用验证集而不是交叉验证。然而,为了避免在验证集中“浪费”过多的训练数据,一种常见的技术是使用交叉验证