svm支持向量机

支持向量机(SVMs)是一组用于分类回归异常值检测的监督学习方法。

SVMs的优点有:

  • 在高维空间下有效。
  • 在样本维度远远高于样本数量时依旧有效。
  • 在决策函数中使用训练样本的子集(被称为支持向量),同时其也是内存有效的。
  • 能够为不同的决策函数指定不同的核函数。(在sklearn中)已经提供了一个普通核,但是同样也支持将其替换成自定义的核。

SVMs的缺点有:

  • 若特征的数量远远大于样本数量,这个函数可能会给出一个糟糕的表现。
  • SVMs并不直接提供概率估计,而是通过使用(性能)昂贵的五层交叉验证(可以查看下方的Scores and probabilities

在scikit-learn中同时支持密集(可通过 numpy.asarray 转化为numpy.ndarray)和稀疏向量(任何scipy.sparse )作为SVMs的输入。但是,如果让SVMs对稀疏向量做预测,其必须拟合这类型的数据。为了获得最好的性能表现,使用 dtype=float64 numpy.ndarray (密集) 或 scipy.sparse.csr_matrix (稀疏)的C语言排序格式的数组。

1.4.1 分类

SVCNuSVCLinearSVC 在数据集中都是有能力处理多元分类问题的。


几种SVC的分类表现


SVCNuSVC 都是相似的方法,但是在应对略有差异(大致相同)的数据集是它们却会使用不同的数学公式来解决(详见 Mathematical formulation 一节)。在另一方面,LinearSVC 是一种使用线性核的支持向量分类的不同实现。要注意的是LinearSVC 并不接受 kernel 关键字,因为其默认(而且是固定)为 linear 。也因此对比于SVCNuSVC ,它也缺少了一些成员变量,例如 support_

作为其他的分类器,SVCNuSVCLinearSVC 接受两个训练样本的数组输入:一个是尺寸为 [n_samples, n_features] 的数据 X,另一个是尺寸为 [n_samples] ,作为分类标签(字符串或整数)的数组 y

>>> from sklearn import svm
>>> X = [[0, 0], [1, 1]]
>>> y = [0, 1]
>>> clf = svm.SVC()
>>> clf.fit(X, y)  
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape=None, degree=3, gamma='auto', kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)

在拟合过后,这个模型就可以用来预测新数据了:

>>> clf.predict([[2., 2.]])
array([1])

SVMs的决策函数取决于被称为支持向量的训练样本的子集。一些支持向量的数据属性能够在成员 support_vectors_support_ n_support中获得。

>>> # get support vectors
>>> clf.support_vectors_
array([[ 0.,  0.],
       [ 1.,  1.]])
>>> # get indices of support vectors
>>> clf.support_ 
array([0, 1]...)
>>> # get number of support vectors for each class
>>> clf.n_support_ 
array([1, 1]...)

1.4.1.1. 多元分类问题

SVCNuSVC 实现了对多元分类问题的实现了“one-vs-one”方法(Knerr等人于1990年提出)。如果 n_class 是分类的数量,那么则在遍历训练集的过程中,使用两个类的数据构造出的 n_class * (n_class - 1) / 2

为了提供与其他分类器的一致接口,decision_function_shape 设置允许将“one-vs-one”分类器的结果聚合到形状为 (n_samples,n_classes)的决策函数:

>>> X = [[0], [1], [2], [3]]
>>> Y = [0, 1, 2, 3]
>>> clf = svm.SVC(decision_function_shape='ovo')
>>> clf.fit(X, Y) 
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovo', degree=3, gamma='auto', kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 4 classes: 4*3/2 = 6
6
>>> clf.decision_function_shape = "ovr"
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 4 classes
4

在另一方面,LinearSVC 实现了"one-vs-the-rest"多元策略,从而来训练 n_class模型。如果只有两个分类,则训练出来的模型如下:

>>> lin_clf = svm.LinearSVC()
>>> lin_clf.fit(X, Y) 
LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss='squared_hinge', max_iter=1000,
     multi_class='ovr', penalty='l2', random_state=None, tol=0.0001,
     verbose=0)
