支持向量机

学习地址

Support Vector Machine(SVM)


优化目标

回顾逻辑回归的激活函数sigmoid
2019-09-03 09-50-57 的屏幕截图.png
逻辑回归单个样本的代价函数如下,分别考虑当y=0和y=1时代价函数的变化情况 在这个基础上稍微变化一下,取z=θTx的某一点,作两条直线如图,这个新的代价函数为cost_1(z)和cost_0(z),这里下标0和1只是代表y=0或y=1。
有了这些定义就可构建支持向量机
2019-09-03 09-59-39 的屏幕截图.png

那么,用cost_1(z)和cost_0(z)分别代替-log(hθ(x(i)))和(-log(1-hθ(x(i))))得到一个变体代价函数,对于1/m这只是个常数,完全不影响最优解,故可以省略。
将逻辑回归代价函数替换成A,正则化项替换成B,那么J(θ)=A+λB。λ代表正则化项所占的权重,即λ越大说明正则化项越重要。对于SVM,同样我们使用C来表示这个常数,我们需要优化的是CA+B,如果C很小那么C*A也很小,同样可以表示正则化项很重要,实际上C=1/λ时和A+λB效果是一样的。
SVM中A则是使用cost替换了激活函数hθ(z)之后的A。那么就得到SVM需要优化的代价函数如下:
2019-09-03 10-28-31 的屏幕截图.png

SVM并不会像逻辑回归输出概率,而是通过优化代价函数得到参数θ然后直接预测y=0或者y=1。
2019-09-03 10-46-24 的屏幕截图.png

直观上对大间隔的理解

SVM有时也被成为大间距分类器。
实际上当θTX≥0就可以正确把正负样本分开,但SVM并不仅仅将样本分开,而是考虑一个安全距离。下图已经给出关于z的代价函数。

2019-09-03 11-12-46 的屏幕截图.png
这个间距是指不同的分类效果离样本的距离,比如下图中黑色分隔线就是比较理想的一种情况。SVM会尽量把正负样本最大间距地隔开。
2019-09-03 10-58-19 的屏幕截图.png

大间隔分类器的数学原理

SVM是如何使得这个间距最大呢?
先看一个内积的例子:设u=[[u1,u2]],v=[[v1,v2]]
那么向量u(两个坐标轴上投影长度分别为u1、u2)的长度||u||=√(u1*u1+u2*u2),这个长度就是u的范数
u和v的内积uTv如何计算?
定义p:v在u上的直角投影
那么uTv=p*||u||=u1v1+u2v2
2019-09-03 11-58-55 的屏幕截图.png

引入到SVM中,那么θTx(i)=p(i)||θ||=θ1x1(i)2x2(i),p(i)是x(i)在向量θ上的投影
2019-09-03 18-44-28 的屏幕截图.png

为什么SVM会选择间距最大的分类方式?
如下,如果想分出正负样本,选择左边的分类方式发现投影p(i)非常小,这就使得&theta的范数必须非常大,而右边的分类方式则不存在这种问题。故决策边界离样本尽可能远。
2019-09-03 18-55-54 的屏幕截图.png

核函数

用f_1代替特征x_1,f_2代替特征x_2,f_i代替特征x_jx_k。这样f的阶数可能是非常大的,如何更好地选择特征f?

选择三个基点l(1),l(2),l(3)
对于给定x:f_1=similarity(x,l(1))=exp(-||x-l(1)||2 / (2σ2))
f_2…同理。

这里相似度函数在数学上就是一个核函数,核函数有很多,这里使用的是高斯核函数,故也记作k(x,l(i))
2019-09-04 10-26-28 的屏幕截图.png
2019-09-04 10-31-33 的屏幕截图.png

如何选取基点l(i)呢?
实际上,基准点和训练样本点是一致的,即l(i)=x(i)
2019-09-04 11-21-59 的屏幕截图.png

优化目标整理:
2019-09-04 11-33-56 的屏幕截图.png

如何选择参数C(=1/λ)和σ:
2019-09-04 11-39-18 的屏幕截图.png

使用SVM

有很多精准的计算库或者软件包进行求参优化问题,例如liblinear,libsvm等来求解参数θ,手动写的优化方法很难和专业库进行比优。

但有几件事还是需要自己来具体实现

  • 参数C的选择
  • 核函数(kernel)的选择(相似度函数similarity function):例如,不带参数的核函数(线性核函数),预测y=1如果θTx≥0;还可以选择带参数σ的高斯核函数

2019-09-04 15-58-00 的屏幕截图.png

默塞尔定理,SVM数值计算包默认满足默塞尔定理以便优化方法正确计算
其他的核函数选择:

  • 多项式核函数:k(x,l)=(xTl+constant)2/3/4,constant=1/5/….
  • 字符串核函数
  • 卡方核函数
  • 直方相交核函数

多分类器:
2019-09-04 16-29-23 的屏幕截图.png

选择逻辑回归还是SVM?

  • 特征数量n很大(n=10000),n≥m:选择逻辑回归或线形核函数
  • 如果n很小(1-1000),m适中:选择高斯核函数
  • 如果n很小,m很大:增加特征,使用逻辑回归或线形核函数

神经网络可能都适用,但训练可能要更慢些。


华丽分割

以下为补充,更新于2019.12.25


sklearn中的支持向量机SVM

超平面

