SVM原理与实践
SVM迅速发展和完善,在解决小样本、非线性及高维模式识别问题中表现出许多特有的优势,并能够推广应用到函数拟合等其他机器学习问题中.从此迅速的发展起来,已经在许多领域(生物信息学,文本和手写识别等)都取得了成功的应用.在地球物理反演当中解决非线性反演也有显著成效,例如(SVM在预测地下水涌水量问题等).
SVM中的一大亮点是在传统的最优化问题中提出了对偶理论,主要有最大最小对偶及拉格朗日对偶.
SVM的关键在于核函数.低维空间向量集通常难于划分,解决的方法是将它们映射到高维空间.但这个办法带来的困难就是计算复杂度的增加,而核函数正好巧妙地解决了这个问题.也就是说,只要选用适当的核函数,就可以得到高维空间的分类函数.
在确定了核函数之后,由于确定核函数的已知数据也存在一定的误差,考虑到推广性问题,因此引入了松弛系数以及惩罚系数两个参变量(注:引入松弛变量之后w依然是唯一的,但是b不是唯一的)来加以校正.在确定了核函数基础上,再经过大量对比实验等将这两个系数取定,该项研究就基本完成,适合相关学科或业务内应用,且有一定能力的推广性.
原理
SVM的优缺点:
SVM优点:
(1)内积核函数:非线性映射是SVM方法的理论基础,SVM利用内积核函数代替向高维空间的非线性映射;
(2)最大化分类边际:对特征空间划分的最优超平面是SVM的目标,最大化分类边际的思想是SVM方法的核心;
(3)支持向量:支持向量是SVM的训练结果,在SVM分类决策中起决定作用的是支持向量. SVM 的最终决策函数只由少数的支持向量所确定,计算的复杂性取决于支持向量的数目,而不是样本空间的维数,这在某种意义上避免了“维度灾难”;
(4)坚实理论基础的新颖的小样本学习方法:SVM 是一种有坚实理论基础的新颖的小样本学习方法.它基本上不涉及概率测度及大数定律等,因此不同于现有的统计方法.从本质上看,它避开了从归纳到演绎的传统过程,实现了高效的从训练样本到预报样本的“转导推理”,大大简化了通常的分类和回归等问题;
(5)鲁棒性:少数支持向量决定了最终结果,这不但可以帮助我们抓住关键样本、“剔除”大量冗余样本,而且注定了该方法不但算法简单,而且具有较好的“鲁棒”性.这种“鲁棒”性主要体现在:①增、删非支持向量样本对模型没有影响;②支持向量样本集具有一定的鲁棒性;③有些成功的应用中,SVM 方法对核的选取不敏感.
不足:
(1) SVM算法对大规模训练样本难以实施:
由于SVM是借助二次规划来求解支持向量,而求解二次规划将涉及m阶矩阵的计算(m为样本的个数),当m数目很大时该矩阵的存储和计算将耗费大量的机器内存和运算时间.针对以上问题的主要改进有J.Platt的SMO算法、T.Joachims的SVM、C.J.C.Burges等的PCGC、张学工的CSVM以及O.L.Mangasarian等的SOR算法
(2) 用SVM解决多分类问题存在困难:
经典的SVM算法只给出了二类分类的算法,而在数据挖掘的实际应用中,一般要解决多类的分类问题.可以通过多个二类SVM的组合来解决.主要有一对多组合模式、一对一组合模式和SVM决策树;再就是通过构造多个分类器的组合来解决.主要原理是克服SVM固有的缺点,结合其他算法的优势,解决多类问题的分类精度.如:与粗集理论结合,形成一种优势互补的多类问题的组合分类器.
代码
####################################R包###################################
R的函数包e1071提供了libsvm的接口。使用e1071包中svm函数可以得到与libsvm相同的结果。write.svm()更是可以把R训练得到的结果写为标准的Libsvm格式,以供其他环境下libsvm的使用。下面我们来看看svm()函数的用法。有两种格式都可以。
svm(formula,data=NULL,…,subset,na.action=na.omit,sacle=TRUE)
或者 svm(x, y = NULL, scale = TRUE, type = NULL, kernel = "radial", degree = 3, gamma = if (is.vector(x)) 1 else 1 / ncol(x), coef0 = 0, cost = 1, nu = 0.5, class.weights = NULL, cachesize = 40, tolerance = 0.001, epsilon = 0.1, shrinking = TRUE, cross = 0, probability = FALSE, fitted = TRUE, ..., subset, na.action = na.omit)
主要参数说明如下:
formula:分类模型形式,在第二个表达式中可以理解为y~x,即y相当于标签,x相当于特征(变量) data:数据框。 subset:可以指定数据集的一部分作为训练数据。
na.cation:缺失值处理,默认为删除缺失数据。
scale:将数据标准化,中心化,使其均值为0,方差为1,将自动执行。
type:svm的形式。
为:C-classification ,nu-classification, one-classification (for novelty detection) ,eps-regression, nu-regression 五种形式。后面两者为做回归时用到。默认为C分类器。
kernel:在非线性可分时,我们引入核函数来做。
R中提供的核函数如下
线性核: u'*v 多项式核:
(gamma*u'*v + coef0)^degree
高斯核: exp(-gamma*|u-v|^2)
sigmoid核: tanh(gamma*u'*v + coef0) 默认为高斯核。顺带说一下,在kernel包中可以自定义核函数。
degree:多项式核的次数,默认为3
gamma:除去线性核外,其他核的参数,默认为1/数据维数
coef0:多项式核与sigmoid核的参数,默认为0.
cost:C分类中惩罚项c的取值
nu:Nu分类,单一分类中nu的值
cross:做k折交叉验证,计算分类正确性。
我们依然使用iris数据集来做svm分类。
如下
> data(iris)
> ir<-iris
> set.seed(124)
> count.test<-round(runif(50,1,150))
> test<-ir[count.test,]
> library(e1071)
>sv<-svm(Species~.,data=ir,cross=5,type='C-classification',kernel='sigmoid')
> summary(sv) #查看支持向量机sv的具体信息,发现做5倍交叉验证的正确率为92%
> pre<-predict(sv,test) #对测试样本作预测。pre是一个类别向量。
> dim(test[test$Species!=pre,])[1]/dim(test)[1] #计算错误率 [1] 0.06我们发现错误率为6%
##################################MATLAB####################################
%主函数
clear all;
close all;
C = 10;
kertype = 'linear';
%训练样本
n = 50;
randn('state',6);
x1 = randn(2,n); %2行N列矩阵
y1 = ones(1,n); %1*N个1
x2 = 5+randn(2,n); %2*N矩阵
y2 = -ones(1,n); %1*N个-1
figure;
plot(x1(1,:),x1(2,:),'bx',x2(1,:),x2(2,:),'k.');
axis([-3 8 -3 8]);
hold on;
X = [x1,x2]; %训练样本d*n矩阵,n为样本个数,d为特征向量个数
Y = [y1,y2]; %训练目标1*n矩阵,n为样本个数,值为+1或-1
svm = svmTrain(X,Y,kertype,C);
plot(svm.Xsv(1,:),svm.Xsv(2,:),'ro');
%测试
[x1,x2] = meshgrid(-2:0.05:7,-2:0.05:7); %x1和x2都是181*181的矩阵
[rows,cols] = size(x1);
nt = rows*cols;
Xt = [reshape(x1,1,nt);reshape(x2,1,nt)];
Yt = ones(1,nt);
result = svmTest(svm, Xt, Yt, kertype);
Yd = reshape(result.Y,rows,cols);
contour(x1,x2,Yd,'m');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function svm = svmTrain(X,Y,kertype,C)
options = optimset; % Options是用来控制算法的选项参数的向量
options.LargeScale = 'off';
options.Display = 'off';
n = length(Y);
H = (Y'*Y).*kernel(X,X,kertype);
f = -ones(n,1); %f为1*n个-1,f相当于Quadprog函数中的c
A = [];
b = [];
Aeq = Y; %相当于Quadprog函数中的A1,b1
beq = 0;
lb = zeros(n,1); %相当于Quadprog函数中的LB,UB
ub = C*ones(n,1);
a0 = zeros(n,1); % a0是解的初始近似值
[a,fval,eXitflag,output,lambda] = quadprog(H,f,A,b,Aeq,beq,lb,ub,a0,options);
epsilon = 1e-8;
sv_label = find(abs(a)>epsilon); %0<a<a(max)则认为x为支持向量
svm.a = a(sv_label);
svm.Xsv = X(:,sv_label);
svm.Ysv = Y(sv_label);
svm.svnum = length(sv_label);
%svm.label = sv_label;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function result = svmTest(svm, Xt, Yt, kertype)
temp = (svm.a'.*svm.Ysv)*kernel(svm.Xsv,svm.Xsv,kertype);
total_b = svm.Ysv-temp;
b = mean(total_b);
w = (svm.a'.*svm.Ysv)*kernel(svm.Xsv,Xt,kertype);
result.score = w + b;
Y = sign(w+b);
result.Y = Y;
result.accuracy = size(find(Y==Yt))/size(Yt);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function K = kernel(X,Y,type)
%X 维数*个数
switch type
case 'linear'
K = X'*Y;
case 'rbf'
delta = 5;
delta = delta*delta;
XX = sum(X'.*X',2);
YY = sum(Y'.*Y',2);
XY = X'*Y;
K = abs(repmat(XX,[1 size(YY,1)]) + repmat(YY',[size(XX,1) 1]) - 2*XY);
K = exp(-K./delta);
end