ma_model_preparation5

SVM

关于SVM的内容介绍,主要参考了这位博主的SVM文章点击此处进行查看(讲得太好了)

1.简介

支持向量机(Support Vector Machine)是Cortes和Vapnik于1995年首先提出的,它在解决小样本、非线性及高维模式识别中表现出许多特有的优势,并能够推广应用到函数拟合等其他机器学习问题中.

支持向量机方法是建立在统计学习理论的VC维理论和结构风险最小原理基础上的,根据有限的样本信息在模型的复杂性(即对特定训练样本的学习精度,Accuracy)和学习能力(即无错误地识别任意样本的能力)之间寻求最佳折衷,以期获得最好的推广能力(泛化能力)
所谓VC维是对函数类的一种度量,可以简单的理解为问题的复杂程度,VC维越高,一个问题就越复杂。正是因为SVM关注的是VC维,后面我们可以看到,SVM解决问题的时候,和样本的维数是无关的(甚至样本是上万维的都可以,这使得SVM很适合用来解决文本分类的问题,当然,有这样的能力也因为引入了核函数)。

与问题真实解之间的误差,就叫做风险(更严格的说,误差的累积叫做风险)。统计学习因此而引入了泛化误差界的概念,就是指真实风险应该由两部分内容刻画,一是经验风险,代表了分类器在给定样本上的误差;二是置信风险,代表了我们在多大程度上可以信任分类器在未知文本上分类的结果。很显然,第二部分是没有办法精确计算的,因此只能给出一个估计的区间,也使得整个误差只能计算上界,而无法计算准确的值(所以叫做泛化误差界,而不叫泛化误差)。置信风险与两个量有关,一是样本数量,显然给定的样本数量越大,我们的学习结果越有可能正确,此时置信风险越小;二是分类函数的VC维,显然VC维越大,推广能力越差,置信风险会变大。
SVM 有如下的优点

小样本 非线性 高维模式识别

2.数学定义

考虑线性分类器

假设线性分类函数 g(x)=wx+b ,当然,其中w,x,b 均为高维向量,那么,不妨假设以g(x)=0为分界面,对于每个$x_i$而言,即有$y_i=sign(g(x_i))$,那么$y_i$要么为1,要么为-1,如此,定义$\sigma_i =\frac{y_i*g(x_i)}{||w||_2}$ ,因此,实际上$\sigma = \sum_{i=1}^n {\sigma_i}$为所有的距离之和,因而,我们找到一个最好的分类的目标变为$min ||w||$,为了在后续过程中使得我们的计算简单,我们将目标函数变为

但如若着这样,会存在一个问题,即分类的结果都会存在于两个分类器的中间地带,那么这样会导致$||w||=0$实际上不是我们需要的值,故在此基础上,我们还需要加上约束条件$y_i*g(x_i)>=1(i=1,2,\ldots,n)$ 故,最终结果表示为

实际上,这里的问题变为了一个凸优化问题,同时也是一个二次规划问题。我们知道,这样一个凸的规划问题,完全可以求得最优解,故我们的问题能够得到实际上的解决。那么需要怎么解决呢?

3.问题解决

实际上,我们求解的目标就是w,因为求得了w之后,可以求得b,则最终可以求到我们想要得到的g(x)这个函数,也就是分界面。而实际上,w与样本有关,那么我们可以定义$w=a_1x_1+a_2x_2+\ldots+a_nx_n$ 其中$a$为拉格朗日乘子,那么再次回顾$g(x)=\vec{w} \cdot \vec{x} +\vec{b}$,再次分析$\vec{w}$ 的表达式,我们可以发现,依然有错误,实际上,w还与y有关,故,我们可以将w这样表示

因此 $g(x) = +b= \sum_{i=1}^n{(a_iy_i)}+b$,因此我们可以看到,我们实际上把w给消掉了,没有了w,实际上也减少了很多约束条件,接下来,我们将开始很重要的一个介绍,核函数

4.核函数

一个解决线性不可分问题的基本思路————向高维空间转化,使其变得线性可分(一个二维空间中的问题,当映射到四维空间中的时候,就变得线性可分了),然而于此同时,也出现了很多问题,例如对于一个文本分类问题,将其映射到上万维的空间中,依旧是线性不可分的,然而,对于高维空间的计算,需要耗费大量的计算资源,因此,这就带来了不可计算的问题(这是实际工程中的一个重要的问题)