>>> dec = lin_clf.decision_function([[1]])
>>> dec.shape[1]
4

查看数学公式以获得决策函数的完整说明。

不过要注意的是LinearSVC 同样实现了一个可选的多元分类策略,通过使用选项 multi_class='crammer_singer'来使用 Crammer and Singer 这个所谓的多类SVM的规定。这种方法是一致的这个函数与其他是一致的,但是对"one-vs-rest"分类却不是。在实践中,"one-vs-rest"分类通常是首选。因为其运行结果与其他相似,但是耗时却显著的少。

对于“one-vs-rest”的 LinearSVC,属性 coef_ intercept_ 分别具有不同的形状,[n_class, n_features] [n_class]。每一行的系数对应着n_class个许多"one-vs-rest"分类器的一个,并且截距与其类似,只是他的类只有"一个"。

在“one-vs-one”的SVC,则更多地涉及其属性的布局。在拥有线性核的情况下,该布局的 coef_ intercept_跟上述描述的 LinearSVC 的类似,除了其 coef_ 的形状是 [n_class * (n_class - 1) / 2, n_features]。对于其他类似的二元分类器,从0开始到n,即“0 vs 1”, “0 vs 2” , ... “0 vs n”, “1 vs 2”, “1 vs 3”, “1 vs n”, . . . “n-1 vs n”.

因为 dual_coef_的形状是[n_class - 1, n_SV],所以有些难以掌握要怎么布局。这些列相当于任何 n_class * (n_class - 1) / 2 一对一 分类器的支持向量。每一个支持向量都在 n_class - 1 分类器中被使用。n_class - 1实体的每一行代表着其分类器的双系数。

通过一个示例或许会更好的解释这一原理:
考虑一个三元分类问题,其"0"类拥有三个支持向量,υ0^0, υ0^1, υ0^2,然后"1"类则拥有两个支持向量 υ1^0, υ1^1,然后"2"类就是 υ2^0, υ2^1
对于每一个支持向量 υi^j 都有两个双系数。在此我们将分类 i kα(i,k)^j支持向量的系数称为 υi^j 。然后 dual_coef_ 看起来如下表所示:

系数A系数B说明
α(0, 1)^0α(0, 2)^0SVs中,"0"类的系数
α(0, 1)^1α(0, 2)^1
α(0, 1)^2α(0, 2)^2
α(1, 0)^0α(1, 2)^0SVs中,"1"类的系数
α(1, 0)^1α(1, 2)^1
α(2, 0)^0α(2, 1)^0SVs中,"2"类的系数
α(2, 0)^1α(2, 1)^1

1.4.1.2. 分数和概率

SVC 方式的 decision_function给出了每个样本的分数(或二元类中每个样本的单位分数)(0和1即可)。当构造器的参数 probability 被设置为 true,就会启用类关系的估计(使用 predict_probapredict_log_proba函数)。在二元分类,其概率通过 Platt 缩放进行计算:。对SVM's的分数的Logisitic回归是通过其训练集的额外交叉验证来拟合。在多元分类问题中,这由Wu等人在2004年中对其进行的延伸。

不必多说,在超大数据集中,使用带有 Platt 缩放 的交叉验证是一项"昂贵"的操作。另外,其概率估计可能会产生不一致的分数,在这种情况下分数的 argmax属性或许不是在该概率中的"最大值"。(例如在二元分类问题中,一个样本通过 predict 其类别来对其所属的类进行标签,根据其概率与 predict_proba对比来判断)。Platt 同样因为其理论问题而被人所知。如果需要置信分数,但是又不需要计算概率,那么建议是设置 probability=False并且使用 decision_function 来代替predict_proba

引用

1.4.1.3. 偏斜问题

在需要更重视某些类或某些单个样本的问题中,可以使用关键字class_weight和sample_weight。

