MATLAB实现多分类问题,使用libsvm,1-vs-rest和1-vs-1两种方法代码

时间:2022-03-13 19:46:52

最近的项目需要实现多分类,主要的平台是MATLAB+LIBSVM.
如果需求只是实现二分类,那非常简单,可以参见这篇博文

多分类存在的主要问题是,LIBSVM主要是为二分类设计的,为了实现多分类就需要通过构造多个二分类器来实现.
目前,构造SVM多类分类器的方法主要有两类:一类是直接法,直接在目标函数上进行修改,将多个分类面的参数求解合并到一个最优化问题中,通过求解该最优化问题“一次性”实现多类分类。这种方法看似简单,但其计算复杂度比较高,实现起来比较困难,只适合用于小型问题中;另一类是间接法,主要是通过组合多个二分类器来实现多分类器的构造,常见的方法有one-against-one和one-against-all两种。
A:一对多法(one-versus-rest,简称1-v-r SVMs, OVR SVMs)。训练时依次把某个类别的样本归为一类,其他剩余的样本归为另一类,这样k个类别的样本就构造出了k个SVM。分类时将未知样本分类为具有最大分类函数值的那类。(注意这种方法关注的是对每个模型测试后得到的概率,而不是label)
假如我有四类要划分(也就是4个Label),它们是A、B、C、D。于是我在抽取训练集的时候,分别抽取A所对应的向量作为正集,B,C,D所对应的向量作为负集;B所对应的向量作为正集,A,C,D所对应的向量作为负集;C所对应的向量作为正集, A,B,D所对应的向量作为负集;D所对应的向量作为正集,A,B,C所对应的向量作为负集,这四个训练集分别进行训练,然后的得到四个训练结果文件,在测试的时候,把对应的测试向量分别利用这四个训练结果文件进行测试,最后每个测试都有一个结果f1(x),f2(x),f3(x),f4(x).于是最终的结果便是这四个值中最大的一个。
p.s.: 这种方法有种缺陷,因为训练集是1:M,这种情况下存在biased.因而不是很实用.

MATLAB代码实现:
`%加载训练样本和标签
train_data=load(‘train_data.mat’);
train_data=train_data.train_data;
train_label=load(‘train_label.mat’);
train_label=train_label.train_label;

trainData=train_data;
trainLabel=train_label;
%%加载训练数据集
test_data=load(‘test_data.mat’);
test_data=test_data.test_data;
test_label=load(‘test_label.mat’);
test_label=test_label.test_label;
fprintf(‘测试样本加载完毕!\n’);

testData=test_data;
testLabel=test_label;
%# train one-against-all models
numLabels=51;
model = cell(numLabels,1);
for k=1:numLabels
model{k} = svmtrain(double(trainLabel==k), trainData, ‘-c 100 -g 0.2 -b 1’);
end

%# get probability estimates of test instances using each model
prob = zeros(numTest,numLabels);
for k=1:numLabels
[~,~,p] = svmpredict(double(testLabel==k), testData, model{k}, ‘-b 1’);
prob(:,k) = p(:,model{k}.Label==1); %# probability of class==k
end

%# predict the class with the highest probability
[~,pred] = max(prob,[],2);
acc = sum(pred == testLabel) ./ numel(testLabel) %# accuracy
C = confusionmat(testLabel, pred) %# confusion matrix`

B:一对一法(one-versus-one,简称1-v-1 SVMs, OVO SVMs, pairwise)。其做法是在任意两类样本之间设计一个SVM,因此k个类别的样本就需要设计k(k-1)/2个SVM。当对一个未知样本进行分类时,最后得票最多的类别即为该未知样本的类别。Libsvm中的多类分类就是根据这个方法实现的。(注意这种方法关注的是对每个模型测试后得到的label,而不是概率)
投票是这样进行的.
A=B=C=D=0;
(A, B)-classifier 如果是A win,则A=A+1;otherwise,B=B+1;
(A,C)-classifer 如果是A win,则A=A+1;otherwise, C=C+1;

(C,D)-classifer 如果是A win,则C=C+1;otherwise,D=D+1;

MATLAB代码实现:你可以根据上一步one-versus-one的步骤来自己构造分类器,但是实际上libsvm本身实现多分类时就是使用的这种办法,因此实现one-versus-one的多分类时只需要把不同的类标签和数据都加载到label和data中去就行,直接用libsvm进行训练.如下面代码中我的data和label中都各自有51个类标签和数据.

%%使用libsvm默认多分类方法进行分类1v1
clear all;
close all;
clc;

%
加载训练样本和标签
train_data=load('train_data.mat');
train_data=train_data.train_data;
train_label=load('train_label.mat');
train_label=train_label.train_label;
fprintf('训练样本加载完毕!\n');

%%加载测试数据集
test_data=load('test_data.mat');
test_data=test_data.test_data;
test_label=load('test_label.mat');
test_label=test_label.test_label;
fprintf('测试样本加载完毕!\n');


%
[bestacc,bestc,bestg] = SVMcg(train_label,train_data,0,10);
%options=sprintf('-t 2 -c %f -g %f ',bestc,bestg);
options='-t 2 -c 1 -g 0.5';
train_model= svmtrain(train_label,train_data,options);
fprintf('训练完毕!\n');

[predictlabel,accuracy,prob_estimates]= svmpredict(test_label,test_data,train_model);



**如果要对比两种方法的复杂度的话,one-versus-one训练的模型数量更多,但是单个模型复杂度小,one-against-all每个模型的复杂度较高,但是模型数量总体较少.
比如我的分类目标是51个类,one-versus-one训练的模型个数为51*50/2=1275个,one-against-all训练模型个数为51个.**