为啥 10 倍交叉验证的准确度得分比使用 sklearn 的 90-10 train_test_split 最差?

Posted

技术标签:

【中文标题】为啥 10 倍交叉验证的准确度得分比使用 sklearn 的 90-10 train_test_split 最差?【英文标题】:How is it that the accuracy score for 10-fold cross validation is worst than for a 90-10 train_test_split using sklearn?为什么 10 倍交叉验证的准确度得分比使用 sklearn 的 90-10 train_test_split 最差? 【发布时间】:2021-11-05 02:08:41 【问题描述】:

任务是通过神经网络进行二元分类。数据存在于字典中,其中包含每个条目的复合名称(作为键)和标签(0 或 1,作为向量值中的第三个元素)。第一个和第二个元素是复合名称的两部分,后面用来提取对应的特征。

在这两种情况下,为了对多数类(存在于 66% 的数据中)执行平衡的欠采样,字典都被转换为两个数组:

data_for_sampling = np.asarray([key for key in list(data.keys())])
labels_for_sampling = [element[2] for element in list(data.values())]

sampler = RandomUnderSampler(sampling_strategy = 'majority')
data_sampled, label_sampled = sampler.fit_resample(data_for_sampling.reshape(-1, 1), labels_for_sampling)

然后使用重采样的名称和标签数组通过 Kfold 方法创建训练集和测试集:

kfolder = KFold(n_splits = 10, shuffle = True)
kfolder.get_n_splits(data_sampled)

for train_index, test_index in kfolder.split(data_sampled):

        data_train, data_test = data_sampled[train_index], data_sampled[test_index]

或者 train_test_split 方法:

data_train, data_test, label_train, label_test = train_test_split(data_sampled, label_sampled, test_size = 0.1, shuffle = True)

最后,来自 data_train 和 data_test 的名称用于从原始字典中重新提取相关条目(按键),然后用于收集这些条目的特征。就我而言,10 倍集的单个拆分应该提供与 90-10 train_test_split 类似的训练测试数据分布,这在训练期间似乎是正确的,两个训练集的准确度都约为 0.82仅在一个 epoch 之后,使用 model.fit() 单独运行。然而,当相应的模型在所述时期之后的测试集上使用 model.evaluate() 进行评估时,来自 train_test_split 的集合给出了~0.86 的分数,而来自 Kfold 的集合是~0.72。我做了很多测试,看看它是否只是一个坏的随机种子,它没有界限,但结果保持不变。这些集合还具有正确平衡的标签分布和看似良好的混洗条目。

【问题讨论】:

【参考方案1】:

根据数据中的噪声量和数据集的大小,这可能是预期的行为,即看到样本外数据的分数偏离这个量。不能保证一个拆分与任何其他拆分一样,这就是为什么您首先有 10 个,然后对所有结果进行平均。

你应该相信最普遍的是不是任何一个给定的拆分(无论是来自 10 折之一还是train_test_split()),但更值得信赖的是 所有 N 折的平均结果。

深入挖掘数据可以揭示一个或多个拆分与另一个拆分如此大的差异是否存在某种原因。例如,您的数据中可能存在某些特征(例如,“收集样本的日期”和每月更改的收集方法)使数据以一种有偏见的方式彼此不同。如果是这种情况,您应该使用分层测试拆分(在您的简历中也是如此)(请参阅scikit-learn documentation on that),这样您就可以获得更公正的数据分组。

【讨论】:

【参考方案2】:

事实证明,问题源于多种来源:

虽然 train_test_split() 方法中的 shuffle = True 首先正确地打乱提供的数据,然后将其拆分为所需的部分,但 Kfold 方法中的 shuffle = True 只会导致随机构建的折叠,但是 数据折叠内保持有序

感谢这篇文章,文档指出了这一点: https://github.com/scikit-learn/scikit-learn/issues/16068

现在,在学习过程中,我的自定义训练函数再次对训练数据应用 shuffle,但它不会对测试数据进行随机排序。此外,model.evaluate() 默认为 batch_size = 32,如果没有给出参数,与有序的测试数据配对导致验证准确度的差异。测试数据确实存在缺陷,因为它包含大部分“难以预测”的条目,这些条目由于排序而聚集在一起,似乎它们拖累了结果的平均准确度。正如 TC Arlen 所指出的那样,给定所有 N 折的完整运行,最终可能确实给出了更精确的估计,但我预计仅在一次折叠后会得到更接近的结果,这导致了这个问题的发现。

【讨论】:

以上是关于为啥 10 倍交叉验证的准确度得分比使用 sklearn 的 90-10 train_test_split 最差?的主要内容,如果未能解决你的问题,请参考以下文章

在 sklearn 中运行 10 倍交叉验证后如何运行 SVC 分类器?

为啥 scikit-learn SVM 分类器交叉验证这么慢?

我的 r-squared 得分为负数,但我使用 k-fold 交叉验证的准确度得分约为 92%

为啥交叉验证的性能比测试差?

为啥我的交叉验证始终比训练测试分割表现更好?

使用交叉验证提高准确性,不使用交叉验证降低准确性