因此我们的目标已经明确了,找到一种能够映射到很高维的方法(要是能到无穷维就更好了!),并且能保证其在有限时间中可计算(如果能与没有升维之前有同样的计算阶那就再好不过了,即是线性的)。因此我们幻想,是否能有这样一种函数K(w,x),他接受低维空间的输入值,却能算出高维空间的内积值。我们不妨假设这个函数为核函数K,那么即有

这两个函数完全相等。因此,我们的目标变为了对非线性的分类问题,有$g(x)=\sum_{i=1}^n a_iy_i+b$
那么核函数(kernel)存在吗?万幸的是,这样的K(w,x)确实存在(发现凡是我们人类能解决的问题,大都是巧得不能再巧,特殊得不能再特殊的问题,总是恰好有些能投机取巧的地方才能解决,由此感到人类的渺小),它被称作核函数(核,kernel),而且还不止一个,事实上,只要是满足了Mercer条件的函数,都可以作为核函数。核函数的基本作用就是接受两个低维空间里的向量,能够计算出经过某个变换后在高维空间里的向量内积值。
常用的核函数有哪些呢?这里可以直接参考这一篇blog,常用的核函数有四个,分别为线性核函数,多项式核函数,高斯(RBF)核函数与sigmoid核函数,对于其的比较,我在这里就不区分了,在实践中,多用,就能更好地理解这几个核函数的区别。

5.其它问题

我们必须再次思考其它的问题,即如果使用核函数向高维空间映射后,问题仍然是线性不可分的,那怎么办?

这一问题实际上我们可以理解为噪声问题,在实际过程中,我们所采集到的数据通常都会有噪声,那么,噪声会改变我们本来正确的分类结果,对程序而言,会导致一种情况,即我们无法对这样的数据,甚至映射到高维空间中后,也线性不可分。那么如何抑制此现象的产生呢?这实际上与我们之前提出的思路有极大的关联,即我们之前给出定义为

这实际上要求了所有的点,均必须满足这一条件,那么这会导致不可分(纵然在极高维的空间中,或许它也可分,但所分出来的结果必然是过拟合的,而不是我们想要的),所以我们模仿人类的思想,只保证绝大多数分类正确,因此,我们给这个阈值加上一个松弛变量,使得原式变为:

在此基础上,我们的优化问题的目标有所改变

其中,C是一个超参数,建议设一组值来进行尝试。这里在具体对C进行一个讨论,C是一个固定参数,对此可以这样理解,如果C设置得较大,则说明我们很care那些被丢弃掉的点,如果我们不care那些点的话,我们则可以将C设置得小一点,同时,我们也能对不同的点设置不同的C,那么,则代表我们对某些点有特殊的偏好,这被称作数据集偏斜。对于特定的问题,读者应该对C的设置有不同的思考,要把握C的实质内容。

6. 将SVM用于多类分类

我们实际上可以将一个多分类问题,看做一个”一类对其余“的问题,那么每次仍然是解一个两类分类的问题。比如我们有5个类别,第一次就把类别1的样本定为正样本,其余2,3,4,5的样本合起来定为负样本,这样得到一个两类分类器,它能够指出一篇文章是还是不是第1类的;第二次我们把类别2 的样本定为正样本,把1,3,4,5的样本合起来定为负样本,得到一个分类器,如此下去,我们可以得到5个这样的两类分类器(总是和类别的数目一致)。到了有文章需要分类的时候,我们就拿着这篇文章挨个分类器的问:是属于你的么?是属于你的么?哪个分类器点头说是了,文章的类别就确定了。这种方法的好处是每个优化问题的规模比较小,而且分类的时候速度很快(只需要调用5个分类器就知道了结果)。但有时也会出现两种很尴尬的情况,例如拿一篇文章问了一圈,每一个分类器都说它是属于它那一类的,或者每一个分类器都说它不是它那一类的,前者叫分类重叠现象,后者叫不可分类现象。分类重叠倒还好办,随便选一个结果都不至于太离谱,或者看看这篇文章到各个超平面的距离,哪个远就判给哪个。不可分类现象就着实难办了,只能把它分给第6个类别了……更要命的是,本来各个类别的样本数目是差不多的,但“其余”的那一类样本数总是要数倍于正类(因为它是除正类以外其他类别的样本之和嘛),这就人为的造成了上一节所说的“数据集偏斜”问题。
因此,这样一个思路看上去可行,但实际上也需要一定的调整才能对实际的多分类问题进行应用。那需要哪些改善呢?还是解两类分类问题,还是每次选一个类的样本作正类样本,而负类样本则变成只选一个类(称为“一对一单挑”的方法,哦,不对,没有单挑,就是“一对一”的方法),这就避免了偏斜。但这样,会大量增加分类器的数量。
看来我们必须再退一步,在分类的时候下功夫,我们还是像一对一方法那样来训练,只是在对一篇文章进行分类之前,我们先按照下面图的样子来组织分类器(如你所见,这是一个有向无环图,因此这种方法也叫做DAG SVM)

