使用交叉验证和F1分数选择SVM参数

3

我需要在调整SVM中的C和Sigma时跟踪F1分数, 例如,以下代码跟踪准确性,我需要将其更改为F1分数,但我无法做到这一点......

%# read some training data
[labels,data] = libsvmread('./heart_scale');

%# grid of parameters
folds = 5;
[C,gamma] = meshgrid(-5:2:15, -15:2:3);

%# grid search, and cross-validation
cv_acc = zeros(numel(C),1);
    for i=1:numel(C)
cv_acc(i) = svmtrain(labels, data, ...
                sprintf('-c %f -g %f -v %d', 2^C(i), 2^gamma(i), folds));
end

%# pair (C,gamma) with best accuracy
[~,idx] = max(cv_acc);

%# now you can train you model using best_C and best_gamma
best_C = 2^C(idx);
best_gamma = 2^gamma(idx);
%# ...

我看到了以下两个链接:

使用libsvm进行交叉验证后的重新训练

一对多SVM中的10折交叉验证(使用LibSVM)

我理解我需要先在训练数据上找到最佳的C和gamma/sigma参数,然后使用这两个值进行一个留一法交叉验证分类实验, 所以现在我想首先进行网格搜索以调整C和sigma。 请注意,我更喜欢使用MATLAB-SVM而不是LIBSVM。 以下是我的LEAVE-ONE-OUT交叉验证分类代码。

... clc
 clear all
close all
a = load('V1.csv');
X = double(a(:,1:12));
Y = double(a(:,13));
% train data
datall=[X,Y];
A=datall;
n = 40;
ordering = randperm(n);
B = A(ordering, :);  
good=B; 
input=good(:,1:12);
target=good(:,13);
CVO = cvpartition(target,'leaveout',1);  
cp = classperf(target);                      %# init performance tracker
svmModel=[];
for i = 1:CVO.NumTestSets                                %# for each fold
trIdx = CVO.training(i);              
teIdx = CVO.test(i);                   
%# train an SVM model over training instances

svmModel = svmtrain(input(trIdx,:), target(trIdx), ...
       'Autoscale',true, 'Showplot',false, 'Method','ls', ...
      'BoxConstraint',0.1, 'Kernel_Function','rbf', 'RBF_Sigma',0.1);
%# test using test instances
pred = svmclassify(svmModel, input(teIdx,:), 'Showplot',false);
%# evaluate and update performance object
cp = classperf(cp, pred, teIdx); 
end
%# get accuracy
accuracy=cp.CorrectRate*100
sensitivity=cp.Sensitivity*100
specificity=cp.Specificity*100
PPV=cp.PositivePredictiveValue*100
NPV=cp.NegativePredictiveValue*100
%# get confusion matrix
%# columns:actual, rows:predicted, last-row: unclassified instances
cp.CountingMatrix
recallP = sensitivity;
recallN = specificity;
precisionP = PPV;
precisionN = NPV;
f1P = 2*((precisionP*recallP)/(precisionP + recallP));
f1N = 2*((precisionN*recallN)/(precisionN + recallN));
aF1 = ((f1P+f1N)/2);

我已经修改了代码,但是出现了一些错误和问题。
a = load('V1.csv');
X = double(a(:,1:12));
Y = double(a(:,13));
% train data
datall=[X,Y];
A=datall;
n = 40;
ordering = randperm(n);
B = A(ordering, :);  
good=B; 
inpt=good(:,1:12);
target=good(:,13);
k=10;
cvFolds = crossvalind('Kfold', target, k);   %# get indices of 10-fold CV
cp = classperf(target);                      %# init performance tracker
svmModel=[];
for i = 1:k 
    testIdx = (cvFolds == i);    %# get indices of test    instances
trainIdx = ~testIdx;   
C = 0.1:0.1:1; 
S = 0.1:0.1:1; 
fscores = zeros(numel(C), numel(S)); %// Pre-allocation
for c = 1:numel(C)   
for s = 1:numel(S)
    vals = crossval(@(XTRAIN, YTRAIN, XVAL, YVAL)(fun(XTRAIN, YTRAIN, XVAL, YVAL, C(c), S(c))),inpt(trainIdx,:),target(trainIdx));
    fscores(c,s) = mean(vals);
end
end
 end

[cbest, sbest] = find(fscores == max(fscores(:)));
C_final = C(cbest);
S_final = S(sbest);    

.......

和这个函数相关的……

.....
function fscore = fun(XTRAIN, YTRAIN, XVAL, YVAL, C, S)
svmModel = svmtrain(XTRAIN, YTRAIN, ...
   'Autoscale',true, 'Showplot',false, 'Method','ls', ...
  'BoxConstraint', C, 'Kernel_Function','rbf', 'RBF_Sigma', S);

   pred = svmclassify(svmModel, XVAL, 'Showplot',false);

   cp = classperf(YVAL, pred)
   %# get accuracy
    accuracy=cp.CorrectRate*100
    sensitivity=cp.Sensitivity*100
    specificity=cp.Specificity*100
    PPV=cp.PositivePredictiveValue*100
    NPV=cp.NegativePredictiveValue*100
    %# get confusion matrix
    %# columns:actual, rows:predicted, last-row: unclassified instances
    cp.CountingMatrix
    recallP = sensitivity;
    recallN = specificity;
    precisionP = PPV;
    precisionN = NPV;
    f1P = 2*((precisionP*recallP)/(precisionP + recallP));
    f1N = 2*((precisionN*recallN)/(precisionN + recallN));
    fscore = ((f1P+f1N)/2);

    end

