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

Posted

技术标签:

【中文标题】使用交叉验证和 F1 分数选择 SVM 参数【英文标题】:Selecting SVM parameters using cross validation and F1-scores 【发布时间】:2015-03-25 21:37:13 【问题描述】:

我需要在 SVM 中调整 C 和 Sigma 时跟踪 F1 分数, 例如下面的代码跟踪准确度,我需要将其更改为 F1-Score 但我无法做到…….

%# 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);
%# ...

我看过以下两个链接

Retraining after Cross Validation with libsvm

10 fold cross-validation in one-against-all SVM (using LibSVM)

我明白我必须首先在训练数据上找到最佳的 C 和 gamma/sigma 参数,然后使用这两个值进行 LEAVE-ONE-OUT 交叉验证分类实验, 所以我现在想要的是首先进行网格搜索以调整 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 中对其进行编码:mathworks.com/help/stats/classificationsvm.crossval.html 或至少发布您的 matlab SVM 代码,您可以在其中任意将参数设置为单个值(即不通过交叉验证进行调整) 这是链接***.com/questions/9047459/… 因此该代码适用于 libsvm。您需要首先向我们展示您的 NON-libsvm 代码是什么样的。只需发布没有交叉验证(即网格搜索)的简单代码。但作为一个线索,你可能想要使用这个函数:mathworks.com/help/stats/crossval.html 我已经编辑了代码并添加了 MATLAB 代码,谢谢 【参考方案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 分数。

您可以像在 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 是具有两个输入的函数的函数句柄,训练 X 的子集 XTRAIN 和 X 的测试子集 XTEST,如下:

testval = fun(XTRAIN,XTEST) 每次调用时,fun 应该使用 XTRAIN 拟合一个模型,然后返回一些计算的标准 testval 使用该拟合模型进行 XTEST。

所以fun 需要:

输出单个 f 分数 将 X 和 Y 的训练和测试集作为输入。请注意,这些都是实际训练集的子集!把它们想象成你的训练集的训练和验证子集。另请注意,crossval 会为您拆分这些设置! 在训练子集上训练分类器(使用循环中当前的 CS 参数) 在测试(或验证)子集上运行您的新分类器 计算并输出性能指标(在您的情况下,您需要 f1 分数)

你会注意到 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

【讨论】:

没问题 - 如果您发现任何错误,请告诉我! 首先你为什么把它改成使用...,inpt,target?这是一个匿名函数,这些变量不是您工作区中的变量...其次,错误听起来像是在抱怨您在 target(trIdx) 中拥有相同的数字。 XTRAIN, YTRAIN, XVAL, YVALcrossval 生成。 crossval 需要你向它提供你的训练数据(然后它会自己拆分成更小的训练和验证集) .....我把它改成了......vals = crossval(@(XTRAIN, YTRAIN, XVAL, YVAL)(fun(XTRAIN, YTRAIN, XVAL, YVAL, C (c), S(c))),input,target);............输入是一个 40X12 数据集,目标是一个 40X1 数据目标,有两个类 0 和 1... ....但 MATLAB 仍然说 Ground Truth 必须至少有两个类。 @Wasnuga 因此,首先,撤消该更改,因为您稍后仍将需要您的测试集。我确定问题出在classperf(以后请发布整个错误)。没有它你只需要计算 f1 分数(这真的不难)。如果您无法解决此问题(经过认真尝试),请将其作为一个单独的问题提出。【参考方案2】:

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

【讨论】:

以上是关于使用交叉验证和 F1 分数选择 SVM 参数的主要内容,如果未能解决你的问题,请参考以下文章

在使用 5 折交叉验证时,在高度不平衡的数据中混淆 F1 分数和 AUC 分数

SVM 回归的交叉验证

字符串python列表上的Kfold交叉验证和SVM

为啥 scikit-learn SVM 分类器交叉验证这么慢?

在调整参数时识别交叉验证的 SVM 中的过拟合

R语言使用e1071包中的svm函数构建支持向量机SVM模型使用tune.svm函数基于网格搜索(10折交叉验证)对RBF核函数的gamma参数和cost参数进行参数寻优使用最优参数构建最终模型