支持向量机(Support Vector Machine, SVM)是一类按监督学习(supervised learning)方式对数据进行二元分类(binary classification)的广义线性分类器(generalized linear classifier),其决策边界是对学习样本求解的最大边距超平面(maximum-margin hyperplane)。
SVM使用铰链损失函数(hinge loss)计算经验风险(empirical risk)并在求解系统中加入了正则化项以优化结构风险(structural risk),是一个具有稀疏性和稳健性的分类器。SVM可以通过核方法(kernel method)进行非线性分类,是常见的核学习(kernel learning)方法之一 。
SVM被提出于1964年,在二十世纪90年代后得到快速发展并衍生出一系列改进和扩展算法,在人像识别(face recognition)、文本分类(text categorization)等模式识别(pattern recognition)问题中有得到应用。
如下节核函数的介绍。
gamma值的大小,对SVM线性核函数没有影响
C参数,会在光滑的决策边界以及尽可能正确分类所有训练点两者之间进行平衡。
C值越大, 会得到更多正确的训练点,分隔平面的决策边界更不平滑;容易造成过拟合
C值越小, 会得到更少正确的训练点,分隔面的决策边界更平滑
在分类问题中给定输入数据和学习目标: ,其中输入数据的每个样本都包含多个特征并由此构成特征空间(feature space): ,而学习目标为二元变量 表示负类(negative class)和正类(positive class)。
若输入数据所在的特征空间存在作为决策边界(decision boundary)的超平面将学习目标按正类和负类分开,并使任意样本的点到平面距离大于等于1 [2] :
则称该分类问题具有线性可分性,参数 分别为超平面的法向量和截距。
满足该条件的决策边界实际上构造了2个平行的超平面作为间隔边界以判别样本的分类:
所有在上间隔边界上方的样本属于正类,在下间隔边界下方的样本属于负类。两个间隔边界的距离 被定义为边距(margin),位于间隔边界上的正类和负类样本为支持向量(support vector)。
0-1损失函数和其代理损失,红实线为0-1损失,黑实线为铰链损失。
0-1损失函数和其代理损失,红实线为0-1损失,黑实线为铰链损失。 [2]
在一个分类问题不具有线性可分性时,使用超平面作为决策边界会带来分类损失,即部分支持向量不再位于间隔边界上,而是进入了间隔边界内部,或落入决策边界的错误一侧。损失函数可以对分类损失进行量化,其按数学意义可以得到的形式是0-1损失函数:
0-1损失函数不是连续函数,不利于优化问题的求解,因此通常的选择是构造代理损失(surrogate loss)。可用的选择包括铰链损失函数(hinge loss)、logistic损失函数(logistic loss)、和指数损失函数(exponential loss),其中SVM使用的是铰链损失函数 [2] :
对替代损失的相合性研究表明,当代理损失是连续凸函数,并在任意取值下是0-1损失函数的上界,则求解代理损失最小化所得结果也是0-1损失最小化的解 [2] [15] 。铰链损失函数满足上述条件。
参见:统计学习理论
按统计学习理论,分类器在经过学习并应用于新数据时会产生风险,风险的类型可分为经验风险和结构风险 :
式中 表示分类器,经验风险由损失函数定义,描述了分类器所给出的分类结果的准确程度;结构风险由分类器参数矩阵的范数定义,描述了分类器自身的复杂程度以及稳定程度,复杂的分类器容易产生过拟合,因此是不稳定的。若一个分类器通过最小化经验风险和结构风险的线性组合以确定其模型参数:
则对该分类器的求解是一个正则化问题,常数 是正则化系数。当 时,该式被称为L2正则化或Tikhonov正则化(Tikhonov regularization) [16] 。SVM的结构风险按 表示,在线性可分问题下,硬边界SVM的经验风险可以归0,因此其是一个完全最小化结构风险的分类器;在线性不可分问题中,软边界SVM的经验风险不可归0,因此其是一个L2正则化分类器,最小化结构风险和经验风险的线性组合。
一些线性不可分的问题可能是非线性可分的,即特征空间存在超曲面(hypersurface)将正类和负类分开。使用非线性函数可以将非线性可分问题从原始的特征空间映射至更高维的希尔伯特空间(Hilbert space) ,从而转化为线性可分问题,此时作为决策边界的超平面表示如下 [2] [3] :
式中 为映射函数。由于映射函数具有复杂的形式,难以计算其内积,因此可使用核方法(kernel method),即定义映射函数的内积为核函数(kernel function): 以回避内积的显式计算 [2] [3] 。
Mercer定理(Mercer’s theorem)
核函数的选择需要一定条件,函数 是核函数的充要条件是,对输入空间的任意向量: ,其核矩阵(kernel matrix),即如下形式的格拉姆矩阵(Gram matrix):
是半正定矩阵。上述结论被称为Mercer定理 [3] [1] 。定理的证明从略,结论性地,作为充分条件:特征空间内两个函数的内积是一个二元函数,在其核矩阵为半正定矩阵时,该二元函数具有可再生性: ,因此其内积空间是一个赋范向量空间(normed vector space),可以完备化得到希尔伯特空间 ,即再生核希尔伯特空间(Reproducing Kernel Hilbert Space, RKHS)。作为必要条件,对核函数构造核矩阵后易知: [3] 。
在构造核函数后,验证其对输入空间内的任意格拉姆矩阵为半正定矩阵是困难的,因此通常的选择是使用现成的核函数 [3] 。以下给出一些核函数的例子,其中未做说明的参数均是该核函数的超参数(hyper-parameter) [2] :
SVM中的常见核函数
当多项式核的阶为1时,其被称为线性核,对应的非线性分类器退化为线性分类器。RBF核也被称为高斯核(Gaussian kernel),其对应的映射函数将样本空间映射至无限维空间。核函数的线性组合和笛卡尔积也是核函数,此外对特征空间内的函数 , 也是核函数。
其他核函数:
当我们在SVM中处理线性不可分的数据时,核函数可以对数据进行映射,从而使得原问题在某种度量下具有更为可分的相似度,而通过引入松弛变量,我们可以放弃一些离群点的精确分类来使分类平面不受太大的影响。将这两种技术与SVM结合起来,正是SVM分类器简洁而强大的原因之一。
训练的SVM(Support Vector Machine)中,试证明若给定训练集中不存在两个点在同一位置,则存在一组参数{α1, … αm, b}以及参数γ使得该SVM的训练误差为0。
2.若我们使用问题1中得到的参数γ训练一个不加入松弛变量的SVM,是否能保证得到的SVM,仍有训练误差为0的结果,试说明你的观点。
3.若我们使用SMO(Sequential Minimal Optimization)算法来训练一个带有松弛变量的SVM,并且惩罚因子C为任意事先不知道的常数,我们是否仍能得到训练误差为0的结果,试说明你的观点。
先验知识:SVM训练过程、核函数、SMO算法
1.根据SVM的原理,我们可以将SVM的预测公式可写为下式:
其中{(x(1), y(1)), …, (x(m), y(m))}为训练样本,而{α1, …, αm, b}以及高斯核参数γ则为训练样本的参数,根据题意我们可以得到对于任意的i≠j 我们有‖x(i)﹣x(j)‖≥ε,我们可以直接对任意i,取αi=1,b=0,则有
将任意x(j)代入则有
注意到y(i)∈{1, ﹣1}
由题意知‖x(i)﹣x(j)‖≥ε,取γ=ε/㏒1/2m
故有
可知对于任意x(j),预测结果与样本的距离不超过1,则训练误差为0。
2.2.若我们使用问题1中得到的参数γ训练一个不加入松弛变量的SVM,是否能保证得到的SVM,仍有训练误差为0的结果,试说明你的观点。
我们能得到训练误差为0的分类器,我们仅需要证明解存在即可。考虑SVM推导中的限制y(i)(wTx(i)﹢b)≥1,与上一问相同,我们取b=0,那么则有y(i)·f(x(j))>0,由上问,我们有
所以一个可行解在将所有αi取到足够大时(这里改变αi的取值并不会影响上一问的结论),我们可得到y(i)(wTx(i)﹢b)≥1,则得到一个可行解,那么最优解的训练误差仍为0。
我们的分类器并不一定能得到0训练误差,因为我们的优化目标改变了,并不再是训练误差最小,考虑我们优化的结果实际上包含两项
可知当我们的参数C选取较小的值时,我们就可以得出后一正则项将占据优化的较大比重,那么一个带有训练误差,但是参数较小的点将成为更优的结果,例如当C取0时,w也可取0即可达到优化目标,但是显然这样我们的训练误差不一定能达到0。
在空间上线性可分的两类点,分别向SVM分类的超平面上做投影,这些点在超平面上的投影仍然是线性可分的吗?能否证明你的观点?
背景知识:数学基础,机器学习基础,SVM理论推导
对于任意线性可分的两类点,他们在SVM分类的超平面上的投影都是线性不可分的。
首先分析题意,线性可分的两类点,即指通过一个超平面可以将两类点完全分开,如左图所示,假设蓝色的超平面(对于二维空间来说,一维的线即为超平面)为SVM算法计算得出的分类平面,那么红绿两类的点就被它完全分开。我们的问题是将红绿两色的点,向蓝色平面上做如右图所示投影,可得在超平面上红绿两色的点,问题即为投影后的点仍然是线性可分的吗?
这个问题初看起来第一感觉是并不是线性可分的,反例也很好构造,设想只有两个点每个点各属于一类的情况,那么SVM的分类超平面就在两点连线的中垂线上,那么两点关于超平面的投影落在了平面上的同一点自然无法线性可分。实际上对于任意线性可分的两组点,它们在SVM分类的超平面上的投影都是线性不可分的,那么这个结论怎么证明呢?
我们在下面的叙述中以二维情况进行讨论,对于高维空间的推广也是成立的。先考虑SVM分类中只有支持向量的情况,使用反证法,假设存在一个SVM分类结果的超平面,所有支持向量在这个超平面上的投影依然线性可分。那么这个超平面的分类结果如下图所示,使用初等几何知识不难发现图中A,B两点连线的中垂线所组成的超平面蓝色虚线是相较于蓝色实线超平面更优的解,且两组点在新的超平面下线性不可分。而我们之前假设蓝色实线超平面为最有的解,由此推出矛盾。
但我们的证明目前还有不严谨之处,即我们假设了仅有支持向量的情况,会不会在超平面的变换过程支持向量发生了改变,原先的非支持向量和支持向量发生了转化呢?下面我们就来证明SVM的分类结果仅依赖于支持向量。考虑SVM推导中的KKT条件:
结合3和4两个条件不难发现gi()<0时,必有i=0,将这一结果与拉格朗日对偶优化问题的公式相比较:
可以看到,除支持向量外,其他非支持向量的系数均为0,所以SVM的分类结果与仅使用支持向量的分类结果一致,这也是SVM有极高的运行效率的关键之一。将这一结论代回我们的假设中,可知去掉非支持向量并不影响SVM的分类结果,故此证明成立。
实际上,该问题也可以通过凸优化理论中的超平面分离定理(Separating Hyperplane Theorem)更加轻巧地解决。该定理是在凸优化理论中极为重要,定理的定义是:对于不相交的两个凸集,存在一个超平面,将两个凸集分离,并且该超平面为两个凸集上最短距离两点连线的中垂线。
我们可以考虑线性可分的这两类点的凸包,不难发现,SVM所求得的超平面为两凸包上最短距离两点连线的中垂线,由超平面分离定理可得,其为定理中两类点的凸包的超平面。而两个凸包中距离最短的两点只有两种可能,为样本点或在两个样本点的连线上。分情况两边均为样本点,两边均在样本点的连线上,一边为样本点一边在样本点的连线上三种情况简单讨论即可发现,无论哪种情况两类点的投影均是线性不可分的。
对于面试者来说,能通过对SVM的推导给出前一种结论即可,如果熟悉凸优化理论,也可以根据后一种思路来作答。
import sys
from class_vis import prettyPicture
from prep_terrain_data import makeTerrainData
import matplotlib.pyplot as plt
import copy
import numpy as np
import pylab as pl
features_train, labels_train, features_test, labels_test = makeTerrainData()
########################## SVM #################################
### we handle the import statement and SVC creation for you here
from sklearn.svm import SVC
clf = SVC(kernel="linear")
#### using the training features/labels, and to
#### make a set of predictions on the test data
#### store your predictions in a list named pred
clf.fit(features_train, labels_train)
pred = clf.predict(features_test)
from sklearn.metrics import accuracy_score
acc = accuracy_score(pred, labels_test)
def submitAccuracy():
return acc
结果是0.92
对于一个使用高斯核训练的SVM中,若给定训练集中不存在两个点在同一位置,则存在一组参数以及参数gamma使得该SVM的训练误差为0.
训练误差为0的SVM分类器一定存在。
加入松弛变量的SVM(SMO算法)训练的线性分类器不一定能得到训练误差为0 的模型。因为我们的优化目标改变了,并不再是使训练误差最小。
几年前,J.K. 罗琳(凭借《哈利波特》出名)试着做了件有趣的事。她以 Robert Galbraith 的化名写了本名叫《The Cuckoo’s Calling》的书。尽管该书得到一些不错的评论,但是大家都不太重视它,直到 Twitter 上一个匿名的知情人士说那是 J.K. Rowling 写的。《伦敦周日泰晤士报》找来两名专家对《杜鹃在呼唤》和 Rowling 的《偶发空缺》以及其他几名作者的书进行了比较。分析结果强有力地指出罗琳就是作者,《泰晤士报》直接询问出版商情况是否属实,而出版商也证实了这一说法,该书在此后一夜成名。
我们也将在此项目中做类似的事。我们有一组邮件,分别由同一家公司的两个人撰写其中半数的邮件。我们的目标是仅根据邮件正文区分每个人写的邮件。在这个迷你项目一开始,我们将使用朴素贝叶斯,并在之后的项目中扩展至其他算法。
我们会先给你一个字符串列表。每个字符串代表一封经过预处理的邮件的正文;然后,我们会提供代码,用来将数据集分解为训练集和测试集。
使用 sklearn SVC 分类器进行导入、创建、训练和预测。在创建分类器时使用线性内核(如果你忘记此步骤,你会发现分类器要花很长的时间来训练)。
#!/usr/bin/python
"""
This is the code to accompany the Lesson 2 (SVM) mini-project.
Use a SVM to identify emails from the Enron corpus by their authors:
Sara has label 0
Chris has label 1
"""
import sys
from time import time
sys.path.append("../tools/")
from email_preprocess import preprocess
### features_train and features_test are the features for the training
### and testing datasets, respectively
### labels_train and labels_test are the corresponding item labels
features_train, features_test, labels_train, labels_test = preprocess()
#########################################################
### your code goes here ###
from sklearn.svm import SVC
clf = SVC(kernel="linear")
t0 = time()
clf.fit(features_train, labels_train)
print "training time:", round(time()-t0, 3), "s"
t1 = time()
pred = clf.predict(features_test)
print "tsting time:", round(time()-t1, 3), "s"
from sklearn.metrics import accuracy_score
acc = accuracy_score(pred, labels_test)
print acc
#########################################################
准确率为:0.984072810011
training time: 170.512 s
tsting time: 17.65 s
加快算法速度的一种方式是在一个较小的训练数据集上训练它。这样做换来的是准确率几乎肯定会下降。让我们更具体地探讨这个问题:在训练分类器之前,立即加入以下两行。
features_train = features_train[:len(features_train)/100]
labels_train = labels_train[:len(labels_train)/100]
这两行有效地将训练数据集切割至原始大小的 1%,丢弃掉 99% 的训练数据。你可以使其他所有代码保持不变。
现在的准确率是多少?
运行结果为:
training time: 0.097 s
tsting time: 1.034 s
0.884527872582
仅仅使用了1%的原始数据进行训练,结果准确率还能达到88.45%,可见效果还不算差。
如果速度是一个主要考虑因素(对于许多实时机器学习应用而言确实如此),并且如果牺牲一些准确率可加快你的训练/预测速度,则你可能会想这样做。
在以下哪些应用中,你可以想象非常快速地运行的算法尤其重要?
标记信用卡欺诈,在欺诈发生之前阻止交易
Siri 之类的语音识别
这两个对实时性要求很高
预测电子邮件作者,则不需要实时进行。
保留上一个测试题中的训练集代码段,以便仍在 1% 的完整训练集上进行训练。将 SVM 的内核更改为“rbf”。
这个更复杂的内核给出的准确率是多少?
import sys
from time import time
sys.path.append("../tools/")
from email_preprocess import preprocess
### features_train and features_test are the features for the training
### and testing datasets, respectively
### labels_train and labels_test are the corresponding item labels
features_train, features_test, labels_train, labels_test = preprocess()
features_train = features_train[:len(features_train)/100]
labels_train = labels_train[:len(labels_train)/100]
#########################################################
### your code goes here ###
from sklearn.svm import SVC
clf = SVC(kernel="rbf")
t0 = time()
clf.fit(features_train, labels_train)
print "training time:", round(time()-t0, 3), "s"
t1 = time()
pred = clf.predict(features_test)
print "tsting time:", round(time()-t1, 3), "s"
from sklearn.metrics import accuracy_score
acc = accuracy_score(pred, labels_test)
print acc
#########################################################
运行结果为:
training time: 0.111 s
tsting time: 1.2 s
0.616040955631
可见仅仅如线型内核替换为RBF内核,准确率会降低。
保持训练集大小不变,并且保留上一个测试题中的 rbf 内核,但是尝试多个 C 值(比如:10.0、100.、1000. 和 10000.)。
哪个给出的准确率最高?
clf = SVC(kernel="rbf",C = 10.0)
当C值分别为 10.0、100.、1000. 和 10000.时,准确率分别为:
0.616040955631
0.616040955631
0.821387940842
0.892491467577
可见,当C=10000时,对应的SVM的准确率最高,此时的决策边界也最复杂。
你已经为 RBF 内核优化了 C,现在恢复为使用完整的训练集。较大的训练集往往能提高算法的性能,所以(通过在大数据集上调整 C 和进行训练)我们应得到相当优化的结果。
经过优化的 SVM 的准确率是多少?
运行结果为:
training time: 109.86 s
tsting time: 11.003 s
0.990898748578
你的 SVM(0 或 1,分别对应 Sara 和 Chris)将测试集中的元素 10 预测为哪一类?元素 26 ?还是元素 50 ?
(使用 RBF 内核、C=10000 和 1% 的训练集。通常,使用完整的训练集能获得最好的结果,但是我们发现使用 1% 的完整训练集不仅大幅加快计算过程,而且不会改变我们的结果,因此你在这里可以随意使用该快捷算法。)
而且需要说明的是,我们这里给出的数据点数字 (10, 26, 50) 假设使用的是零索引列表。因此,使用类似于 answer=predictions[100] 的表达式可找到元素 # 100 的正确答案。
import sys
from time import time
sys.path.append("../tools/")
from email_preprocess import preprocess
### features_train and features_test are the features for the training
### and testing datasets, respectively
### labels_train and labels_test are the corresponding item labels
features_train, features_test, labels_train, labels_test = preprocess()
features_train = features_train[:len(features_train)/100]
labels_train = labels_train[:len(labels_train)/100]
#########################################################
### your code goes here ###
from sklearn.svm import SVC
clf = SVC(kernel="rbf",C = 10000.)
t0 = time()
clf.fit(features_train, labels_train)
print "training time:", round(time()-t0, 3), "s"
pred = clf.predict(features_test)
print pred[10]
print pred[26]
print pred[50]
预测结果如下
输出元素10/26/50分别被预测为Chris/Sara /Chris
1
0
1
共有超过 1700 个测试事件——有多少预测属于“Chris”(1) 类?(使用 RBF 内核、C=10000. 以及完整的训练集。)
num = 0
for i in range(len(pred)):
if pred[i] == 0:
num += 1
print "chris's email number is: "
print num
SVM的公式推导过程:
http://cs229.stanford.edu/notes/cs229-notes3.pdf
对偶问题与KKT条件:
http://stanford.edu/class/ee364a/lectures/duality.pdf
超平面分离定理:
http://www.princeton.edu/~amirali/Public/Teaching/ORF523/S16/ORF523_S16_Lec5_gh.pdf