我有一个包含20个测试对象的数据集,其中有50个变量,结果向量为1
和0
,用于确定其状态。我想建立一个嵌套的交叉验证,这样我在内部折叠中执行特征选择以及调整SVM的超参数。然后应在外部折叠处测试这些参数。
我以前在使用逻辑回归进行特征选择方面做了这个(使用sequentialfs),但我不知道如何同时进行特征选择和超参数调整。
一些示例代码是理想的,但是对给定输出的设置和解释的一般解释也会有所帮助,因为我是SVM的新手。
功能选择必须是前进。如果可能的话,我希望输出是具有参数和所选功能的最佳整体SVM。当然,我也想知道测试集的错误率。使用的内核是二阶多项式,所以我想只有一个参数可以调整?
此外,我希望它在内部和外部交叉验证上都是5倍。
编辑:我找到了一些示例代码,应该做我想要的,但我似乎无法使它工作。任何人都可以解释如何在MATLAB中设置它吗?
https://stats.stackexchange.com/questions/40906/nested-cross-validation-for-classification-in-matlab
答案 0 :(得分:3)
我提出以下答案,其中allData
包含所有数据。每一行都是一个条目,每一列都是一个特征。名为targets
的变量将包含目标类。有了这些假设,我将逐一解释下面附带的代码。
变量featSize
将包含多个要素。
featSize = size(allData, 2);
变量kFolds
将包含您想要的折叠数。您说5
,但如果您的数据充足,我建议您至少10
。
kFolds = 5;
结构bestSVM
将是"输出"你需要。您可以清除所有变量,除此之外。该结构将包含最佳SVM及其参数以及将产生最佳性能的特征索引。
bestSVM = struct('SVMModel', NaN, 'C', NaN, 'FeaturesIdx', NaN, 'Score', Inf);
变量kIdx
将包含基于数据和折叠数的交叉验证索引。它使用了matlab的函数crossvalind()。
kIdx = crossvalind('Kfold', length(targets), kFolds);
现在,主外部循环将运行您在kFolds
中指定的多次折叠,并将准备训练集和测试集(trainData
,{{1 }})及其相应的目标(testData
,trainTarg
),如下所示:
testTarg
现在,我们准备选择功能。我们将变量for k = 1:kFolds
trainData = allData(kIdx~=k, :);
trainTarg = targets(kIdx~=k);
testData = allData(kIdx==k, :);
testTarg = targets(kIdx==k);
初始化为bestFeatScore
,以便稍后我们可以将SVM(得分)的性能与该值进行比较(可能会更进一步降低)。我们还初始化了一个结构Inf
,其中包含所有可能的要素组合中最好的bestFeatCombo
及其相应的要素索引SVM
和参数feat
。
C
可能的功能组合数量为 bestFeatScore = inf;
bestFeatCombo = struct('SVM', NaN, 'feat', NaN, 'C', NaN);
。例如,如果您总共有两个功能,则有2^featSize - 1
个选项:1)仅选择功能1,2)仅选择功能2,3)仅选择功能1和2。因此,我们需要一个2^2 - 1 = 3
循环来遍历所有可能的功能组合。
for
然而,有一个棘手的部分(我确信有更好的解决方案),你想先开始选择功能集。我认为这是一个二进制表示问题。假设您总共有三个要素 for b = 1:(2^featSize) - 1
,那么,我们可以说二元向量[f1, f2, f3]
表示要素[1, 0, 0]
的选择,而忽略了其余部分。将二进制向量传递给matlab会给出索引错误。所以,我发现解决这个问题的最好方法是使用matlab的find()函数来查找非零元素的"索引和值"。因此,如果我们f1
,变量featCombo = find([1, 0, 0])
将等于featCombo
。所以,我所做的是使用变量1
(来自上面的for循环),它将包含一个数字,指示当前可能的要素组合,并使用matlab将其转换为大小为b
的二进制向量&# 39; s函数de2bi()。例如,featSize
给出de2bi(1, 3)
,正如您所注意到的那样,使用左数字作为最不重要的数字。 [1, 0, 0]
代表de2bi(3, 3)
,[1, 1, 0]
代表de2bi(5, 3)
,依此类推。然后,如果您在[1, 0, 1]
上使用find()
函数,则会导致您要选择的要素的索引。
de2bi()
例如,如果 featCombo = find(de2bi(b, featSize));
,则:
featSize = 3
会给你这样的东西::
for b = 1:(2^featSize) - 1; display(de2bi(b, featSize)); end;
与find结合如下:
1 0 0
0 1 0
1 1 0
0 0 1
1 0 1
0 1 1
1 1 1
会给你这样的东西::
for b = 1:(2^featSize) - 1; display(find(de2bi(b, featSize))); end;
适合用作logical indexing。因此, 1
2
1 2
3
1 3
2 3
1 2 3
将包含要选择的要素集(带索引的向量)。
下一部分将为BoxConstraint featCombo
初始化grid search的变量,它也称为超参数(取决于您可能有其他参数的SVM类型)。 C
将在网格搜索期间包含SVM的最佳性能,bestCScore
将包含最佳bestC
参数,C
将在搜索期间拥有经过最佳训练的SVM。变量bestCSVM
将包含搜索空间,在我的示例中,搜索空间从gridC
,2^-5
到2^-3
。如果您有足够的计算资源,我建议您将2^15
更改为较小的增量,例如2.^(-5:2:15)
,或更小,2.^(-5:1:15)
,但要小心,因为它需要一段时间才能完成。同样,如果您的计算能力较低(或时间有限),则将时间间隔增加到2.^(-5:0.1:15)
甚至2.^(-5:3:15)
,因为它知道它会很难选择超参数。
2.^(-5:4:15)
接下来,我们将基于数组 bestCScore = inf;
bestC = NaN;
bestCSVM = NaN;
gridC = 2.^(-5:2:15);
开始网格搜索,我们将使用matlab的函数fitcsvm()训练SVM。 SVM使用由当前折叠确定的当前gridC
进行训练,并使用trainData
确定的特定选定要素进行训练。培训目标featCombo
也由当前折叠确定。注意a)我使用的是RBF内核类型(因为你没有指定它),以及b)我让matlab 自动确定内核规模。如果要使用其他内核,则需要对此代码进行一些修改。
trainTarg
下一步是确定SVM对该折叠的那组特征的参数执行情况。我们使用SVM函数loss()执行此操作。
for C = gridC
anSVMModel = fitcsvm(trainData(:, featCombo), trainTarg, ...
'KernelFunction', 'RBF', 'KernelScale', 'auto', ...
'BoxConstraint', C);
如果当前SVM( L = loss(anSVMModel,testData(:, featCombo), testTarg);
)的效果优于之前的最佳效果,我们会将得分保存到anSVMModel
,将最佳参数保存到bestCScore
,将最佳SVM保存到bestC
1}}。
bestCSVM
在最内层循环结束时,我们应该拥有当前功能集的最佳超参数和SVM。因此,如果此SVM具有比任何其他任何其他功能集的先前训练的SVM更好的分数,我们将该SVM,该组功能和该超参数保存到由 if L < bestCScore
bestCScore = L;
bestC = C;
bestCSVM = anSVMModel;
end
end
给出的结构中,{分别为{1}},bestFeatCombo.C`。
bestFeatCombo.SVM
但请注意,在上面的bestFeatCombo.feat
语句中,我在 if (bestCScore < bestFeatScore) || ...
((bestCScore == bestFeatScore) && ...
(length(featCombo) < length(bestFeatCombo.feat)))
bestFeatScore = bestCScore;
bestFeatCombo.SVM = bestCSVM;
bestFeatCombo.feat = featCombo;
bestFeatCombo.C = bestC;
end
end
子句中创建了一个特例。我说如果当前的SVM执行(得分)与目前最好的相同,但是当前的SVM具有一组较小的功能,可以提供相同的性能,我选择替换目前为止最好使用较少功能的SVM。
现在,在中间循环结束时,我们应该为任何一组功能提供最好的SVM,具有最佳超参数if
(在网格范围内)搜索允许)。因此,我们可以将当前最佳(存储在结构or
中)与目前总体最佳(存储在结构C
中)进行比较
bestFeatCombo
这就是结束。正如我所说,您想要的输出位于bestSVM
,其中包含 if bestFeatScore < bestSVM.Score
bestSVM.SVMModel = bestFeatCombo.SVM;
bestSVM.C = bestFeatCombo.C;
bestSVM.FeaturesIdx = bestFeatCombo.feat;
bestSVM.Score = bestFeatScore
end
end
中最佳的SVM,用于最佳功能组合,以及最佳超参数bestSVM
作为网格搜索允许。
我希望这是有道理的。以下是使用matlab kFolds
数据集的工作示例,该数据集具有C
个样本和fisheriris
个功能。
工作代码:
100
修改强>
要回答您编辑过的问题,您希望在参数选择中进行另外5次交叉验证,这就是您需要做的事情。
请将最内层循环更改为如下所示:
4
代码使用标准CV。但请注意以下行很重要:
load fisheriris
inds = ~strcmp(species,'setosa');
allData = meas(inds,:);
targets = species(inds);
featSize = size(allData, 2);
kFolds = 5; % this is where you specify your number of folds
bestSVM = struct('SVMModel', NaN, ... % this is to store the best SVM
'C', NaN, 'FeaturesIdx', NaN, 'Score', Inf);
kIdx = crossvalind('Kfold', length(targets), kFolds);
for k = 1:kFolds
trainData = allData(kIdx~=k, :);
trainTarg = targets(kIdx~=k);
testData = allData(kIdx==k, :);
testTarg = targets(kIdx==k);
% forward feature selection starts
bestFeatScore = inf;
bestFeatCombo = struct('SVM', NaN, 'feat', NaN, 'C', NaN);
for b = 1:(2^featSize) - 1
% this is to choose the features. e.g. [1 0 0] selects the first
% feature out of three features.
featCombo = find(de2bi(b, featSize));
% this is the grid search for the BoxConstraint
bestCScore = inf;
bestC = NaN;
bestCSVM = NaN;
gridC = 2.^(-5:2:15);
for C = gridC
anSVMModel = fitcsvm(trainData(:, featCombo), trainTarg, ...
'KernelFunction', 'RBF', 'KernelScale', 'auto', ...
'BoxConstraint', C);
L = loss(anSVMModel,testData(:, featCombo), testTarg);
if L < bestCScore % saving best SVM on parameter
bestCScore = L; % selection
bestC = C;
bestCSVM = anSVMModel;
end
end
% saving the best SVM on feature selection
if (bestCScore < bestFeatScore) || ...
((bestCScore == bestFeatScore) && ...
(length(featCombo) < length(bestFeatCombo.feat)))
bestFeatScore = bestCScore;
bestFeatCombo.SVM = bestCSVM;
bestFeatCombo.feat = featCombo;
bestFeatCombo.C = bestC;
end
end
% saving the best SVM over all folds
if bestFeatScore < bestSVM.Score
bestSVM.SVMModel = bestFeatCombo.SVM;
bestSVM.C = bestFeatCombo.C;
bestSVM.FeaturesIdx = bestFeatCombo.feat;
bestSVM.Score = bestFeatScore
end
end
在这一行中,您可以保存所有折叠中的所有表演。但请注意,您必须平均,这是CV的目的。
% this is the grid search for the BoxConstraint
bestCScore = inf;
bestC = NaN;
gridC = 2.^(-5:2:15);
for C = gridC
% cross validation for parameter C
kIdxC = crossvalind('Kfold', length(trainTarg), kFolds);
L = zeros(1, kFolds);
for kC = 1:kFolds
trainDataC = trainData(kIdxC~=kC, :);
trainTargC = trainTarg(kIdxC~=kC);
testDataC = trainData(kIdxC==kC, :);
testTargC = trainTarg(kIdxC==kC);
anSVMModel = fitcsvm(trainDataC(:, featCombo), trainTargC, ...
'KernelFunction', 'RBF', 'KernelScale', 'auto', ...
'BoxConstraint', C);
L(kC) = loss(anSVMModel,testDataC(:, featCombo), testTargC);
end
L = mean(L);
if L < bestCScore
bestCScore = L;
bestC = C;
end
end
% we need to retrain here and save the SVM for the best C
bestCSVM = fitcsvm(trainData(:, featCombo), trainTarg, ...
'KernelFunction', 'RBF', 'KernelScale', 'auto', ...
'BoxConstraint', bestC);
bestCScore = loss(bestCSVM,testData(:, featCombo), testTarg);
你还需要进行一次再培训。