我有一个数据集,为简单起见,假设它有 1000 个样本(每个样本都是一个向量)。
我想分割我的数据进行交叉验证,用于训练和测试,不是随机1,所以例如,如果我想要4倍交叉验证,我应该得到:
fold1:火车= 1:250;测试= 251:1000
fold2:训练 = 251:500,测试 = [1:250 ; 501:1000]
fold3:训练= 501:750,测试= [1:500; 751:1000]
fold4:训练 = 751:1000,测试 = 1:750
我知道CVPARTITION,但是据我所知 - 它随机分割数据 - 这不是我需要的。
我想我可以为其编写代码,但我想可能有一个我可以使用的函数。
(1) 数据已经被打乱,我需要能够轻松地重现实验。
这里是一个通用的函数:
function [test, train] = kfolds(data, k)
n = size(data,1);
test{k,1} = [];
train{k,1} = [];
chunk = floor(n/k);
test{1} = data(1:chunk,:);
train{1} = data(chunk+1:end,:);
for f = 2:k
test{f} = data((f-1)*chunk+1:(f)*chunk,:);
train{f} = [data(1:(f-1)*chunk,:); data(f*chunk+1:end, :)];
end
end
它不是一个优雅的 1 衬垫,但它相当强大,不需要
k
成为样本数量的一个因素,在 2D 矩阵上工作并输出实际的集合而不是索引。
假设您有
k*n
样本,想要将其划分为 k
折叠,训练中的 n
样本和测试中的 (k-1)*n
样本(在您的问题中 k = 4
,n = 250
)。 >> foldId = kron( 1:k, ones(1,n) );
foldId
为您提供每个样本所属的训练折叠的索引。
对于折叠
f
,您可以使用获取训练和测试样本的索引
>> trainIdx = find( foldId == f );
>> testIdx = find( foldId ~= f );
(您可以使用逻辑索引代替
find
并加快速度)。
要将数据集划分为
k
长度为 n
的折叠,您可以使用:
f=arrayfun(@(x)struct('train',x*n+(1:n),'test',setdiff(1:n*k,x*n+(1:n))), 0:k-1);
其中
f
是一个结构数组,其中字段 train
和 test
包含相应折叠的索引。
例如,对于 n=5
和 k=3
并折叠 2
:
>> f(2).train
ans =
6 7 8 9 10
>> f(2).test
ans =
1 2 3 4 5 11 12 13 14 15
您甚至可以直接提取数据。假设您的数据是
n*k
行 的二维矩阵
E=arrayfun(...
@(x) struct('train', D(x*n+(1:n),:), ...
'test', D(setdiff(1:n*k, x*n+(1:n)),:)), 0:k-1)
假设您的数据是
D = [(1:15).^2; (1:15).^3].';
对于折叠
2
,E
包含:
>> E(2).train
ans =
36 216
49 343
64 512
81 729
100 1000
>> E(2).test
ans =
1 1
4 8
9 27
16 64
25 125
121 1331
144 1728
169 2197
196 2744
225 3375
自 Matlab 2023 起,您现在可以使用自定义 cvpartition。例如,来自 Matlab 文档:
c = cvpartition("CustomPartition",[1 2 2 1 3 3 1 2 3 2]) 表示 3 倍交叉验证方案,第一个测试集中有第一个、第四个和第七个观察值。
c = cvpartition("CustomPartition",[true false true false false]) 表示保留验证方案,其中第一个和第三个观测值位于测试集中。
您可以使用
kron( 1:k, ones(1,n) )
生成数字向量,如 Shai 的答案所示,其中 k 是折叠数,n 是每个折叠中的样本数。