1. 线性可分与线性不可分
上图左边为线性可分的,通过一条直线就可以把两类分开;而右边是线性不可分的,如何用一条线都不能把红黑两个分离开。对于线性不可分的我们可以采取升维的方式来解决,比如有如下十个样本。
红色的是一类,绿色的是另一类,它在二维平面上是线性不可分的;我们现在加上一维,如果它是红色的,第三维设为1,否则设为0。这样我们通过升维把线性不可分的变为线性可分的,如下图所示。
从正方向看下来,三维还原成二维,就变为:
2 支持向量机原理
支持向量机(Support Vector Machine,以下简称SVM),作为传统机器学习的一个非常重要的分类算法,它是一种通用的前馈网络类型,最早是由Vladimir N.Vapnik 和 Alexey Ya.Chervonenkis在1963年提出,目前的版本(softmargin)是CorinnaCortes 和 Vapnik在1993年提出,1995年发表。深度学习(2012)出现之前,如果不考虑集成学习的算法,不考虑特定的训练数据集,在分类算法中的表现SVM说是排第一估计是没有什么异议的。
SVM本来是一种线性分类和非线性分类都支持的二元分类算法,但经过演变,现在也支持多分类问题,也能应用到了回归问题。
一些线性不可分的问题可能是非线性可分的,即特征空间存在超曲面(hypersurface)将正类和负类分开。使用非线性函数可以将非线性可分问题从原始的特征空间映射至更高维的希尔伯特空间(Hilbert space),从而转化为线性可分问题。
支持向量机通过某非线性变换 φ( x) ,将输入空间映射到高维特征空间。特征空间的维数可能非常高。如果支持向量机的求解只用到内积运算,而在低维输入空间又存在某个函数 K(x, x') ,它恰好等于在高维空间中这个内积,即K( x, x') =<φ( x) ⋅φ( x') > 。那么支持向量机就不用计算复杂的非线性变换,而由这个函数 K(x, x')直接得到非线性变换的内积,使大大简化了计算。这样的函数 K(x, x') 称为核函数。
核函数一般包括以下几个:
中文名称 |
英文名称 |
表达式 |
线性核 |
Linear kernel |
k(x,y) = xTy+c |
多项式核 |
polynomial kernel |
k(x,y) = (axTy+c)d |
径向基函数核(高斯核) |
RBF kernel |
k(x,y) = exp(- ) |
拉普拉斯核 |
Laplacian kernel |
k(x,y) = exp(- ) |
Sigmoid核 |
Sigmoid kernel |
k(x,y) = tanh(axy+r) |
我们下面来看代码
# 导入NumPy库 import numpy as np # 导入画图工具 import matplotlib.pyplot as plt from sklearn import svm from sklearn.datasets import make_blobs def SVM_linear(): #创建50个数据点,分成2类 X , y = make_blobs(n_samples=50,random_state=6,centers=2) #创建一个线性内核的支持向量 clf = svm.SVC(kernel='linear',C=1000)# C-SVC的惩罚参数C,默认值是1.0 clf.fit(X,y) # 画出数据点 plt.scatter(X[:,0],X[:,1],c=y,cmap=plt.cm.Paired,s=30) # 建立图像坐标 ax = plt.gca() #当前的图表和子图可以使用plt.gcf()和plt.gca()获得 xlim = ax.get_xlim() #返回当前Axes视图的x的上下限 ylim = ax.get_ylim() #返回当前Axes视图的y的上下限 # 生成等差数列 xx = np.linspace(xlim[0],xlim[1],30) yy = np.linspace(ylim[0],ylim[1],30) YY , XX = np.meshgrid(yy,xx) # meshgrid函数用两个坐标轴上的点在平面上画网格。 xy = np.vstack([XX.ravel(),YY.ravel()]).T# np.hstack():横向拼接,增加特征量;np.vstack():纵向拼接,增加样本个数 Z = clf.decision_function(xy). reshape(XX.shape) #decision_function:计算样本点到分割超平面的函数距离 #shape是查看数据有多少行多少列 #reshape()是数组array中的方法,作用是将数据重新组织 # 把分类决定边界画出来 ax.contour(XX,YY,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestyles=['--','-','--']) #绘制等高线 ax.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],s=100,linewidth=1,facecolors='none') plt.show()
以上展示的是SVC线性核的模型。中间这条“线”叫做最大边界超平面(Maximum Margin Separating Hyperplane)(二维时为线,三维时为面,多维时为超平面)。这条线到和所有支持向量的距离都是最大的。离这个超平面最近的点就是“支持向量”,点到超平面的距离叫做间隔,支持向量机的意思就是使超平面和支持向量之间的间隔尽可能的大。
f(x) = wTx+b
w = (w1,w2,w3,…,wn)为法向量
f(x) = 0:超平面上
> 0 :超平面一边的一个点
< 0 :超平面另一边的一个点
注:
- SVM=Support Vector Machine 是支持向量机;
- SVC=Support VectorClassification就是支持向量机用于分类;
- SVR=Support Vector Regression.就是支持向量机用于回归分析。
SVM适合于有监督学习的分类与回归算法,如下图。
wTxi+b>=1=>yi=1
wTxi+b<=-1=>yi=-1
下面我们通过代码来看一下:SVC with Linear Kernel、SVC with RBF Kernel、SVC with sigmoid Kernel和SVC with poly Kernel的曲线。
import matplotlib.pyplot as plt from sklearn import svm from sklearn.datasets import make_blobs def SVM_base(mykernel,title): #创建50个数据点,分成2类 X , y = make_blobs(n_samples=50,random_state=6,centers=2) #创建一个线性内核的支持向量 if mykernel == 'LinearSVC': clf = svm.LinearSVC() else: clf = svm.SVC(kernel=mykernel,C=1000)# C-SVC的惩罚参数C,默认值是1.0 clf.fit(X,y) # 画出数据点 plt.scatter(X[:,0],X[:,1],c=y,cmap=plt.cm.Paired,s=30) # 建立图像坐标 ax = plt.gca() #当前的图表和子图可以使用plt.gcf()和plt.gca()获得 xlim = ax.get_xlim() #返回当前Axes视图的x的上下限 ylim = ax.get_ylim() #返回当前Axes视图的y的上下限 # 生成等差数列 xx = np.linspace(xlim[0],xlim[1],30) yy = np.linspace(ylim[0],ylim[1],30) YY , XX = np.meshgrid(yy,xx) # meshgrid函数用两个坐标轴上的点在平面上画网格。 xy = np.vstack([XX.ravel(),YY.ravel()]).T# np.hstack():横向拼接,增加特征量;np.vstack():纵向拼接,增加样本个数 Z = clf.decision_function(xy). reshape(XX.shape) #decision_function:计算样本点到分割超平面的函数距离 #shape是查看数据有多少行多少列 #reshape()是数组array中的方法,作用是将数据重新组织 # 把分类决定边界画出来 ax.set_title(title) ax.contour(XX,YY,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestyles=['--','-','--'])#绘制等高线 if mykernel != 'LinearSVC': ax.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],s=100,linewidth=1,facecolors='none') def SVM_for_all_model(): models = ['linear','rbf','sigmoid','poly','LinearSVC'] titles = ['SVC with Linear Kernel','SVC with RBF Kernel','SVC with sigmoid Kernel','SVC with poly Kernel','LinearSVC'] figure, axs = plt.subplots(2,3,figsize =(10,3)) figure.suptitle('All Model of SVM') i = 0 for model in models: plt.subplot(2,3,i+1) SVM_base(model,titles[i]) i = i+1 plt.show()
我们再通过程序绘制Linear Kernel、RBF Kernel和poly Kernel 和LinearSVC的对红酒数据画出边界曲线。
from sklearn import datasets #定义一个函数来画图 def make_meshgrid(x, y, h=.02): x_min,x_max = x.min()-1,x.max()+1 y_min,y_max = y.min()-1,y.max()+1 xx, yy = np.meshgrid(np.arange(x_min, x_max, h),np.arange(y_min, y_max, h)) return xx,yy #定义一个绘制等高线的函数 def plot_contour(ax, clf, xx,yy,**params) : Z = clf.predict(np.c_[xx.ravel(),yy.ravel()]) Z = Z.reshape(xx.shape) out = ax.contourf(xx,yy,Z,**params) return out #不同核函数的SVM对比 def compare_with_different_SVM(): wine = datasets.load_wine() #选取数据集前两个特征 X = wine.data[:,:2] y = wine.target C = 2.5 #SVM参数正则化 models = (svm.SVC(kernel='linear',C=C), svm.LinearSVC(C=C), svm.SVC(kernel='rbf',gamma=0.7,C=C), svm.SVC(kernel='sigmoid',C=C), svm.SVC(kernel='poly',degree=3,C=C)) models = (clf.fit(X,y) for clf in models) #设定图标 titles = ('SVC with Linear Kernel', 'LinearSVC(Linear Kernel)', 'SVC with RBF Kernel', 'SVC with sigmoid Kernel', 'SVC with polynomial(degree 3) Kernel'#设置子图形的个数和排列方式 fig, sub = plt.subplots(2,3,figsize =(10,3)) plt.subplots_adjust(wspace=0.4,hspace=0.4) #使用前面定义的函数画图 X0, X1 = X[:,0],X[:,1] xx, yy = make_meshgrid(X0, X1) for clf, title, ax in zip(models, titles,sub.flatten()): plot_contour(ax,clf,xx,yy,cmap=plt.cm.plasma,alpha=0.8) ax.scatter(X0,X1,c=y,cmap=plt.cm.plasma,s=20,edgecolors='k') ax.set_xlim(xx.min(),xx.max()) ax.set_ylim(yy.min(),yy.max()) ax.set_xlabel('Feature 0') ax.set_ylabel('Feature 1') ax.set_xticks(()) ax.set_yticks(()) ax.set_title(title) plt.show()
由此可以看出:
- Linear Kernel与LinearSVC差不多,边界都是线性的;
- RBF、sigmoid和polynomial Kernel 边界都不是线性的。