一、前言
在推导出SVM公式的基础上,就可以考虑动手实现了。SVM解决分类问题,这里用MATLAB来实现,具体就不多说了,所以首先给出两种标记不同的点,然后分别标记为+1,-1。先训练,再测试,最后画图展示出来。代码也是主演参考的别人的,有加上自己的理解注释。
二、流程及实现
1.流程图
2.大家对二次规划可能有点陌生,可以查看帮助文档或者百度,讲解得都很详细,下面是我简单记录一下,其实就是一一对应起来:
3.得到大致流程之后,下面直接贴代码,复制之后就可直接运行。
主函数代码如下:
- %------------主函数----------------
- clear all;
- close all;
- C = 10; %成本约束参数
- kertype = 'linear'; %线性核
-
- %①------数据准备
- n = 30;
- %randn('state',6); %指定状态,一般可以不用
- x1 = randn(2,n); %2行N列矩阵,元素服从正态分布
- y1 = ones(1,n); %1*N个1
- x2 = 4+randn(2,n); %2*N矩阵,元素服从正态分布且均值为5,测试高斯核可x2 = 3+randn(2,n);
- y2 = -ones(1,n); %1*N个-1
-
- figure; %创建一个用来显示图形输出的一个窗口对象
- plot(x1(1,:),x1(2,:),'bs',x2(1,:),x2(2,:),'k+'); %画图,两堆点
- axis([-3 8 -3 8]); %设置坐标轴范围
- hold on; %在同一个figure中画几幅图时,用此句
-
- %②-------------训练样本
- X = [x1,x2]; %训练样本2*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)];
- %前半句reshape(x1,1,nt)是将x1转成1*(181*181)的矩阵,所以xt是2*(181*181)的矩阵
- %reshape函数重新调整矩阵的行、列、维数
- Yt = ones(1,nt);
-
- result = svmTest(svm, Xt, Yt, kertype);
-
- %④--------------画曲线的等高线图
- Yd = reshape(result.Y,rows,cols);
- contour(x1,x2,Yd,[0,0],'ShowText','on');%画等高线
- title('svm分类结果图');
- x1=xlabel('X轴');
- x2=ylabel('Y轴');
训练样本函数svmTrain:
- %-----------训练样本的函数---------
- function svm = svmTrain(X,Y,kertype,C)
-
- % Options是用来控制算法的选项参数的向量,optimset无参时,创建一个选项结构所有字段为默认值的选项
- options = optimset;
- options.LargeScale = 'off';%LargeScale指大规模搜索,off表示在规模搜索模式关闭
- options.Display = 'off'; %表示无输出
-
- %二次规划来求解问题,可输入命令help quadprog查看详情
- n = length(Y); %返回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);
- %a是输出变量,问题的解
- %fval是目标函数在解a处的值
- %eXitflag>0,则程序收敛于解x;=0则函数的计算达到了最大次数;<0则问题无可行解,或程序运行失败
- %output输出程序运行的某些信息
- %lambda为在解a处的值Lagrange乘子
-
- epsilon = 1e-8;
- %0<a<a(max)则认为x为支持向量,find返回一个包含数组X中每个非零元素的线性索引的向量。
- sv_label = find(abs(a)>epsilon);
- svm.a = a(sv_label);
- svm.Xsv = X(:,sv_label);
- svm.Ysv = Y(sv_label);
- svm.svnum = length(sv_label);
- %svm.label = sv_label;
- end
测试函数svmTest:
- %---------------测试的函数-------------
- 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(svm.Ysv-temp); %b取均值
- w = (svm.a'.*svm.Ysv)*kernel(svm.Xsv,Xt,kertype);
- result.score = w + b;
- Y = sign(w+b); %f(x)
- result.Y = Y;
- result.accuracy = size(find(Y==Yt))/size(Yt);
- end
核函数kernel:
- %---------------核函数---------------
- 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);%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
- end
4.结果
|
请发表评论