在几何中,超平面是一个空间的子空间,它是维度比所在空间小一维的空间。 如果数据空间本身是三维的, 则其超平面是二维平面,而如果数据空间本身是二维的,则其超平面是一维的直线。

在二分类问题中,如果一个超平面能够将数据划分为两个集合,其中每个集合中包含单独的一个类别,则称这个超平面是数据的决策边界

样本点到决策边界的距离叫做边际(margin),通常记作d,SVM是使得d越大越好,这样泛化误差就会小了。拥有更大边际的决策边界在分类中的泛化误差更小。支持向量机,就是通过找出边际最大的决策边界,来对数据进行分类的分类器

sklearn中的支持向量机

含义
svm.LinearSVC 线性支持向量分类
svm.LinearSVR 线性支向量回归
svm.SVC 非线性多维支持向量分类
svm.SVR 非线性多维支持向量回归
svm.NuSVC Nu支持向量分类
svm.NuSVR Nu支持向量回归
svm.OneClassSVM 无监督异常值检测
直接使用libsvm的函数 含义
svm.libsvm.cross_validation() SVM专用的交叉验证
svm.libsvm.decision_function() SVM专用的预测边际函数(libsvm名称为predict_values)
svm.libsvm.fit() 使用libsvm训练模型
svm.libsvm.predict() 给定模型预测X的目标值
svm.libsvm.predict_proba() 预测概率

注意:

  • 除了特别表明是线性的两个类LinearSVC和LinearSVR之外,其他的所有类都是同时支持线性和非线性的。
  • NuSVC和NuSVC可以手动调节支持向量的数目,其他参数都与最常用的SVC和SVR一致。注意OneClassSVM是无监督的类。

sklearn.svm.SVC

  • class sklearn.svm.SVC (C=1.0, kernel=’rbf’, degree=3, gamma=’auto_deprecated’, coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape=’ovr’, random_state=None)

参数解释:

  • kernel为核函数,可选[‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’ ], kernel=’rbf’时(default),为高斯核
  • 错误项的惩罚系数。C越大,即对分错样本的惩罚程度越大C越大分类效果越好,但可能过拟合泛化误差大(default C=1.0)
  • decision_function_shape为分类器,可选[‘ovo’,’ovr’]分别表示一对一分类器,一对多分类。默认为’ovr’,’one vs rest’。

实践与调参

数据:

  • 数据集选用乳腺癌数据集。

kernel的选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from time import time

data=load_breast_cancer()
X=data.data
y=data.target
# print(X.shape,y.shape) # (569, 30) (569,)
# print(np.unique(y)) # [0 1]

Xtrain,Xtest,Ytrain,Ytest=train_test_split(X,y,test_size=0.3,random_state=10)
kernel=['linear', 'rbf', 'sigmoid'] # poly,这核函数特别耗时间,所以不做演示

for kernel in kernel:
time0=time()
clf=SVC(kernel=kernel
,C=1.0
,gamma='auto'
# ,degree=1
,cache_size=5000 # 允许使用的内存MB
).fit(Xtrain,Ytrain)
print('The accuracy under kernel %s is %.2f' %(kernel,clf.score(Xtest,Ytest)))
print(time()-time0)

'''
The accuracy under kernel linear is 0.96
1.7291104793548584
The accuracy under kernel rbf is 0.65
0.04491472244262695
The accuracy under kernel sigmoid is 0.65
0.004982948303222656
'''

注意

  • degree=参数是多项式核ploy特有的,如果设置为1就表示线性核,这样上述代码的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    The accuracy under kernel linear is 0.96
    1.6605808734893799
    The accuracy under kernel poly is 0.95
    0.0538640022277832
    The accuracy under kernel rbf is 0.65
    0.043853759765625
    The accuracy under kernel sigmoid is 0.65
    0.0060122013092041016
  • 可以看出,线性核的效果特别好,据此就可以说明所使用的数据就是线性的

  • 但是存在一个问题是正确确实应该选择线性核,但是它的运行时间确是个缺陷,这个时候就要从数据预处理的角度出发了,实际上做了标准化后运行时间和其他核函数的结果都会很出色

其他参数的选取问题

确定单个参数:

  • 学习曲线

协同参数:比如多项式核函数ploy有三个可调参数gamma、degree、coef0

  • 网格搜索
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    from sklearn.svm import SVC
    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import train_test_split
    from time import time

    data=load_breast_cancer()
    X=data.data
    y=data.target

    from sklearn.model_selection import StratifiedShuffleSplit # 专门用来设置交叉验证模式
    from sklearn.model_selection import GridSearchCV # 带交叉验证的网格搜索

    time0=time()
    gamma_range=np.logspace(-5,1,10) # Return numbers spaced evenly on a log scale.
    coef0_range=np.linspace(0,5,10)

    param_grid=dict(gamma=gamma_range,coef0=coef0_range)

    cv=StratifiedShuffleSplit(n_splits=5,test_size=0.3,random_state=10) # 数据集分n份,每一份数据轮流做测试集,即做5次交叉验证
    grid=GridSearchCV(SVC(kernel='poly',degree=1,cache_size=5000)
    ,param_grid=param_grid
    ,cv=cv)

    grid=grid.fit(X,y)
    print('The best parameters are %s with a score of %0.5f ' %(grid.best_params_,grid.best_score_))
    print(time()-time0)

    # 这段代码运行很慢,不过最终会返回最佳参数对与最高分数