你想使用Matlab的统计工具箱还是机器学习?无论哪种方式,代码都会与你的libsvm示例完全不同。请先尝试在Matlab中编写代码:http://www.mathworks.com/help/stats/classificationsvm.crossval.html 或者至少发布你的Matlab SVM代码,其中你任意设置参数为单个值(即没有通过交叉验证进行调整)。 - Dan
这是链接https://dev59.com/IWox5IYBdhLWcg3wq2IC#9049225。 - Wasnuga
那段代码是用于libsvm的。您需要先向我们展示您的非libsvm代码是什么样子的。只需发布简单的代码,不包括交叉验证(即网格搜索)部分。但是作为提示,您可能想使用此函数:http://www.mathworks.com/help/stats/crossval.html - Dan
1
我已经编辑了代码并添加了MATLAB代码,谢谢。 - Wasnuga
2个回答

1
"所以基本上,您想采取自己的这行代码:"
svmModel = svmtrain(input(trIdx,:), target(trIdx), ...
       'Autoscale',true, 'Showplot',false, 'Method','ls', ...
      'BoxConstraint',0.1, 'Kernel_Function','rbf', 'RBF_Sigma',0.1);

将其置于循环中,该循环变化您的'BoxConstraint''RBF_Sigma'参数,然后使用crossval输出该迭代参数组合的f1-score。

您可以使用与libsvm代码示例完全相同的单个for循环(即使用meshgrid1:numel(),这可能更快),也可以使用嵌套for循环。我将使用嵌套循环,以便您拥有两种方法:

C = [0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100, 300] %// you must choose your own set of values for the parameters that you want to test. You can either do it this way by explicitly typing out a list
S = 0:0.1:1 %// or you can do it this way using the : operator
fscores = zeros(numel(C), numel(S)); %// Pre-allocation
for c = 1:numel(C)   
    for s = 1:numel(S)
        vals = crossval(@(XTRAIN, YTRAIN, XVAL, YVAL)(fun(XTRAIN, YTRAIN, XVAL, YVAL, C(c), S(c)),input(trIdx,:),target(trIdx));
        fscores(c,s) = mean(vals);
    end
end

%// Then establish the C and S that gave you the bet f-score. Don't forget that c and s are just indexes though!
[cbest, sbest] = find(fscores == max(fscores(:)));
C_final = C(cbest);
S_final = S(sbest);

现在我们只需要定义函数fun。文档对fun的描述如下:

fun是一个函数句柄,指向一个带有两个输入参数的函数:训练集子集XTRAIN和测试集子集XTEST。其形式如下:

testval = fun(XTRAIN,XTEST) 每次调用时,fun应该使用XTRAIN来拟合模型,然后使用该拟合模型计算在XTEST上某些标准的testval。

因此,fun需要:
  • 输出单个f-score
  • 接收X和Y的训练和测试子集作为输入。请注意,这两个子集都是您实际训练集的子集!将它们视为训练和验证训练集的子集。还要注意,交叉验证将为您拆分这些集合!
  • 在训练子集上训练分类器(使用当前循环中的CS参数)
  • 在测试(或验证)子集上运行新的分类器
  • 计算并输出性能度量(在您的情况下,您需要f1-score)
你会注意到fun无法接受额外的参数,这就是为什么我将其包装在匿名函数中,以便我们可以传递当前的CS值。(即上面所有的@(...)(fun(...))。这只是一个技巧,将我们的六个参数的fun转换为crossval所需的四个参数之一。)
function fscore = fun(XTRAIN, YTRAIN, XVAL, YVAL, C, S)

   svmModel = svmtrain(XTRAIN, YTRAIN, ...
       'Autoscale',true, 'Showplot',false, 'Method','ls', ...
      'BoxConstraint', C, 'Kernel_Function','rbf', 'RBF_Sigma', S);

   pred = svmclassify(svmModel, XVAL, 'Showplot',false);

   CP = classperf(YVAL, pred)

   fscore = ... %// You can do this bit the same way you did earlier
end

没问题 - 如果您发现任何错误,请告诉我! - Dan
首先,你为什么要改用...,inpt,target?这是一个匿名函数,那些变量不是你工作区中的变量... 其次,错误听起来好像在抱怨你在target(trIdx)中有所有相同的数字。 - Dan
crossval 会生成 XTRAIN,YTRAIN,XVAL,YVAL 。但是 crossval 需要您提供您的训练数据(然后它将自己将其拆分为较小的训练和验证集)。 - Dan
我已将其更改为vals = crossval(@(XTRAIN, YTRAIN, XVAL, YVAL)(fun(XTRAIN, YTRAIN, XVAL, YVAL, C(c), S(c))),input,target);其中input是一个40X12的数据集,target是一个40X1的数据目标,包含两个类别0和1。但MATLAB仍然显示“Ground truth must have at least two classes”。 - Wasnuga
@Wasnuga 首先,撤销那个更改,因为你以后仍然需要测试集。我确定问题出在 classperf 上(请将整个错误信息发布出来)。你只需要在没有它的情况下计算 f1-score(这真的不难)。如果你无法做到这一点(在认真尝试之后),请将其作为一个单独的问题提出来。 - Dan

0

我发现了target(trainIdx)的唯一问题。它是一个行向量,所以我只需将target(trainIdx)替换为列向量target(trainIdx)即可。


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