K 折 CV
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 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. 和 Tibshirani, R. (2013)。统计学习简介:在 R 中的应用。