使用支持向量机(SVM)建一个邮件垃圾分类器。
作业分为Guassian Kernel、Parameters for dataset3、Email Preprocessing、Email Feature Extraction四部分。
其实就是在线性分类的基础上增加核函数。
支持向量机看上是很深奥的名次,实际上是很简单的概念。它一般用于分类器,就是对两堆东西分类。以二维平面为例,就是找一条直线,把两堆不同的点分开。这种直线可能有无数条,那么,如何选择最好的一条呢?
我们认为,最好的分类直线,应该是具有最好鲁棒性的直线。二维平面懒得画,以一维的数值为例,设训练集中有1,2,3,50,80,100这六个数,要分成两类。可以在4,5处分开,也可以在48,49处分开,但都不好。比如说在5处分开,那么6就被分到50,80,100那一侧,很明显这是错误的;再比如说在48处分开,那47就和1,2,3是一类,这看上去也不怎么样。因此,最好的应该是在(3+50)/2,即26处分开,这样分开是最理想的。这里的26就是支持向量机的表现。其中3和50称为支持向量,这两个支持了该直线,钦定它为分类直线。
因此,最好的直线,在高位空间应称作超平面,应该是距离所有支持向量距离一样,而且对于两个不同分类的支持向量,它们到分类直线的距离应该最远的直线。
实际上支持向量机的数学推导如下:
我们假设分类直线的解析式如下:
而“两个不同分类的支持向量”到直线的距离和为,它被称为间隔。简而言之,需要这个间隔最大,那么需要
最小,为简化计算,需要
最小。这里的
就是向量距离,也就是
。
有了需要最小化的函数,还需要它的限制条件。
考虑在预测时我们是如何给样本x分类的,带入,求出的值要是大于0,那么就是正样本,否则是负样本。但是大于0这一条件太弱了,很容易被破坏,出现误认现象。为了让其条件变强,选择大于1,小于-1作为分类标准。这样说出来的底气更足。这也是距离和为
的前提条件。那么限制条件就如下:
由此可以计算出对应的参数。
需要将代价函数最小化,这里其实也用了类似正则化的方法,但是没有用lambda,而是用了C,这个C的效果和1/lambda类似,因此,选择一个好的C值也很重要。
实际中计算参数时,matlab已经有了好用的函数,svmTrain,不需要再自己写了。用这个函数,输入训练集X,y,C,选择核函数就可以计算出对应的theta值了。
对于那些线性不可分的问题,我们无法找到一个超平面或者一条直线将所有的样本准确的分成两部分,因此需要利用核函数来帮助。本次作业选择使用高斯核函数。
核函数的具体作用就是拓展维度,用一种较为简便的方式求出高维空间中原样本向量的内积,在2维无法分开的问题,在3维中可能就是可分的了。直观来讲就是一种转换,比如说原来是1,2,3,5,8,理论上是将这几个元素映射到高维空间,再求内积,得到五个新的数1,2,100,200,250。但是直接那么映射再求内积太麻烦,求不出,选择使用核函数来求,可以减少计算量,曲线救国。
核函数怎么用呢?我们来看代价函数,原来预测需要使用,现在不用这个了,把x换成核函数求出的值,那么theta也要换,这样就是应用核函数了。
我们使用的高斯核函数如下:
需要在函数Guassian Kernel中实现,其代码如下:
x=x1-x2;
sim=exp(-x'*x/(2*sigma^2));
为了理解该核函数是如何工作的,我们可以来看一下,这一函数又称为相似度函数,简而言之,它可以评价xi和xj之间的相似度,若相似,则其值接近1,若不相似,则其值接近0。那么如何调整其严格度呢?需要使用高斯核函数的带宽,也就是公式中的,这个值越大,越不严格,离得较远的两个x也可能较为相似;这个值越小,则越严格。
那么问题来了,svmTrain中要两个超参数,C和,它们都需要用交叉验证集来确定值。这在函数Parameters for dataset3中实现。代码如下:
Cset=[0.01 0.03 0.1 0.3 1 3 10 30];
sigmaset=[0.01 0.03 0.1 0.3 1 3 10 30];
error=1000000;
for i=1:8
for j=1:8
Cnow=Cset(i);
sigmanow=sigmaset(j);
model= svmTrain(X, y, Cnow, @(x1, x2) gaussianKernel(x1, x2, sigmanow));
predictions = svmPredict(model, Xval);
errornow=mean(double(predictions ~= yval));
if errornow
如上,给C和分别设置一组预设值,然后训练多次SVM,给出多个预测结果,找出错误最少的超参数即可。
该部分将实际应用SVM,对邮件进行分类。本部分的重点在于数据处理。就是将邮件内容向量化。首先,将制作一个词典,将常用单词映射成数字,对于每个邮件,将其从单词转换为数字,得到一个矩阵。其次,将矩阵转换为0-1向量,也就是说,对于矩阵中的某个元素5,向量中第五个元素应该为1;若矩阵中未出现3,则向量中第三个元素为0。
在Email Preprocessing函数中,我们将实现邮件内容的矩阵化。代码如下:
for i=1:length(vocabList)
if strcmp(str,vocabList{i})==1
word_indices = [word_indices ; i];
break
end
end
该函数的输入是邮件内容,输出是邮件内容的数字映射版。注意,如果一个矩阵的元素不是数字而是字符串的话,那么其下标就不能用中括号,而要用大括号。
在Email Feature Extraction函数中,实现矩阵到0-1向量的映射。代码如下:
for i=1:length(word_indices)
if x(word_indices(i))==0
x(word_indices(i))=1;
end
end
之后就看它们表演吧。其实具体实现分类器的代码不是我们写的,都是利用已有的函数,感觉成就感不是很强。
本次作业对SVM进行了分析和应用,和上次作业差不多,具体作业很简单,但是其背后的原理很复杂。