更新训练/验证/测试集

数据挖掘 数据集 训练 验证 测试
2022-02-18 01:18:27

在数据科学/机器学习项目开始时将数据拆分为训练集和测试集被认为是最佳实践(然后将训练集进一步拆分为超参数优化的验证集)。

如果事实证明您的训练集中的分布与您的测试集中的分布不一样,可能是测试集中完全缺少一组,或者测试集中的一组过度代表,例如,您会怎么做?

在得知两组中的分布不同后重新计算您的训练集和测试集是否有问题?实际上,这一定没问题,而且您不会总是预先知道集合中的分布具有代表性。但是,这是一种数据泄漏形式,因为您将所学信息应用于创建这些集合,而这些集合不一定在您开始任务之前就已交付。

如何处理这种情况?

2个回答

有两种完全不同的场景:

训练和测试数据来自同一个数据集

如果数据是在训练集和测试集之间随机分配的,那么一开始就极不可能发生这种情况。如果数据包含一些小组/类,则不仅可以随机进行拆分,还可以进行分层抽样:这可以防止一个组的 100% 偶然出现在拆分的任一侧。如果有一些类或组仍然很少出现(只有一次或两次),那么这些通常应该在某些预处理阶段被丢弃或替换为一些通用值。

训练和测试数据独立获得

这是一个严重的情况。在某些情况下,外部约束使训练和测试集中的分布略有不同是不可避免的,即使这违反了监督学习的主要假设。请注意,如果分布差异太大,则很可能是失败的原因。在这种情况下,人们别无选择:如果训练集和测试集是分开提供的,那么预计性能是“按原样”在测试集上衡量的。所以这是一个处理这个约束的问题:一些特定的预处理可能是必要的(例如在训练集中引入一个特殊的组/类“未知”),一个健壮的方法可能是可取的,可能一些计划一个默认的预测(多数类)对于无效实例等

错误发生

无论如何,如果人们意识到拆分过程的设计有问题或任何其他问题导致需要重新洗牌数据,那么尽管存在数据泄漏的风险,有时最好重做整个过程。当然,如果可以避免这种情况会更好,但并不是说 ML 警察会逮捕你;)

在数据集不平衡的情况下,我们可以在拆分数据时使用分层,或使用交叉验证,或两者兼而有之。预先分层/CV有助于通过开发人员确认偏差来减轻这种数据泄漏。

分层

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
                                                    stratify=y, 
                                                    test_size=0.25)

交叉验证

>>> from sklearn.model_selection import cross_val_score
>>> clf = svm.SVC(kernel='linear', C=1, random_state=42)
>>> scores = cross_val_score(clf, X, y, cv=5)
>>> scores
    array([0.96..., 1. , 0.96..., 0.96..., 1. ])

在这个例子中,有 5 个随机拆分,5 个模型得分。

RepeatedStratifiedKFold 有帮助。

from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold

cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=2, random_state=42)
scores = cross_val_score(model, X, y, scoring='mean_squared_error', cv=cv, n_jobs=-1)
print('Mean MSE: %.4f' % mean(scores))

在这个例子中,有 5 次拆分和两次迭代,总共 10 个模型的总得分。