MATLAB:不使用现有函数进行10倍交叉验证

3

我有一个矩阵(在MatLab中可能称为结构体)或数据结构:

  data: [150x4 double]
labels: [150x1 double]

以下是我的matrix.data文件的样子,假设我使用名为matrix的文件进行加载:

5.1000    3.5000    1.4000    0.2000
4.9000    3.0000    1.4000    0.2000
4.7000    3.2000    1.3000    0.2000
4.6000    3.1000    1.5000    0.2000
5.0000    3.6000    1.4000    0.2000
5.4000    3.9000    1.7000    0.4000
4.6000    3.4000    1.4000    0.3000
5.0000    3.4000    1.5000    0.2000
4.4000    2.9000    1.4000    0.2000
4.9000    3.1000    1.5000    0.1000
5.4000    3.7000    1.5000    0.2000
4.8000    3.4000    1.6000    0.2000
4.8000    3.0000    1.4000    0.1000
4.3000    3.0000    1.1000    0.1000
5.8000    4.0000    1.2000    0.2000
5.7000    4.4000    1.5000    0.4000
5.4000    3.9000    1.3000    0.4000
5.1000    3.5000    1.4000    0.3000
5.7000    3.8000    1.7000    0.3000
5.1000    3.8000    1.5000    0.3000

以下是我的 matrix.labels 的样式。
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1

我正在尝试创建10折交叉验证,但不使用MatLab中的任何现有函数。由于我对MatLab知识非常有限,所以我在继续进行时遇到了困难。希望能得到帮助。
这是我目前的代码,我相信这可能不是Matlab的方式,但我对Matlab非常陌生。
function[output] = fisher(dataFile, number_of_folds)
    data = load(dataFile);
    %create random permutation indx
    idx = randperm(150);
    output = data.data(idx(1:15),:);
end

请尝试访问以下链接:http://www.mathworks.com/help/bioinfo/ref/crossvalind.html - im so confused
然后在您的矩阵上使用这些生成的索引。 - im so confused
对不起,我想在不使用crossvalind函数的情况下完成它。 - add-semi-colons
1
哦抱歉!我没有仔细阅读问题。等一下。 - im so confused
没问题,如果您正在编写解决方案,任何评论都很好,因为我正在尝试学习Matlab。 - add-semi-colons
看一下,如果你发现任何错误或有问题,请告诉我。 - im so confused
2个回答

5

这里是关于交叉验证的我的看法。我使用magic(10)创建了虚拟数据,同时随机创建了标签。思路如下,我们将数据和标签组合起来,并与随机列结合。请考虑以下虚拟代码。

>> data = magic(4)

data =

    16     2     3    13
     5    11    10     8
     9     7     6    12
     4    14    15     1

>> dataRowNumber = size(data,1)

dataRowNumber =

     4

>> randomColumn = rand(dataRowNumber,1)

randomColumn =

    0.8147
    0.9058
    0.1270
    0.9134


>> X = [ randomColumn data]

X =

    0.8147   16.0000    2.0000    3.0000   13.0000
    0.9058    5.0000   11.0000   10.0000    8.0000
    0.1270    9.0000    7.0000    6.0000   12.0000
    0.9134    4.0000   14.0000   15.0000    1.0000

如果我们按列1对X进行排序,那么我们的数据将被随机排序。这将提供交叉验证的随机性。接下来要做的是根据交叉验证百分比划分X。对于一个案例,这个任务很容易完成。假设我们将%75作为训练集,%25作为测试集。这里的数据大小为4,那么3/4等于%75,1/4等于%25。

testDataset = X(1,:)
trainDataset = X(2:4,:)

但是在N折交叉验证中,实现这一点会更加困难。因为我们需要做N次。所以必须使用for循环。对于5折交叉验证,我得到了以下结果:

  1. 第1轮:测试集为1和2号数据,训练集为3到10号数据
  2. 第2轮:测试集为3和4号数据,训练集为1、2和5到10号数据
  3. 第3轮:测试集为5和6号数据,训练集为1到4和7到10号数据
  4. 第4轮:测试集为7和8号数据,训练集为1到6和9到10号数据
  5. 第5轮:测试集为9和10号数据,训练集为1到8号数据

以下代码是此过程的示例:

data = magic(10);
dataRowNumber = size(data,1);
labels= rand(dataRowNumber,1) > 0.5;
randomColumn = rand(dataRowNumber,1);

X = [ randomColumn data labels];


SortedData = sort(X,1);

crossValidationFolds = 5;
numberOfRowsPerFold = dataRowNumber / crossValidationFolds;

crossValidationTrainData = [];
crossValidationTestData = [];
for startOfRow = 1:numberOfRowsPerFold:dataRowNumber
    testRows = startOfRow:startOfRow+numberOfRowsPerFold-1;
    if (startOfRow == 1)
        trainRows = [max(testRows)+1:dataRowNumber];
        else
        trainRows = [1:startOfRow-1 max(testRows)+1:dataRowNumber];
    end
    crossValidationTrainData = [crossValidationTrainData ; SortedData(trainRows ,:)];
    crossValidationTestData = [crossValidationTestData ;SortedData(testRows ,:)];

end