SVC (但NuSVC 没有)在 fit 函数实现了 class_weight 关键字。其是一个格式为 {class_lable : value}的字典,其中 value 是一个大于0的浮点数,将类 class_label 的参数 C 设置为 C * value


偏斜类的分布

SVCNuSVCSVRNuSVROneClassSVM 在其的 fit 方法中通过实现关键字 sample_weight 为每一个独立样本提供了权重。类似于 class_weight,对每一个样本的参数 C 设置 C * sample_weight[i]


SVM的权重

示例

1.4.2. 回归

支持向量分类器的方法能够通过扩展以解决回归问题。该方法被称为支持向量回归。

这个模型产生一个支持向量分类器(跟上面章节描述的一样),其只根据训练集的子集,因为对于这个模型的代价函数,它会忽略任意分布在(预测结果)边缘的训练样本。同样地,这个模型产生一个只依赖训练集的子集的支持向量回归,因为建立模型的成本函数会忽略任何接近模型预测结果的训练数据。

这里有三种支持向量回归的实现:SVRNuSVR LinearSVRLinearSVR 提供了一个比SVR 处理更快的实现,但是他只考虑线性核,而 NuSVR 实现了一个跟SVRandLinearSVR 处理方式不同的方法。详情可以查看实现的细节

与分类的类一样,fit 方法只接受参数向量 X ,向量 y 。但是在这情况下,y 的值类型应该是浮点数而不是整数:

>>> from sklearn import svm
>>> X = [[0, 0], [2, 2]]
>>> y = [0.5, 2.5]
>>> clf = svm.SVR()
>>> clf.fit(X, y) 
SVR(C=1.0, cache_size=200, coef0=0.0, degree=3, epsilon=0.1, gamma='auto',
    kernel='rbf', max_iter=-1, shrinking=True, tol=0.001, verbose=False)
>>> clf.predict([[1, 1]])
array([ 1.5])

示例

1.4.3. 密度估计与新奇检测

单类SVM可以用来新奇检测,即,对给定一个样本的训练集检测出其集合的软边界,然后据此来判断新点是否属于此类。这种实现的类叫做 OneClassSVM

在这种情况下,因为它是一种无监督学习(根据无标签的训练数据来找出其类别),所以它的 fit 函数只接受一个参数,数组 X

可以在新奇检测和异常值检测 这一章看到更多关于此的使用细节。


新奇检测

示例

1.4.4. 复杂度

支持向量机是一个很强大的工具,但是它会随着训练集的规模增加时,它所需的计算能力和存储空间也会跟着增加。SVM的核心是一个二次规划问题(QP),其将支持向量与训练数据的剩余部分(子集之外的数据)分离。这个基于libsvm 的QP求解器在 O(n_features × n_samlpes ^ 2) O(n_features × n_samples ^ 3)之间进行缩放,然后他的缩放效率取决于如何在实例中使用 libsvm 的缓存(但也依赖数据集的规模)。如果数据集很稀疏,那么 n_features应该用样本向量中非零特征的平均数替换。

同样要注意的是在线性情况下,通过使用liblinear 实现的LinearSVC 的算法比使用基于libsvmSVC 中对应的算法更有效率,并且可以以线性的方式扩展样本与特征的数量到数百万个。

