我有4个类的数据,我正在尝试构建一个分类器。我有一个类的〜1000个向量,另一个有~10 ^ 4,第三个为~10 ^ 5,第四个为~10 ^ 6。我希望使用交叉验证,所以我查看了scikit-learn docs。
我的第一次尝试是使用StratifiedShuffleSplit,但这会给每个班级留下相同的百分比,但这些班级仍然会严重失衡。
有没有办法进行交叉验证,但是平衡了类 培训和测试集?
作为旁注,我无法解决StratifiedShuffleSplit和StratifiedKFold之间的差异。描述与我非常相似。
答案 0 :(得分:20)
我的第一次尝试是使用StratifiedShuffleSplit但是这给了每个类相同的百分比,使得这些类仍然严重失衡。
我觉得你会混淆分层策略会做什么,但你需要显示你的代码和结果,以确定发生了什么(与原始集合中的百分比相同的百分比,或者在返回的火车/测试装置中的百分比相同?第一个是它应该如何)。
作为旁注,我无法弄清楚StratifiedShuffleSplit和StratifiedKFold之间的区别。描述与我非常相似。
其中一个绝对应该有用。对第一个的描述肯定有点令人困惑,但这就是他们所做的。
提供列车/测试索引以在列车测试集中分割数据。
这意味着它会将您的数据拆分为火车和测试集。分层部分意味着百分比将在此分割中保持。因此,如果您的数据的10%
位于第1级,而90%
位于第2级,则这将确保您的列车的10%
位于第1级且{{1将在第2课。测试集也一样。
你的帖子听起来好像你想要测试集中每个类的90%
。这不是分层所做的,分层保持了原始的百分比。你应该保留它们,因为否则你会给自己一个关于分类器性能的不相关的想法:谁在乎它对50%
分裂的分类程度,在实践中你会看到50/50
分裂?
此交叉验证对象是KFold的变体,可返回分层折叠。折叠是通过保留每个类别的样本百分比来实现的。
见k-fold cross validation。如果没有分层,它只会将您的数据拆分为10/90
个折叠。然后,每个折叠k
一次用作测试集,而其他折叠用于训练。结果平均到最后。它与运行1 <= i <= k
ShuffleSplit
次类似。
分层将确保整个数据中每个类别的百分比在每个单独的折叠中相同(或非常接近)。
有很多文献涉及不平衡的阶级。一些简单易用的方法涉及使用类权重和分析ROC曲线。我建议以下资源作为起点:
答案 1 :(得分:2)
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-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等方法对于计算抽样比例至关重要。