DAG SVM

这样在分类时,我们就可以先问分类器“1对5”(意思是它能够回答“是第1类还是第5类”),如果它回答5,我们就往左走,再问“2对5”这个分类器,如果它还说是“5”,我们就继续往左走,这样一直问下去,就可以得到分类结果。好处在哪?我们其实只调用了4个分类器(如果类别数是k,则只调用k-1个),分类速度飞快,且没有分类重叠和不可分类现象!缺点在哪?假如最一开始的分类器回答错误(明明是类别1的文章,它说成了5),那么后面的分类器是无论如何也无法纠正它的错误的(因为后面的分类器压根没有出现“1”这个类别标签),其实对下面每一层的分类器都存在这种错误向下累积的现象。

关于SMO算法的学习,在这里就不加以重点介绍,需要的可以参考SMO这一篇博客

python 代码实现

在python中,scikit-learn是一个广泛使用的用于实现机器学习算法的库(也许后面我也会对这个库的使用方法作一系列的博客介绍),SVM算法可以在这个库中找到并用于实现。
这里的python实现借用了blog的实现方式,需要源码的也可以从中进行下载

sklearn中的SVC函数是基于libsvm实现的,所以在参数设置上有很多相似的地方。(PS: libsvm中的二次规划问题的解决算法是SMO)。

1
2
3
4
5
6
7
8
9
10
#Import Library
from sklearn import svm
#Assumed you have, X (predictor) and Y (target) for training data set and x_test(predictor) of test_dataset
# Create SVM classification object
model = svm.svc(kernel='linear', c=1, gamma=1)
# there is various option associated with it, like changing kernel, gamma and C value. Will discuss more # about it in next section.Train the model using the training sets and check score
model.fit(X, y)
model.score(X, y)
#Predict Output
predicted= model.predict(x_test)
1
sklearn.svm.SVC(C=1.0, kernel='rbf', degree=3, gamma=0.0, coef0=0.0, shrinking=True, probability=False,tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, random_state=None)

这里展示了sklearn.svm.SVC的主要的参数,其中C为我们设置的超参数,而kernel有多种选择,这里为’rbf’,即高斯核函数。除此之外,还可以选择’linear’:线性核函数 ‘poly’ 多项式核函数 ‘sigmoid’ sigmoid核函数 ‘precomputed’:核矩阵(precomputed表示自己提前计算好核函数矩阵,这时候算法内部就不再用核函数去计算核矩阵,而是直接用你给的核矩阵。)

degree: int型参数 默认为3这个参数只对多项式核函数有用,是指多项式核函数的阶数n如果给的核函数参数是其他核函数,则会自动忽略该参数。

gamma: float参数 默认为auto核函数系数,只对‘rbf’,‘poly’,‘sigmod’有效。如果gamma为auto,代表其值为样本特征数的倒数,即1/n_features.

coef0: float参数 默认为0.0。核函数中的独立项,只有对‘poly’和‘sigmod’核函数有用,是指其中的参数c

probability: bool参数 默认为False。是否启用概率估计。 这必须在调用fit()之前启用,并且会fit()方法速度变慢。

还有其它的参数,都可以参照svm参数说明进行学习,这里就不再具体说明。