Svm(support Vector Mac)又称为支持向量机,是一种二分类的模型。当然如果进行修改之后也是可以用于多类别问题的分类。支持向量机可以分为线性核非线性两大类。其主要思想为找到空间中的一个更够将所有数据样本划开的超平面,并且使得本本集中所有数据到这个超平面的距离最短。
在了解svm算法之前,我们首先需要了解一下线性分类器这个概念。比如给定一系列的数据样本,每个样本都有对应的一个标签。为了使得描述更加直观,我们采用二维平面进行解释,高维空间原理也是一样。举个简单子:如下图所示是一个二维平面,平面上有两类不同的数据,分别用圆圈和方块表示。我们可以很简单地找到一条直线使得两类数据正好能够完全分开。但是能将据点完全划开直线不止一条,那么在如此众多的直线中我们应该选择哪一条呢?从直观感觉上看图中的几条直线,是不是要更好一些呢?是的我们就是希望寻找到这样的直线,使得距离这条直线最近的点到这条直线的距离最短。这读起来有些拗口,我们从图三中直观来解释这一句话就是要求的两条外面的线之间的间隔最大。这是可以理解的,因为假如数据样本是随机出现的,那么这样分割之后数据点落入到其类别一侧的概率越高那么最终预测的准确率也会越高。在高维空间中这样的直线称之为超平面,因为当维数大于三的时候我们已经无法想象出这个平面的具体样子。那些距离这个超平面最近的点就是所谓支持向量,实际上如果确定了支持向量也就确定了这个超平面,找到这些支持向量之后其他样本就不会起作用了。
图 1 图2
1.2.1点到超平面的距离公式
既然这样的直线是存在的,那么我们怎样寻找出这样的直线呢?与二维空间类似,超平面的方程也可以写成一下形式:
(1.1)
有了超平面的表达式之后之后,我们就可以计算样本点到平面的距离了。假设为样本的中的一个点,其中表示为第个特征变量。那么该点到超平面的距离就可以用如下公式进行计算:
(1.2)
其中||W||为超平面的范数,常数b类似于直线方程中的截距。
上面的公式可以利用解析几何或高中平面几何知识进行推导,这里不做进一步解释。
1.2.2最大间隔的优化模型
现在我们已经知道了如何去求数据点到超平面的距离,在超平面确定的情况下,我们就能够找出所有支持向量,然后计算出间隔margin。每一个超平面都对应着一个margin,我们的目标就是找出所有margin中最大的那个值对应的超平面。因此用数学语言描述就是确定w、b使得margin最大。这是一个优化问题其目标函数可以写成:
(1.3)
其中表示数据点的标签,且其为-1或1。距离用计算,这是就能体会出-1和1的好处了。如果数据点在平面的正方向(即+1类)那么是一个正数,而当数据点在平面的负方向时(即-1类),依然是一个正数,这样就能够保证始终大于零了。注意到当w和b等比例放大时,d的结果是不会改变的。因此我们可以令所有支持向量的u为1,而其他点的u大1这是可以办通过调节w和b求到的。因此上面的问题可以简化为: (1.4)
为了后面计算的方便,我们将目标函数等价替换为:
(1.5)
这是一个有约束条件的优化问题,通常我们可以用拉格朗日乘子法来求解。拉格朗日乘子法的介绍可以参考这篇博客。应用拉格朗日乘子法如下:
令 (1.6)
求L关于求偏导数得: (1.7)
将(1.7)代入到(1.6)中化简得:
(1.8)
原问题的对偶问题为:
(1.9)
该对偶问题的KKT条件为
(1.10)
到此,似乎问题就能够完美地解决了。但是这里有个假设:数据必须是百分之百可分的。但是实际中的数据几乎都不那么“干净”,或多或少都会存在一些噪点。为此下面我们将引入了松弛变量来解决这种问题。
1.2.3松弛变量
由上一节的分析我们知道实际中很多样本数据都不能够用一个超平面把数据完全分开。如果数据集中存在噪点的话,那么在求超平的时候就会出现很大问题。从图三中课看出其中一个蓝点偏差太大,如果把它作为支持向量的话所求出来的margin就会比不算入它时要小得多。更糟糕的情况是如果这个蓝点落在了红点之间那么就找不出超平面了。
图 3
因此引入一个松弛变量ξ来允许一些数据可以处于分隔面错误的一侧。这时新的约束条件变为:
(1.11)
式中ξi的含义为允许第i个数据点允许偏离的间隔。如果让ξ任意大的话,那么任意的超平面都是符合条件的了。所以在原有目标的基础之上,我们也尽可能的让ξ的总量也尽可能地小。所以新的目标函数变为:
(1.12)
(1.13)
其中的C是用于控制“最大化间隔”和“保证大部分的点的函数间隔都小于1”这两个目标的权重。将上述模型完整的写下来就是:
(1.14)
新的拉格朗日函数变为:
(1.15)
接下来将拉格朗日函数转化为其对偶函数,首先对分别求ξ的偏导,并令其为0,结果如下:
(1.16)
代入原式化简之后得到和原来一样的目标函数:
(1.17)
但是由于我们得到而,因此有所以对偶问题写成:
(1.18)
经过添加松弛变量的方法,我们现在能够解决数据更加混乱的问题。通过修改参数C,我们可以得到不同的结果而C的大小到底取多少比较合适,需要根据实际问题进行调节。
1.2.4核函数
以上讨论的都是在线性可分情况进行讨论的,但是实际问题中给出的数据并不是都是线性可分的,比如有些数据可能是如图4样子。
图4
那么这种非线性可分的数据是否就不能用svm算法来求解呢?答案是否定的。事实上,对于低维平面内不可分的数据,放在一个高维空间中去就有可能变得可分。以二维平面的数据为例,我们可以通过找到一个映射将二维平面的点放到三维平面之中。理论上任意的数据样本都能够找到一个合适的映射使得这些在低维空间不能划分的样本到高维空间中之后能够线性可分。我们再来看一下之前的目标函数:
(1.19)
定义一个映射使得将所有映射到更高维空间之后等价于求解上述问题的对偶问题:
(1.20)
这样对于线性不可分的问题就解决了,现在只需要找出一个合适的映射即可。当特征变量非常多的时候在,高维空间中计算内积的运算量是非常庞大的。考虑到我们的目的并不是为找到这样一个映射而是为了计算其在高维空间的内积,因此如果我们能够找到计算高维空间下内积的公式,那么就能够避免这样庞大的计算量,我们的问题也就解决了。实际上这就是我们要找的核函数,即两个向量在隐式映射后的空间中的内积。下面的一个简单例子可以帮助我们更好地理解核函数。
通过以上例子,我们可以很明显地看到核函数是怎样运作的。上述问题的对偶问题可以写成如下形式:
(1.21)
那么怎样的函数才可以作为核函数呢?下面的一个定理可以帮助我们判断。
Mercer定理:任何半正定的函数都可以作为核函数。其中所谓半正定函数是指拥有训练集数据集合,我们定义一个矩阵的元素,这个矩阵是的矩阵,如果这个矩阵是半正定的,那么就称为半正定函数。
值得注意的是,上述定理中所给出的条件是充分条件而非充要条件。因为有些非正定函数也可以作为核函数。
下面是一些常用的核函数:
表1 常用核函数表
核函数名称 |
核函数表达式 |
核函数名称 |
核函数表达式 |
线性核 |
指数核 |
||
多项式核 |
拉普拉斯核 |
||
高斯核 |
Sigmoid核 |
现在我们已经了解了一些支持向量机的理论基础,我们通过对偶问题的的转化将最开始求的问题转化为求的对偶问题。只要找到所有的(即找出所有支持向量),我们就能够确定。然后就可以通过计算数据点到这个超平面的距离从而判断出该数据点的类别。
function varargout = main_gui(varargin)
% MAIN_GUI MATLAB code for main_gui.fig
% MAIN_GUI, by itself, creates a new MAIN_GUI or raises the existing
% singleton*.
%
% H = MAIN_GUI returns the handle to a new MAIN_GUI or the handle to
% the existing singleton*.
%
% MAIN_GUI('CALLBACK',hObject,eventData,handles,...) calls the local
% function named CALLBACK in MAIN_GUI.M with the given input arguments.
%
% MAIN_GUI('Property','Value',...) creates a new MAIN_GUI or raises the
% existing singleton*. Starting from the left, property value pairs are
% applied to the GUI before main_gui_OpeningFcn gets called. An
% unrecognized property name or invalid value makes property application
% stop. All inputs are passed to main_gui_OpeningFcn via varargin.
%
% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one
% instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES
% Edit the above text to modify the response to help main_gui
% Last Modified by GUIDE v2.5 29-Dec-2018 17:29:22
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @main_gui_OpeningFcn, ...
'gui_OutputFcn', @main_gui_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before main_gui is made visible.
function main_gui_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to main_gui (see VARARGIN)
% Choose default command line output for main_gui
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes main_gui wait for user response (see UIRESUME)
% uiwait(handles.figure1);
% --- Outputs from this function are returned to the command line.
function varargout = main_gui_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global str img cc
[filename,pathname] = uigetfile({'*.jpg';'*.bmp'},'选择图片');
str = [pathname,filename];
img = imread(str);
cc=imread(str);
subplot(1,3,1),imshow(cc);
set(handles.text5,'string',str);
% --- Executes on button press in pushbutton3.
function pushbutton3_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton3 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
close(gcf);
% --- Executes on button press in pushbutton4.
function pushbutton4_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton4 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global cc img t
load('save.mat');
mapping=getmapping(8,'u2');%LBP映射
W=[2,1,1,1,1,1,2; ...
2,4,4,1,4,4,2; ...
1,1,1,0,1,1,1; ...
0,1,1,0,1,1,0; ...
0,1,1,1,1,1,0; ...
0,1,1,2,1,1,0; ...
0,1,1,1,1,1,0];
d=[];
image_size = size(cc);
dimension = numel(image_size);
if dimension == 3
cc=rgb2gray(cc);
end
X = double(cc);
X=255*imadjust(X/255,[0.3;1],[0;1]);
X = imresize(X,[64 64],'bilinear'); %采用'bilinear':采用双线性插值算法扩展为64*64
H2=DSLBP(X,mapping,W);%提取图片的LBP直方图
Gray=X;
Gray=(Gray-mean(Gray(:)))/std(Gray(:))*20+128;
lpqhist=lpq(Gray,3,1,1,'nh'); %计算每个照片lpq直方图
a=[H2,lpqhist];
d=[d;a];
P_test=d;
P_test=mapminmax(P_test,0,1);
%%%%%%%%以上是特征提取的部分
%%%%%从这里开始是识别表情的算法,使用支持向量机来识别
addpath SVM-KM %%添加支持向量机工具箱
c = 100;
kerneloption= 1.3; %设置核参数
kernel='gaussian'; %设置高斯核作为支持向量机的核函数
[ypred2,maxi] = svmmultival(P_test,xsup,w,b,nbsv,kernel,kerneloption);
for i=1:length(ypred2)
if ypred2(i)==1 disp('Anger');t='Anger';
elseif ypred2(i)==2 t='Disgust';
elseif ypred2(i)==3 t='Fear';
elseif ypred2(i)==4 t='Happiness';
elseif ypred2(i)==5 t='Sad';
elseif ypred2(i)==6 t='Surprise';
end
detector = vision.CascadeObjectDetector;
bboxes=step(detector,img);
FrontalFaceCART=insertObjectAnnotation(img,'rectangle',bboxes,t,'color','cyan','TextBoxOpacity',0.8,'FontSize',13);
subplot(1,3,2),imshow(FrontalFaceCART);
end
set(handles.text10,'string',t);
% --- Executes during object creation, after setting all properties.
function text5_CreateFcn(hObject, eventdata, handles)
% hObject handle to text5 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% --- Executes on button press in pushbutton6.
function pushbutton6_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton6 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
load('save.mat');
disp('训练结束');
axes(handles.axes2);
vid = videoinput('winvideo',1,'YUY2_640x480');
set(vid,'ReturnedColorSpace','rgb');
vidRes = get(vid, 'VideoResolution');
nBands = get(vid, 'NumberOfBands');
hImage = image( zeros(vidRes(2), vidRes(1), nBands) );
preview(vid, hImage);
disp('摄像头开启');
faceDetector1 = vision.CascadeObjectDetector;
while(1)
frame = getsnapshot(vid);
box = step(faceDetector1, frame); % Detect faces
if isempty(box)==0
ff=imcrop(frame,[box(1),box(2),box(3),box(4)]);
%figure;imshow(cc);
ff=rgb2gray(ff);
%figure;imshow(cc);
ff=histeq(ff); %直方图均衡化
% imwrite(cc,'.\test\1.jpg');
yy=svm_test(xsup,w,b,nbsv,ff);
h=rectangle('position',[box(1),box(2),box(3),box(4)],'LineWidth',2,'edgecolor','b');
for i=1:length(yy)
if yy(i)==1 t1=text(box(1),box(2)-20, sprintf('生气'),'FontSize',14,'Color','blue','FontWeight','Bold');
elseif yy(i)==2 t1=text(box(1),box(2)-20, sprintf('厌恶'),'FontSize',14,'Color','blue','FontWeight','Bold');
elseif yy(i)==3 t1=text(box(1),box(2)-20, sprintf('恐惧'), 'FontSize',14,'Color','blue','FontWeight','Bold');
elseif yy(i)==4 t1=text(box(1),box(2)-20, sprintf('高兴 '), 'FontSize',14,'Color','blue','FontWeight','Bold');
elseif yy(i)==5 t1=text(box(1),box(2)-20, sprintf('悲伤'), 'FontSize',14,'Color','blue','FontWeight','Bold');
elseif yy(i)==6 t1=text(box(1),box(2)-20, sprintf('惊讶'), 'FontSize',14,'Color','blue','FontWeight','Bold');
end
end
pause(0.05);
set(t1,'string',[]);
delete(h);
if strcmpi(get(gcf,'CurrentCharacter'),'c')
delete(vid);
disp('程序退出');
break;
end
else t1=text(10,10, sprintf('未检测到人脸'), 'FontAngle','italic','FontSize',15,'Color','b','FontWeight','Bold');
pause(0.05);
set(t1,'string',[]);
if strcmpi(get(gcf,'CurrentCharacter'),'1')
delete(vid);
disp('程序退出');
break;
end
end
end