1.4.5. 实用技巧

  • 避免拷贝数据:对于SVCSVRNuSVCNuSVR 来说,如果传递给某些方法的数据不是双精度的连续的C语言格式的数组,那么在调用底层的方法之前,它会再拷贝一份(符合格式的)。所以你可以通过调用其 flags参数来确认它是不是符合格式的。
    LinearSVC(和LogisticRegression) 中任何作为numpy数组传递的输入都会经过复制并转化成 liblinear 中内部表示的稀疏数据(双精度浮点和以int32索引的非零分量)。如果你想要在不拷贝密集 numpy 的C-连续双精度浮点数数组的情况下拟合一个大规模线性分类器,那我们就建议使用SGDClassifier 来代替原来的。并且它的设置跟LinearSVC 模型是差不多的。

  • 核缓存大小:对SVCSVR,nuSVC
    NuSVR,核缓存的大小在处理大规模的问题上有很重要的作用。如果你有足够用的可用内存,那就尽可能的设置 cache_size的大小,并且设置成比默认值(200MB)要大,例如512(MB)或1024(MB)。

  • C设置:是所有SVM核都有的属性,即训练样本中的错误分类与决策面复杂度的比例。C 参数的默认值是 1,并且通常情况下最好不要更改。如果你的数据有很多噪音,那就应该降低它的值。它对应着正则化在估量过程中的比例。

  • 支持向量机算法是非尺度不变性的,所以高度建议对数据进行缩放。例如,对输入向量X的每一个参数缩放为[0, 1] 或 [-1, +1] 或将其标准化为具有均值0和方差1的值域。当然对测试向量也要进行相对应的缩放。预处理数据 一章有更多关于缩放和正规化的细节。

  • NuSVC/OneClassSVM/NuSVR 里的参数 nu 为近似训练误差和支持向量的分数。

  • SVC 里,如果分类的数据是偏斜的(即很多正结果和很少负结果),那就需要设置 class_weight='balanced',而且可以对参数 C 设置(或只设置)不同的惩罚项。

  • LinearSVC 的底层是在拟合模型的过程中使用了随机数生成器去选择特征。所以对相同的输入通常会产生(稍微)不同的结果。如果出现的偏差较大,可以尝试一下减少参数 tol的值。

  • 可以通过 LinearSVC(loss='12', penalty='l1', dual='False') 使用自带的L1惩罚项来产生稀疏解。即,产生不等于零的特征权重的子集并且对决策函数有贡献的解。增加参数 C会产生更复杂的模型(导致更多的特征被选中)。对于"零"模型(即所以权重都为零)的 C 值可以通过 l1_min_c 来计算。

1.4.6. 核函数

