使用支持向量机(SVM) 算法进行分类
1 支持向量机(SVM)的基本概念
SVM是一种分类算法,其侧重于模式识别方面。使用SVM可以大大提高分类的准确性。
分类相当于模式识别的子集合,模式识别重点在于对已知数据进行特征发现与提取。
SVM重点在于解决线性可分的问题。但很多时候,实际的问题是线性不可分的。SVM的思想就是将线性不可分的问题转化线性可分的问题。那么如何来是实现呢?就是将空间映射到多维空间。如把二维空间映射到三维空间。以增加维数来减少方程的次数。
比如,在二维空间中,不得不用 f(x)=ax2+b+c 这个曲线将两类样本分开,而不是用一条直线将它们分开,这样就是一个线性不可分的问题。为了解决这个问题,我们可以使用换元法将即 x^2=y1 , x=y2 , 这样一来替换为 z=a'y1+b'y2+c' 这个线性方程,从而转换到了高维空间,代价是维度的增加引入了更多的变量。这样我们就完成了从线性不可分问题到线性可分问题的转换。
所谓支持向量,即在每一类样本集中,过离separating hyper plane 最近的样本做一个与separating hyper plane 平行的线/超平面。有几个分类,就有几个这种直线/超平面,这样的直线/超平面就叫做支持向量(Support Vector)。
2 核函数
那么找到一个线/超平面来完成二分类成为问题的关键。这个线/超平面的函数被我们成为“核函数”。常见的核函数有:
- 线性核函数————linear
- 多项式核函数————poly
- 径向基核函数————rbf(用得较多)
- Sigmoid核函数————sigmoid
那么如何来选择合适的核函数呢?答案就是比较不同核函数的分类的准确率。在实际处理分类问题时,分别计算几种核函数的分类性能,将准确率最高的核函数作为最终用于预测分类的核函数即可。在本文的例子中,我们使用画图的方式来判断分类性能的好坏。
使用模型不同,转换的方式不同,在多维空间中的图形就不同,分类的效果就不同。
网上关于SVM、线性可不可分、核函数的博文很多,如http://www.cnblogs.com/LeftNotEasy/archive/2011/05/02/basic-of-svm.html 。这里就不再赘述。
3 比较分类效果与子图的划分
为了比较不同分类器的准确性,我们采用绘制散点图,然后人工观察来判断。我们将两个类的点在坐标系中用不同颜色表示出来,同时标出训练数据,看预测点与训练点的在图中的重叠情况。重叠越紧密,说明分类的效果越准确。
在绘图的时候,我们希望在一个平面中同时展现多幅图片,这时使用matplotlib模块的子图(subplot)来展现。
subplot的参数
subplot(横向划分个数,纵向划分个数,当前定位)
第一个参数代表的是横向要划分的子图个数,第二个参数代表的是纵向要划分的子图的个数,第三个参数表示当前的定位。
使用示例一:
使用示例二:
使用示例三:
4 源码
应用场景
假设在第一象限有10个点,它们分别是[0, 0], [1, 1], ... , [9, 9]。它们[0,0],[1,1],[2,2],[3,3]属于“0”这个类(类标签为0),另外6个点属于“1”这个类(类标签为1)。
现在第一象限构造900*900个点,纵横坐标方向上相邻的点距离为0.01。
[ 0.00, 0.00],[ 0.01, 0.00], [ 0.02, 0.00],..., [ 8.97, 8.99], [ 8.98, 8.99],[ 8.99, 8.99]
这样,布满区间的点作为预测数据,用各种分类模型进行二分类。最后通过绘图模块给不同类的点上色以判断分类性能。
import numpy as npy
import matplotlib.pyplot as plt
from sklearn import svm
x=[]#存储样本数据
y=[]#存储类标号
for i in range(0,10):#构造10个点作为训练数据
if(i<=3):#if(i<=3 or i>=8):
x.append([i,i])
y.append(0)
else:
x.append([i,i])
y.append(1)
train_x=npy.array(x)#转换为数组
train_y=npy.array(y)
'''
创建svm分类器的格式:
svm.SVC(kernel=某个核函数).fit(训练样本,类标签)
'''
#linear
linear_svc=svm.SVC(kernel="linear").fit(train_x,train_y)
#poly 要定义维度,degree决定了多项式的最高次幂.关于SVC参数的意义请参见文章后头的内容。
poly_svc=svm.SVC(kernel="poly",degree=4).fit(train_x,train_y)
#径向基核函数(这时SVC默认的核函数)
rbf_svc=svm.SVC().fit(train_x,y)
#Sigmoid
sigmoid_svc=svm.SVC(kernel="sigmoid").fit(train_x,train_y)
#下面就可以进行预测了
x1,x2=npy.meshgrid(npy.arange(train_x[:,0].min(),train_x[:,0].max(),0.01),npy.arange(train_x[:,1].min(),train_x[:,1].max(),0.01))
#先生成各个点。定义最小值和最大值后,定义隔多少值建立一个点。
#npy.arange(train_x[:,1].min(),train_x[:,1].max(),0.01))返回的是900个元素的数组
#meshgrid函数用来产生矩阵。上面的语句也是就是numpy.meshgrid(numpy.arange(0,9,0.01),numpy.arange(0,9,0.01))
'''
x1是矩阵
[[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
...,
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99]]
x2是矩阵
[[ 0. , 0. , 0. , ..., 0. , 0. , 0. ],
[ 0.01, 0.01, 0.01, ..., 0.01, 0.01, 0.01],
[ 0.02, 0.02, 0.02, ..., 0.02, 0.02, 0.02],
...,
[ 8.97, 8.97, 8.97, ..., 8.97, 8.97, 8.97],
[ 8.98, 8.98, 8.98, ..., 8.98, 8.98, 8.98],
[ 8.99, 8.99, 8.99, ..., 8.99, 8.99, 8.99]]
'''
splocation=1
for i in [linear_svc,poly_svc,rbf_svc,sigmoid_svc]:#遍历各个模型以便绘图,以看哪个核函数的准确率更高
rst = i.predict(npy.c_[x1.ravel(),x2.ravel()])#横坐标和纵坐标的组合。x1.ravel()和x2.ravel()都是长度为810000的数组。c_[]用来将前后两个数组串联成一个810000行、2列的矩阵。
#因为上面用到了四种分类模型,那么一个2×2的图就能够显示完全了。
plt.subplot(2,2,splocation)#第一个参数代表的是横向要划分的子图个数,第二个参数代表的是纵向要划分的子图的个数,第三个参数表示当前的定位
plt.contourf(x1,x2,rst.reshape(x1.shape))#contourf用来填充颜色。(当前横坐标,当前纵坐标,预测的分类结果(转为x1的规模维数))
#训练数据的点也绘制出来
for j in range(0,len(y)):
if(int(y[j])==0):
plt.plot(train_x[j:j+1,0],train_x[j:j+1],"yo")#y代表黄色,o代表散点图
else:
plt.plot(train_x[j:j+1,0],train_x[j:j+1],"ko")#类别为1填充为黑色。k代表黑色
splocation+=1
plt.show()
运行结果:
可见,在比较这种分布下,径向基核函数表现出更好的分类性能。
5 SVC的参数
具体参数的意义与使用请参见链接:http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html
- C:目标函数的惩罚系数C,用来平衡分类间隔margin和错分样本的,default C = 1.0;
C越大,相当于惩罚松弛变量,希望松弛变量接近0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样对训练集测试时准确率很高,但泛化能力弱。C值小,对误分类的惩罚减小,允许容错,将他们当成噪声点,泛化能力较强。
-
kernel :核函数,默认是rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’
-
degree :多项式poly函数的维度,默认是3,选择其他核函数时会被忽略。
-
gamma : ‘rbf’,‘poly’ 和‘sigmoid’的核函数参数。默认是’auto’,则gamma=1/n_features
-
coef0 :核函数的常数项。对于‘poly’和 ‘sigmoid’有用。
-
probability :是否采用概率估计。默认为False。要采用的话必须先于调用fit,这个过程会增加用时。
-
shrinking :是否采用shrinking heuristic方法,默认为true
-
tol :停止训练的误差值大小,默认为1e-3
-
cache_size :核函数cache缓存大小,默认为200
-
class_weight :类别的权重,字典形式传递。设置第几类的参数C为weight*C(C-SVC中的C)
-
verbose :允许冗余输出。跟多线程有关系。默认为False。
-
max_iter :最大迭代次数。-1为无限制。
-
decision_function_shape :是否返回模型中每一个类别的样本的ovr决策函数,或者ovo决策函数。 默认为None
-
random_state :数据洗牌时的种子值,int值
主要调节的参数有:C、kernel、degree、gamma、coef0。
6 推广
官方文档是学习的绝佳利器。传送门:http://scikit-learn.org/dev/modules/svm.html
6.1 多分类
SVM算法最初是为二值分类问题设计的,当处理多类问题时,就需要构造合适的多类分类器。目前,构造SVM多类分类器的方法主要有两类:一类是直接法,直接在目标函数上进行修改,将多个分类面的参数求解合并到一个最优化问题中,通过求解该最优化问题“一次性”实现多类分类。这种方法看似简单,但其计算复杂度比较高,实现起来比较困难,只适合用于小型问题中;另一类是间接法,主要是通过组合多个二分类器来实现多分类器的构造,常见的方法有one-against-one和one-against-all两种。
- 一对多法(one-versus-rest,简称1-v-r SVMs)。训练时依次把某个类别的样本归为一类,其他剩余的样本归为另一类,这样k个类别的样本就构造出了k个SVM。分类时将未知样本分类为具有最大分类函数值的那类。
- 一对一法(one-versus-one,简称1-v-1 SVMs)。其做法是在任意两类样本之间设计一个SVM,因此k个类别的样本就需要设计k(k-1)/2个SVM。当对一个未知样本进行分类时,最后得票最多的类别即为该未知样本的类别。Libsvm中的多类分类就是根据这个方法实现的。
- 层次支持向量机(H-SVMs)。层次分类法首先将所有类别分成两个子类,再将子类进一步划分成两个次级子类,如此循环,直到得到一个单独的类别为止。 对c和d两种方法的详细说明可以参考论文《支持向量机在多类分类问题中的推广》(计算机工程与应用。2004)
- 其他多类分类方法。除了以上几种方法外,还有有向无环图SVM(Directed Acyclic Graph SVMs,简称DAG-SVMs)和对类别进行二进制编码的纠错编码SVMs。
6.2 回归
支持分类的支持向量机可以推广到解决回归问题,这种方法称为支持向量回归。
支持向量分类所产生的模型仅仅依赖于训练数据的一个子集,因为构建模型的成本函数不关心在超出边界范围的点,类似的,通过支持向量回归产生的模型依赖于训练数据的一个子集,因为构建模型的函数忽略了靠近预测模型的数据集。
有三种不同的实现方式:支持向量回归SVR,nusvr和linearsvr。linearsvr提供了比SVR更快实施但只考虑线性核函数,而nusvr实现比SVR和linearsvr略有不同。
作为分类类别,训练函数将X,y作为向量,在这种情况下y是浮点数