也许你可以这样想。假设您有一个数据集,其中有 100 个样本,“A”类中有 90 个,“B”类中有 10 个。在这种非常不平衡的设计中,如果您进行正常的随机分组,您最终可能会在“B”类的极少数(或什至没有!)上构建模型。如果您正在构建一个模型,该模型是在其他类别中很少甚至没有的数据上进行训练的,您如何期望它能够有效地预测稀有组?分层交叉验证允许随机化,但也确保这些不平衡的数据集具有两个类中的一些。
为了消除对使用具有更“平衡”数据集的分层 CV 的担忧,让我们看一个使用 R 代码的示例。
require(mlbench)
require(caret)
require(cvTools)
# using the Sonar dataset (208 samples)
data(Sonar)
# see the distribution of classes are very well balanced
prop.table(table(Sonar$Class))
> prop.table(table(Sonar$Class))
M R
0.5336538 0.4663462
# stratified
# set seed for consistency
# caret::createFolds does stratified folds by default
set.seed(123)
strat <- createFolds(Sonar$Class, k=10)
# non-stratified using cvTools
set.seed(123)
folds <- cvFolds(nrow(Sonar), K=10, type="random")
df <- data.frame(fold = folds$which, index = folds$subsets)
non_strat <- lapply(split(df, df$fold), FUN=function(x) x$index)
# calculate the average class distribution of the folds
strat_dist <- colMeans(do.call("rbind", lapply(strat, FUN = function(x) prop.table(table(Sonar$Class[x])))))
non_strat_dist <- colMeans(do.call("rbind", lapply(non_strat, FUN = function(x) prop.table(table(Sonar$Class[x])))))
strat_dist
> strat_dist
M R
0.5338312 0.4661688
non_strat_dist
> non_strat_dist
M R
0.5328571 0.4671429
如您所见,在一个平衡良好的数据集中,褶皱将随机出现相似的分布。因此,在这些情况下,分层 CV 只是一种保证措施。但是,要解决差异,您需要查看每个折叠的分布。在某些情况下(甚至从 50-50 开始),您可能会随机产生 30-70 的折叠(您可以运行上面的代码并看到这实际上发生了!)。这可能会导致模型性能更差,因为它没有足够的一类来准确预测它,从而增加了整体 CV 方差。当您拥有“有限”样本时,这显然更重要,因为您更有可能在分布上有非常极端的差异。
现在对于非常大的数据集,可能不需要分层,因为折叠将足够大,仍然可能至少包含很大比例的“稀有”类。但是,如果您的样本不平衡,无论您有多少数据,我个人认为 ,实际上没有计算损失,也没有真正的理由放弃分层。