有一次公司项目上的同事一起吃饭(面前是一锅炒土鸡),提到了支持向量机,学文的同事就问支持向量机是什么,另一个数学物理大牛想了一下,然后说,一种鸡。。。
确实很难一句话解释清楚这只鸡。。。support vector machine从字面意思来说应该是依靠support vector来划分数据(其实也能回归啦。。)的机器学习模型。它是一个凸优化问题。
SVM的核心将数据的特征投射到高维,然后找到超平面,分割不同类别的数据点,而且要使分离的程度越大越好,至于为什么叫支持向量机,是因为每个类别都会有一些数据点作为支撑向量,这些支撑向量决定了最后分割的超平面。
这个图里的圈圈就是两边的支撑向量。
我们先讲最简单的情况,也就是线性可分的平面二分类问题。
支持向量机的目标是,找到一条分界线,把两类数据分开。但是这样的线可能有很多。
像上图。并不是每条线都是一个很好的分割线,如果紧贴着红点,那么线的另一头会立马被判为蓝色,这样对红点就很不合适了。所以SVM的目标不光是要找到这样一条分割线,而是要找到一条能最公平合理的分割线,让两类数据中最边缘的数据点距离分割线越远越好,这样这个分类器的泛化能力才会更强。于是乎支撑向量就被引入了。
直线的一般表达式:ax+by+c=0
对于多维,可以直接表达成: wTx+b=0 ,在多维情况下,它是个超平面。
对于直线上方的类别有 wTx+b>0
对于直线下方的类别有 wTx+b<0
有了直线,我们需要定义数据点到分割平面的距离,这里用的是点到直线的距离公式。
回顾一下支持向量机是要干啥来着,是要找到一条把两类数据分得很开的割平面对不对,这需要我们找到两类数据最边缘的数据点(支撑向量),然后画出来两条平行的切面,两边支撑向量距离的垂直平分线就是最终的切割平面。
支持向量机的目标是让最边缘的点之间的距离越大越好。即 ρ 最大
这是我们的终极目标,敲黑板!
接下来,我们来分析怎么把这个目标写成数学表达式~
很简单,只需要表示support vector到分割线的距离,并且让该距离最大不就行了。
直线上方的点 yi=1wTxi+b≥ρ2
直线下方的点 yi=−1wTxi+b≤−ρ2
等价于: yi(wTxi+b)≥ρ2
那么新的问题来了,我们如何知道哪个点是support vector?
答案就是离分割线最近的点对不对。
所以,目标函数是一个极小极大问题。。。
之前我们有 yi(wTxi+b)≥ρ2 ,我们总可以通过缩放w和b,使该不等式右边划归到1。如此,就可以简化目标函数。
我们把问题改为求它的对偶问题
因为目标函数是二次的,而约束在参数w和b上是线性的,因此这个问题是一个凸优化问题,可以通过标准的拉格朗日乘子来求解。
用拉格朗日乘子法:
解这个优化问题的方法是先求关于 α 的极大,再求关于w和b的极小。
我们转而求它的对偶问题:
(1)先求极小问题
另L对w,b求偏导并等于0, 有
然后我们把这两个式子带入到拉格朗日函数(1)中,最终化简可以得到:
(2)现在求极大问题
还是将问题转成对偶问题,求解这个问题用到了SMO,sequential minimal optimization,即每次只挑两个拉格朗日乘子,其他乘子认为是常数,然后每次都化为两变量凸优化问题。
最终通过求得 α 的值,找到最优的w和b。注意,最后的结果里,只有支撑向量的 α>0 , 对于非支撑向量,其 α=0
有时候数据点不会分隔的这么好,会有一些点,总是站在对方阵营。或者及时我们能找到一条线,把两个类别的数据点刚好完美分开,我们可能也不希望是这根线。。。因为我们希望模型能有较好的泛化能力,希望中间的过渡带 ρ 越大越好,所以我们会对某些不好的点选择性忽略。
一般用 ξ 来表示松弛变量。加入松弛变量后的模型优化目标和约束变成啥样了呢。
然后跟刚才的过程差不多,先对w,b, ξ 求极小,在对 α 求极大。
这里需要强调的是,C越大,我们越倾向于没有松弛变量,即模型会尽可能分对每一个点,反之,C越小,模型的泛化能力越强。
终于讲到核函数了。。。我最喜欢的部分。。。
之前的问题中我们都是用的x本尊,现实是,这堆数据点可能一条线不可分,比如说圆环图,斑马图。。。于是我们可以对x做高维变换,以期望在高维空间中,数据点能分开。此处盗用一张非常著名的图。
之前所有 xi 出现的地方,都可以改成 Φ(xi)
比如最终的分割超平面:
因为从始至终,我们都只用到了内积,所以我们并不需要定义核函数的具体形式,而只需给出核函数的内积形式。
三种常见核函数:
多项式核函数: κ(xi,xj)=(α||xi−xj||a+r)b α,a,b,r是常数
RBF径向基核函数: κ(xi,xj)=exp(−||xi−xj||22σ2) 也叫高斯核函数,因为长得像
sigmoid核函数: κ(xi,xj)=tanh(γ||xi−xj||a+r)
一般来讲RBF核已经很好用了,它可以投射到无穷维(回顾一下 ex 的tylor展开式。。。)
你可能还会有疑问,都什么函数能当核函数。答案是,所有半正定矩阵。
好了,现在我们终于可以来两行代码了。。。
from sklearn.datasets import load_iris
data = load_iris()
用sklearn自带的鸢尾花数据集,好嘛,我知道老掉牙了。。。复习一下
dir(data)返回的结果是[‘DESCR’, ‘data’, ‘feature_names’, ‘target’, ‘target_names’],比较直观,我就不解释了。
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(data.data, data.target,test_size=0.3,random_state=30,stratify=data.target)
这一步是划分训练集和测试集,永远不要忘了这一步。
from sklearn import svm
model=svm.SVC(C=0.5, kernel='rbf',gamma=5 )
sklearn.svm.SVC(C=1.0, kernel=’rbf’, degree=3, gamma=’auto’, 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)
C是松弛变量的系数
kernel是要定义核函数的名字,跟后面的系数也息息相关
gamma是这三个核函数的乘系数
degree是对多项式核函数来的。。。
coef0是另外两个核函数里的r
probability是可以允许多分类的情况
class_weight是可以实现给不同类别设不同的权重
from sklearn.metrics import accuracy
model.fit(X_train,y_train)
model.score(X_test,y_test)
输出是91%
虽然如此,我们还是象征性调个参
import numpy as np
from sklearn.model_selection import GridSearchCV
parameters={'kernel':['linear','rbf','sigmoid','poly'],'C':np.linspace(0.1,20,50),'gamma':np.linspace(0.1,20,20)}
svc = svm.SVC()
model = GridSearchCV(svc,parameters,cv=5,scoring='accuracy')
model.fit(X_train,y_train)
model.best_params_
model.score(X_test,y_test)
{‘C’: 1.7244897959183674, ‘gamma’: 0.10000000000000001, ‘kernel’: ‘rbf’}
accuracy=95%