本文翻译自:
http://scikit-learn.org/stable/modules/svm.html#
1.4 SVM
支持向量机SVM是一系列监督学习算法,包括分类,回归,异常点检测。
SVM优点:
1. 在高维空间有效;
2. 即使维数高于样本数,算法仍然高效;
3. 决策函数由支持向量机(子数据集)决定,可在内存中有效;
4. 不同核函数产生不同的决策函数。常用的核函数和自定义的核函数都可以使用。
SVM缺点:
1. 如果特征数量远高于样本数量,这个方法可能表现很差。
2. 不直接提供概率估计,可以使用代价高昂的五折交叉验证方法计算概率。
scikit-learn中实现的SVM可以支持稠密矩阵(numpy.ndarray或者可转换的numpy.asarray)和系数矩阵(任何scipy.sparse)样本向量作为输入。但是,要想预测稀疏数据,就必须在系数矩阵上训练模型。例如,为了获得最佳性能,请使用C-ordered numpy.ndarray(dense)或scipy.sparse.csr_matrix(sparse)时,设置dtype = float64。
1.4.1 分类
SVC, NuSVC and LinearSVC 是能能够在数据集上执行多分类的类。
SVC和NuSVM是相似的方法,只是在参数集合和数学公式有细微的不同。另一方面,LinearSVC是SVC基于线性核函数的另一个实现。请注意,LinearSVC不接受关键字 kernel ,因为它已经被被假定为线性的。这个方法没有 SVC and NuSVC中实现的一些属性,例如 support_。
(其实现也不是基于LIBSVM,所以它具有更大的灵活性在选择处罚和损失函数时,而且可以适应更大的数据集,他支持密集和稀疏的输入是通过一对一的方式解决的。摘自:
http://www.cnblogs.com/zhbzz2007/p/5974485.html)
SVC, NuSVC and LinearSVC这三种分类器都是以数组X([n_samples, n_features])训练样本和数组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])
SVM的决策函数依赖于训练子集,称为支持向量。一些关于支持向量的属性如下:
>>> # 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 多分类的分类器
SVC和NuSVC实施了“一对一”(“one-against-one” )的方法(Knerr等,1990)进行多类分类。如果n_class是类的数量,则构造 n_class*(n_class-1)/ 2 个分类器,并且每个分类器都从两个类中训练数据得到的。为了提供与其他分类器的一致的接口,decision_function_shape选项允许将“一对一”分类器的结果聚合到形如(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]])
>>> ptint dec
[[-0.63212056 0. 0.3495638 0.63212056 0.98168436 0.3495638 ]]
>>> 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”(一对多)多分类策略,训练得到多类模型。如果有两个类,只需要训练一个模型:
>>> 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还实现了一种替代的多类策略,即所谓的由Crammer和Singer制定的多类SVM,通过使用选项multi_class =’crammer_singer’。实现方法是一致的,相对于one-vs-rest 分类是不准确的。事实上,one-vs-rest 分类还是更常用,因为结果差不多,但是训练时间明显减少。
对于“one-vs-rest” LinearSVC 的属性coef_ 和intercept_ ,它们分别是这样的形式,[n_class, n_features] 和 [n_class]。
系数的每一行对应于n_class个“one-vs-rest”分类器中的一个,并且类似于截距,按照“one”类的顺序。
在“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 个“one-vs-one”分类器中。在n_class - 1 个分类器中每个支持向量机都用到了。每行的n_class - 1多对应着这些分类器中的对偶系数。
通过例子描述可能更加清楚:
考虑一个三分类问题,类0有三个支持向量机,类1和类2都分别有两个支持向量机:
。对于每个支持向量机
都有两个对偶系数。我们把支持向量机在类i之间的系数称为
dual_coef_ 就像图片中这样:
1.4.1.2 得分和概率
SVC方法的decision_function函数提供每个样本的每个类的分数。(或者只是二进制例子中的每个样本的单个分数)。当构造函数的选项probability设置为True,类成员概率估计才可用。(使用predict_proba 和 predict_log_proba方法)。在二分类情况下,使用Platt缩放来校正概率:对SVM评分的逻辑回归,训练模型时要对训练数据进行额外的交叉验证。在多分类情况下,根据per Wu 等人得到了延伸。
在中Platt缩放方法中引入交叉验证,对于大数据集来说是很昂贵的代价。除此之外,概率估计可能和得分不一致,也就是说,得分的“argmax”不一定等于概率的“argmax”。(例如,在二分类中,可以通过预测将样本标记为属于具有predict_proba的概率<1/2的类)。Platt方法(普氏方法)也被称为理论问题。如果要是得分可信,不一定需要概率值,可以设置probability=False 并且使用decision_function 代替predict_proba方法。
引用:
Wu, Lin and Weng,
“Probability estimates for multi-class classification by pairwise coupling” ,JMLR 5:975-1005, 2004.
1.4.1.3. 不平衡问题
当我们需要对某一类或者某些不样本给予更多的重视时,可以使用关键字class_weight 和sample_weight。
SVC(不包括NuSVC)在fit函数中实现了关键字class_weight。它的形式是一个字典{class_label : value},value是一个大于0的浮点数字,设置类class_label的参数C为C * value。
SVC, NuSVC, SVR, NuSVR 和OneClassSVM类都在函数fit中通过关键字sample_weight 实现某个样本的权重。和class_weight是相似的,设置第 i 个样本的参数C为C * sample_weight[i]。
1.4.2. 回归
支持向量分类方法也可以用于解决回归问题,称为SVR。( Support Vector Regression支持向量回归)
产生于SVC的模型,只依赖于少量训练数据,因为损失函数不关心在决策边界之外的数据点。类似的,产生于SVR的模型也是只依赖于少量训练数据,因为损失函数忽视那些接近于模型预测的其他训练数据。
SVR有几种实现:SVR, NuSVR and LinearSVR.。LinearSVR训练模型比SVR更快,但是只提供线性核函数,而NuSVR 相比于 SVR 和LinearSVR的公式有点不同。
和分类问题相比,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是一个强大的工具,但是他的计算和存储会随着训练数据的增加而快速增加。SVM的核心是二次规划问题(QP),将支持向量从其他训练数据中分离出来。QP问题的求解基于libsvm的实现,时间复杂度介于
和
之间,主要取决于libsvm缓存在实践中的有效性(和数据集有关)。如果数据是稀疏的,那么
可以用样本向量中非零特征数量代替。
还要注意,对于线性情况,线性实现中基于 liblinear 的LinearSVC中的算法比其基于libsvm的SVC对应方法更有效,并且可以几乎线性地缩放到数百万个样本或特征。
1.4.5. 使用技巧
- 避免数据复制
对于SVC, SVR, NuSVC 和 NuSVR,如果传递给某些方法的数据不是C-ordered连续的且是双精度的,它将在调用底层C实现之前被复制。您可以通过检查其flags属性来检查给定的numpy数组是否为C连续的。
对于 LinearSVC (和LogisticRegression)来说,作为numpy数组传递的任何输入将被复制并转换为liblinear内部稀疏数据表示(双精度浮点数和非零分量的int32索引)。如果您想训练大规模的线性分类器,而不需要复制密集的C连续的双精度numpy数组作为输入,我们建议使用SGDClassifier 类代替。目标函数可以配置为与LinearSVC模型的几乎相同。 内核缓存大小
对于 SVC, SVR, nuSVC 和NuSVR,内核缓存区的大小对运行时间有很大的影响。如果你有足够的RAM,可以设置cache_size大于默认的200MB,比如500MB或1000MB。设置C参数
C默认是1,并且这是一个合理的默认选择。如果你有很多杂乱的观察结果,你应该减少它。 它是正则化而不是估计。支持向量机算法不是尺度不变量,因此强烈建议您扩展数据。
例如,扩展每个输入向量X为[0,1] 或者 [-1,+1],或者标准化到平均值为0,方差为1。请注意,必须将相同的缩放应用于测试向量才能获得有意义的结果。 有关缩放和归一化的更多详细信息,请参见预处理数据部分。- NuSVC / OneClassSVM / NuSVR中的参数nu近似于训练误差和支持向量的分数。
- 在SVC中,如果用于分类的数据不均匀(例如,正例多,负例少),可以设置class_weight=’balanced’ 或者尝试不同惩罚参数C。
- 基本的LinearSVC实现使用随机数生成器来在拟合模型时选择特征。因此对于相同的输入数据产生稍微不同的结果也是常见的。如果是这样,可以使用一个更小的tol(容忍度)参数。
- 使用LinearSVC(loss=’l2’, penalty=’l1’, dual=False) 提供的L1 惩罚产生一个稀疏解。只有特征权重的一个子集不同于零,并有助于决策函数。增加C产生一个更复杂的模型(更多特征被选择)。产生“零”模型(所有权重等于零)的C值可以使用l1_min_c计算。
1.4.6. 核函数
核函数可以是一下几种形式:
数学公式如上图,具体解释(省略公式):
- 线性核:**。
- 多项式核:**。d是关键字degree,r是关键字 coef0。(coefficient系数)。
- rbf径向基核:**。
是关键字 gamma,必须大于0。
- sigmoid核:**。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函数或者预先计算Grammar矩阵。
使用自定义核函数的分类器和其他分类器表现一样,除了:
- 字段 support_vectors_ 是空的,只有支持向量指数保存在 support_ 。
- fit()方法中的第一个参数的引用(而不是副本)存储以供将来参考。如果数组(前面说的输入向量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.使用Grammar矩阵
在 fit 方法中设置 kernel=’precomputed’ 并且用Grammar矩阵代替X向量。此时,必须提供所有训练向量和测试向量之间的内核值。
>>> 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核函数的参数
当训练基于RBF径向基核函数的SVM时,必须考虑这两个参数: C 和 gamma 。参数 C 对于所有的SVM 核函数都是相同的,用于权衡决策表面的简单性和训练样本误分类情况。C 较低的话使得决策面平滑,而 C 较高是为了正确分类所有的训练样本。 gamma 定义了单个训练样本的影响程度。gamma 越大,则其他距离决策面越近的样本会受到影响。
合适的 C 和 gamma 的选择是非常重要的。建议使用sklearn.model_selection.GridSearchCV (网格选择法),其中C和gamma指数间隔相差很远,以选择好的值。
示例:
RBF SVM 参数选择示例
1.4.7. 数学公式
支持向量机在高或无限维空间中构建超平面或一组超平面,可用于分类,回归或其他任务。 直观地,通过与任何类别的最接近的训练数据点具有最大距离(所谓的函数间隔)的超平面来实现良好的分离,因为通常间隔越大,分类器的泛化误差越低。
1.4.7.1. SVC
给定两类问题中的向量 和一个向量
。SVC 求解如下最优化问题:
对偶形式如下:
其中,e 表示所有向量,C > 0 是上界,Q 是一个 n * n 的正定矩阵。 ,其中核函数表示为
。训练姓梁通过函数
被隐式映射到高维 (或无限维)空间中。
决策函数是:
注意: libsvm 和 liblinear 得到的SVM 模型使用 C 作为正则化参数,大多数其他估计使用 参数 alpha 。二者的关系是:
参数可以通过成员来访问,dual_coef_ 表示点积 。support_vectors_ 表示支持向量。intercept_ 表示独立因子
。
参考:
- “Automatic Capacity Tuning of Very Large VC-dimension Classifiers”, I. Guyon, B. Boser, V. Vapnik - Advances in neural information processing 1993.
- “Support-vector networks”, C. Cortes, V. Vapnik - Machine Learning, 20, 273-297 (1995).
1.4.7.2. NuSVC
我们引入一个新的参数 来控制支持向量的数量和训练误差。这个参数范围是 ( 0, 1 ] ,是训练误差分数的上限和支持向量分数的下限。
可以看出 - SVC (NuSVC)是C- SVC 的重新参数化,因此数学上是相等的。
1.4.7.3. SVR
给定训练向量 和向量
。
-SVR 求解如下最优化问题:
对偶形式是:
参数解释同1.4.7.1. SVC。
决策函数是:
其中,dual_coef_ 表示差值 ,其他表示同1.4.7.1. SVC。
参考:
- “A Tutorial on Support Vector Regression”, Alex J. Smola, Bernhard Schölkopf - Statistics and Computing archive Volume 14 Issue 3, August 2004, p. 199-222.
实现细节
在内部,我们使用libsvm和liblinear来处理所有的计算。 这些库使用C和Cython封装。
参考:
有关使用的算法的实现和细节的描述,请参考:
- LIBSVM: A Library for Support Vector Machines.
- LIBLINEAR – A Library for Large Linear Classification.
以上只是我的翻译,可能有些理解有错误。具体某个算法的实现细节可以参考原文链接,继续学习。