k-fold分层交叉验证与不平衡类

时间:2015-09-16 17:52:27

标签: python machine-learning scikit-learn

我有4个类的数据,我正在尝试构建一个分类器。我有一个类的〜1000个向量,另一个有~10 ^ 4,第三个为~10 ^ 5,第四个为~10 ^ 6。我希望使用交叉验证,所以我查看了scikit-learn docs

我的第一次尝试是使用StratifiedShuffleSplit,但这会给每个班级留下相同的百分比,但这些班级仍然会严重失衡。

  

有没有办法进行交叉验证,但是平衡了类   培训和测试集?

作为旁注,我无法解决StratifiedShuffleSplitStratifiedKFold之间的差异。描述与我非常相似。

2 个答案:

答案 0 :(得分:20)

  

我的第一次尝试是使用StratifiedShuffleSplit但是这给了每个类相同的百分比,使得这些类仍然严重失衡。

我觉得你会混淆分层策略会做什么,但你需要显示你的代码和结果,以确定发生了什么(与原始集合中的百分比相同的百分比,或者在返回的火车/测试装置中的百分比相同?第一个是它应该如何)。

  

作为旁注,我无法弄清楚StratifiedShuffleSplit和StratifiedKFold之间的区别。描述与我非常相似。

其中一个绝对应该有用。对第一个的描述肯定有点令人困惑,但这就是他们所做的。

StratifiedShuffleSplit

  

提供列车/测试索引以在列车测试集中分割数据。

这意味着它会将您的数据拆分为火车和测试集。分层部分意味着百分比将在此分割中保持。因此,如果您的数据的10%位于第1级,而90%位于第2级,则这将确保您的列车的10%位于第1级且{{1将在第2课。测试集也一样。

你的帖子听起来好像你想要测试集中每个类的90%。这不是分层所做的,分层保持了原始的百分比。你应该保留它们,因为否则你会给自己一个关于分类器性能的不相关的想法:谁在乎它对50%分裂的分类程度,在实践中你会看到50/50分裂?

StratifiedKFold

  

此交叉验证对象是KFold的变体,可返回分层折叠。折叠是通过保留每个类别的样本百分比来实现的。

k-fold cross validation。如果没有分层,它只会将您的数据拆分为10/90个折叠。然后,每个折叠k一次用作测试集,而其他折叠用于训练。结果平均到最后。它与运行1 <= i <= k ShuffleSplit次类似。

分层将确保整个数据中每个类别的百分比在每个单独的折叠中相同(或非常接近)。

有很多文献涉及不平衡的阶级。一些简单易用的方法涉及使用类权重和分析ROC曲线。我建议以下资源作为起点:

  1. A scikit-learn example of using class weights
  2. A quora question about implementing neural networks for imbalanced data
  3. This stats.stackexchange question with more in-depth answers

答案 1 :(得分:2)

K-Fold CV

K-Fold CV通过将数据随机分区为k(相当)相等的分区来工作。如果您的数据在[0,1,0,1,0,1,0,1,0,1]等类之间均衡平衡,则随机抽样(或不进行替换)将为您提供大约等量的01样本。

但是,如果你的数据更像 [0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0] 如果一个班级代表数据,那么没有加权抽样的k-fold cv会给你错误的结果。

如果您使用普通的k-fold CV而不调整均匀采样的采样权重,那么您将获得类似

的内容
## k-fold CV
k = 5
splits = np.array_split(y, k)
for i in range(k):
    print(np.mean(splits[i]))

 [array([0, 0, 0, 0, 0, 0, 0]),
 array([0, 0, 0, 0, 0, 0, 0]),
 array([0, 0, 0, 0, 0, 0]),
 array([0, 0, 0, 0, 0, 0]),
 array([0, 1, 1, 1, 1, 1])]

其中有明显的分裂而没有两个类的有用表示。

k-fold CV的目的是在所有数据子集上训练/测试模型,而在每次试验中,留下1个子集并训练k-1个子集。

在这种情况下,您希望使用分层分割。在上面的数据集中,有27 0s和5 1s。如果您想计算k = 5 CV,将1的层次划分为5个子集是不合理的。更好的解决方案是将其分解为k <1。 5个子集,例如2. 0s的层可以保留k = 5个分裂,因为它更大。然后在训练时,您从数据集中获得了2 x 5的简单产品。这里有一些代码来说明

from itertools import product

for strata, iterable in groupby(y):
    data = np.array(list(iterable))
    if strata == 0:
        zeros = np.array_split(data, 5)
    else:
        ones = np.array_split(data, 2)


cv_splits = list(product(zeros, ones))
print(cv_splits)

m = len(cv_splits)
for i in range(2):
    for j in range(5):
        data = np.concatenate((ones[-i+1], zeros[-j+1]))
        print("Leave out ONES split {}, and Leave out ZEROS split {}".format(i,j))
        print("train on: ", data)
        print("test on: ", np.concatenate((ones[i], zeros[j])))



Leave out ONES split 0, and Leave out ZEROS split 0
train on:  [1 1 0 0 0 0 0 0]
test on:  [1 1 1 0 0 0 0 0 0]
Leave out ONES split 0, and Leave out ZEROS split 1
train on:  [1 1 0 0 0 0 0 0]
...
Leave out ONES split 1, and Leave out ZEROS split 4
train on:  [1 1 1 0 0 0 0 0]
test on:  [1 1 0 0 0 0 0]

此方法可以将数据拆分为分区,最终省略所有分区以进行测试。应该注意的是,并非所有统计学习方法都允许加权,因此调整CV等方法对于计算抽样比例至关重要。

  • 参考文献:James,G.,Witten,D.,Hastie,T。,&amp; Tibshirani,R。(2013年)。统计学习简介:使用R。
  • 中的应用程序