[英]k-fold stratified cross-validation with imbalanced classes
我有4个类的数据,我正在尝试构建一个分类器。 我有一个类的〜1000个向量,另一个有~10 ^ 4,第三个为~10 ^ 5,第四个为~10 ^ 6。 我希望使用交叉验证,所以我查看了scikit-learn文档 。
我的第一次尝试是使用StratifiedShuffleSplit但是这给了每个类相同的百分比,使得类仍然严重失衡。
有没有办法进行交叉验证,但是在训练和测试集中平衡了类?
作为旁注,我无法弄清楚StratifiedShuffleSplit和StratifiedKFold之间的区别。 描述与我非常相似。
我的第一次尝试是使用StratifiedShuffleSplit但是这给了每个类相同的百分比,使得类仍然严重失衡。
我觉得你会混淆分层策略会做什么,但你需要显示你的代码和结果,以确定发生了什么(与原始集合中的百分比相同的百分比,或相同返回的火车/测试集中的百分比?第一个是它应该如何)。
作为旁注,我无法弄清楚StratifiedShuffleSplit和StratifiedKFold之间的区别。 描述与我非常相似。
其中一个绝对应该工作。 对第一个的描述肯定有点令人困惑,但这就是他们所做的。
提供列车/测试索引以在列车测试集中分割数据。
这意味着它将您的数据拆分为火车和测试集。 分层部分意味着在这种分裂中将保持百分比 。 因此,如果10%
的数据属于1级, 90%
属于2级, 这将确保10%
的列车将在1级, 90%
将在2级 。 对于测试集也是如此。
你的帖子听起来好像你想要测试集中每个类的50%
。 这不是分层所做的,分层保持了原始的百分比。 你应该维护它们,因为否则你会给自己一个关于你的分类器性能的不相关的想法:谁在乎它对50/50
分割的分类,在实践中你会看到10/90
分裂?
此交叉验证对象是KFold的变体,可返回分层折叠。 通过保留每个类别的样本百分比来进行折叠。
参见k-fold交叉验证 。 没有分层,它只是将您的数据分成k
折叠。 然后,每个折叠1 <= i <= k
一次用作测试集,而其他折叠用于训练。 结果平均到最后。 它类似于运行ShuffleSplit
k
次。
分层将确保整个数据中每个类别的百分比在每个单独的折叠中相同(或非常接近)。
有很多文献涉及不平衡的阶级。 一些简单易用的方法涉及使用类权重和分析ROC曲线。 我建议以下资源为此起点:
K-Fold CV通过将数据随机分区为k
(相当)相等的分区来工作。 如果您的数据在[0,1,0,1,0,1,0,1,0,1]
等类之间均衡平衡,则随机抽样(或不进行替换)将为您提供大约等于0
和1
样本大小。
但是,如果您的数据更像是[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
。 如果你想计算k = 5 CV,将1
的分层分成5个子集是不合理的。 更好的解决方案是将其分成k <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等方法对于考虑采样比例至关重要。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.