首页 文章

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

提问于
浏览
14

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

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

有没有办法进行交叉验证,但培训和测试集中的课程是否 balancer ?


作为旁注,我无法解决StratifiedShuffleSplitStratifiedKFold之间的区别 . 描述与我非常相似 .

2 回答

  • 20

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

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

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

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

    StratifiedShuffleSplit

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

    这意味着它将您的数据拆分为火车和测试集 . 分层部分意味着 percentages will be maintained in this split . 因此,如果您的数据的 10% 在类1中, 90% 在类2中, this will ensure that 10% of your train set will be in class 1 and 90% will be in class 2 . 对于测试集也是如此 .

    你的帖子听起来好像你想要测试集中每个类的 50% . 那不是给你自己一个关于你的分类器性能的无关的想法:谁在乎它如何分类 50/50 分裂,在实践中你会看到 10/90 分裂?

    StratifiedKFold

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

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

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


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

  • 2

    K-Fold CV

    K-Fold CV通过将数据随机分区为 k (相当)相等的分区来工作 . 如果您的数据在 [0,1,0,1,0,1,0,1,0,1] 等类之间均衡 balancer ,则随机抽样(或不进行替换)将为您提供大约等于 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倍CV的点是在所有数据子集上训练/测试模型,而在每次试验中留下1个子集并训练k-1个子集 .

    在这种情况下,您希望使用split by strata . 在上面的数据集中,有27个 0s 和5个 1s . 如果你将 1 的分层划分为5个子集是合理的 . 一个更好的解决方案是将其分成k <5个子集,例如2.由于's much larger. Then while training, you'从数据集中具有 2 x 5 的简单乘积,因此 0s 的层可以保持k = 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 . ,&Tibshirani,R . (2013) . 统计学习简介:在R中的应用程序

相关问题