有下列核函数可供选择:

  • linear : < x, x' >
  • polynomial:(γ <x, x'> + r)^ d),其中 d 可以通过关键字 degree来指定,而 r 则是 coef0
  • rbf: exp(-γ | x - x' |^ 2) ,其中 γ 可以使用关键字 gamma来指定,但是其值必须大于0。
  • sidmoid:(tanh(γ <x, x'> + r)),其中 r可以使用关键字 coef0来指定。

可以在初始化的时候通过关键字 kernel 来使用不同的核:

>>> linear_svc = svm.SVC(kernel='linear')
>>> linear_svc.kernel
'linear'
>>> rbf_svc = svm.SVC(kernel='rbf')
>>> rbf_svc.kernel
'rbf'

1.4.6.1. 自定义核

你可以通过给出一个python函数或预计算Gram矩阵的方式来定义一个自定义的核。

使用自定义核的分类器的行为必须跟其他分类器有相同的行为,除了:

  • 字段 support_vectors_ 是置空的,除了支持向量的索引存储在 support_ 时才不为空。
  • fit() 方法的第一个参数 X 的引用会被储存起来。如果 X fit() 之后, predict() 之前内容就进行了更改,那么你会得到一个"意想不到"的结果。

1.4.6.1.1 使用Python函数作为核

你同样可以在构造器的关键字 kernel 中传入一个自定义的函数,将其作为自定义的核函数。

你的核必须接受两个矩阵参数,分布是 (n_samples_1, n_features) (n_samples_2, n_features),然后返回一个形状为 (n_samples_1, n_samples_2) 的核矩阵。

下列代码定义了一个线性核,并且创建了一个使用这个线性核的分类器的实例:

>>> import numpy as np
>>> from sklearn import svm
>>> def my_kernel(X, Y):
...     return np.dot(X, Y.T)
...
>>> clf = svm.SVC(kernel=my_kernel)

示例

1.4.6.1.2. 使用Gram矩阵

设置 kernel='precomputed' ,并且使用 X 的Gram矩阵代替 X传进 fit 方法里。

>>> import numpy as np
>>> from sklearn import svm
>>> X = np.array([[0, 0], [1, 1]])
>>> y = [0, 1]
>>> clf = svm.SVC(kernel='precomputed')
>>> # linear kernel computation
>>> gram = np.dot(X, X.T)
>>> clf.fit(gram, y) 
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape=None, degree=3, gamma='auto',
    kernel='precomputed', max_iter=-1, probability=False,
    random_state=None, shrinking=True, tol=0.001, verbose=False)
>>> # predict on training examples
>>> clf.predict(gram)
array([0, 1])

1.4.6.1.3. RBF核的参数

当SVM使用径向基函数(RBF)来训练时,需要考虑两个参数:C gamma C是所有SVM核都有的属性,即训练样本中的错误分类与决策面复杂度的比例。一个较低的 C 值会使得决策面变得平滑(相比错误分类更倾向于决策面),而 C值较高时则目标则是分类出所有正确的样本(因为更倾向于找出错误的分类)。 gamma 定义着单个训练样本的影响度。越高的 gamma值越代表着会使得该样本影响到其附近的样本。

C gamma 的值是决定SVM表现的关键参数。一个建议是使用携带 C gamma sklearn.model_selection.GridSearchCV,并且对其设置值域时,尽量使间隔变得大点以便于选出较为合适的值。

示例

1.4.7. 数学公式

支持向量机能够在高维或无限维空间里构造出超平面或超平面集,所以可以利用此特性将其用在分类,回归或其他任务上。直观来说,一个通过超平面实现的好分界线应该具有到任何类的训练样本点都是最大的间隔距离(即所谓的功能裕度,其实就是下图虚线到实现的"距离")。因为一般来说如果边缘间隔越大,分类器的泛化的误差就越低。


SVM的裕度

1.4.7.1. SVC

对给定的训练向量 xi ∈ R^p, i=1,...,n ,在两类问题中,向量 y ∈ {1, -1}^n,SVC解决以下主要问题:


SVC公式_A

其对偶于


SVC公式_B

其中 e 是值全为一的向量, C > 0 是上限, Q 是一个 n x n 的半正定矩阵, Qij = yi · yj ·K(xi, xj),其中 K(xi, xj) = ∅(xi)^T · ∅(xj)是核函数。其中训练向量通过函数被隐式映射到高维(可能是无限维)空间中。

决策函数为:


SVC的决策函数

注意
虽然从libsvmliblinear派生出的SVM模型是使用 C作为正则化参数的,但是更多的估量器是使用 alpha 作为正则化参数,而这两者之间的的关系是 C = n_samples / alpha

成员 dual_coef_ 保存着 yi·ai support_vectors保存着支持向量,然后 intercept_ 保存着独立项 ρ

引用

1.4.7.2. NuSVC

接下来会介绍一个新的变量 ν ,其控制着支持向量和训练错误的数量。这个参数(ν ∈ (0, 1])是训练误差分数的上限,同时也是支持向量分数的下限。

所以ν-SVC 公式等价于重新换参后的 C-SVC

1.4.7.3. SVR

对给定的训练向量 xi ∈ R^p, i=1,...,n 和向量 y ∈ R^n ε-SVR 求解器会使用以下公式:


SVR公式_A

其对偶于:


SVR公式_B

其中 e 是个值全为一的向量。 C > 0 是上限。Q 是一个 n x n 的半正定矩阵。Qij ≡ K(xi, xj) = ∅(xi)^T · ∅(xj)是其核函数。其中训练向量通过函数被隐式映射到高维(可能是无限维)空间中。

决策函数为:


SVR的决策函数

成员 dual_coef_ 保存各自不同的 ai - ai^* support_vectors保存着支持向量,然后 intercept_ 保存着独立项 ρ

引用

1.4.8. 实现细节

在这些SVM内部都是使用libsvmandliblinear 这些库来计算的。这些库使用了 C 和 Cython 包装。

引用
要了解使用的算法的实现细节的话,可以看下列链接:



作者:HabileBadger
链接:http://www.jianshu.com/p/d7c3529f0594
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值