那么,如果我有数据文件,我可以将其传递给X吗? - add-semi-colons
还有,我怎么使用随机排列来选择折叠点呢? - add-semi-colons
这个很好,但我设法将其放入一个函数中:function [training_data, test_data] = diagFisher(dataFile, x),然后在最后赋值training_data = crossValidationTrainData; test_data = crossValidationTestData; 但是我该如何访问它们? - add-semi-colons
你能解释一下for循环里的代码吗?我正在尝试理解,虽然我已经让它工作了,但是我还不是很清楚,主要是因为我刚接触Matlab。 - add-semi-colons
我使用了代码的一些部分,对我来说效果很好(+1)。然而,它不是分层交叉验证,因此在训练或测试集中可能不包括所有类别。 - berkay
显示剩余4条评论

3
抱歉,目前我手头没有MATLAB,无法检查代码错误,以下是一般思路:
1.生成k个(在您的情况下为10)子样本 - 在1和预先分配新矩阵的地方开始两个计数器: ``` index = 1; subsample = 1; newmat = zeros("150","6") ``` 其中150是样本数量,6 = 4个宽数据+1个宽标签+1我们稍后将使用的宽度。 - 只要还有数据:`while (length(labels) > 0)` - 生成剩余数据量内的随机数:`randNum = randi(length(labels))`,这是一个从1到标签数组大小的随机整数(可能为0,请查看文档 - 如果是,则做简单的数学运算使其1 < rand < length)。 - 使用标签将该行添加到新数据集中:`newmat(index,:) = [data(randNum,:) labels(randNum) subsample]`,最后一列是从1到10的子样本编号。 - 从数据和标签中删除该行:`data(randNum,:) = []`(标签同理)。请注意,这将从矩阵中实际删除一行,因此我们必须使用while循环并检查长度是否大于0,而不是使用for循环和简单索引。 - 递增计数器:`index = index + 1; subsample = subsample + 1;` - 如果子样本等于11,请将其设置为1。
完成后,您应该有一个大型的数据矩阵,几乎与原始矩阵完全相同,但具有随机分配的“折叠标签”。
2.重复执行代码k(10)次。
编辑:代码以更易访问的方式放置。注意,这仍然是伪代码,不完整!此外,您应该注意,这绝不是最有效的方法,但如果无法使用MATLAB函数,则不会太糟糕。
for k = 1:10

index = 1; subsample = 1; newmat = zeros("150","6");
while ( length(labels) > 0 )
    randNum = randi(length(labels));
    newmat(index,:) = [data(randNum,:) labels(randNum) subsample];
    data(randNum,:) = []; same for labels
    index = index + 1; subsample = subsample + 1;
    if ( subsample == 11 )
        subsample = 1;
    end
end

% newmat is complete, now run code here using the sampled data 
%(ie pick a random number from 1:10 and use that as your validation fold. the rest for training

end

回答 #2 的编辑:

另一种方法是创建一个与数据集一样长的向量。

foldLabels = zeros("150",1);

然后,循环那么长时间(150),将标签分配给随机索引!
foldL = 1;
numAssigned = 0;
while ( numAssigned < 150 )
    idx = randi(150);
    % no need to reassign a given label, so check if is still 0
    if ( foldLabels(idx) == 0 )
        foldLabels(idx) = foldL;
        numAssigned++; % not matlab code, just got lazy. you get it
        foldL++;
        if ( foldL > 10 )
            foldL = 1;
        end
    end
end

第2.5个答案修改

foldLabels = zeros("150",1);
for i = 1:150
    notChosenLabels = [notChosenLabels i];
end
foldL = 1;
numAssigned = 0;
while ( length(notChosenLabels) > 0 )
    labIdx = randi(length(notChosenLabels));
    idx = notChosenLabels(labIdx);
    foldLabels(idx) = foldL;
    numAssigned++; % not matlab code, just got lazy. you get it
    foldL++;
    if ( foldL > 10 )
        foldL = 1;
    end
    notChosenLabels(labIdx) = [];
end

编辑randperm

使用randperm生成索引

idxs = randperm(150);

现在只需要分配。
foldLabels = zeros(150,1);
for i = 1:150
    foldLabels(idxs(i)) = sampleLabel;
    sampleLabel = sampleLabel + 1;
    if ( sampleLabel > 10 )
       sampleLabel = 1;
    end
end

1
仍在进行中,但发布中途以便人们指出错误/您可以开始提问。 - im so confused
1
啊,好的,那我应该让它更明确一些。基本上,为了方便起见,我们将您的两个数组混合在一起。一个有4个元素宽,另一个只有1个,对吧?那就是5列。我们还为稍后给该样本命名的标签保留了1个。清楚吗? - im so confused
1
好的,我刚刚发布了一个更简单的解决方案,但是在答案#1中的那一行只是从向量中删除了该行。 向量在该元素消失后物理上缩短了1(将其视为MATLAB的NULL)。 - im so confused
1
请注意,由于随机索引查找,#2的性能可能比#1差。因此,更好的方法是生成另一个向量,其中包含您尚未选择的索引,并从中删除已选择的索引。 - im so confused
1
如果您可以使用randperm,那么我答案末尾的最后一部分就足以为数据生成一个折叠标签向量。 - im so confused
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接