此文比较详细的介绍了SVM在sklearn中的应用和一些相关概念 本文以案例为主,辅以少量讲解,需要读者自行理解
VC维 VC 维是衡量函数类的复杂度的一种方式,通过评估函数类中函数的弯曲程度实现。举个例子,假设 { f ( x , α ) } \{f(x,\alpha)\} {f(x,α)}为线性指示函数类 I ( α 0 + α 1 T x > 0 ) ) I(\alpha_0 + \alpha_1^T x \gt 0)) I(α0+α1Tx>0))。直观理解,该函数通过直线 将平面分成两部分,一侧取值为0,一侧取值为1。如果该函数的VC维为 ,则表明在平面上任意给出 个点的位置及取值(0或1),总存在一条直线 将这 个点分开,一侧取0,一侧取1,也就是存在能满足这 个点的函数 。结论是,平面中线性指示函数的VC维等于3,也就是平面中任意3个点(无论如何取值)总能被一条直线分开,而四个点却不行,如下图
详细讲解推荐看台湾大学林轩田老师的视频 推荐阅读这篇笔记https://blog.csdn.net/red_stone1/article/details/71191232
超平面 在几何中,超平面是一个空间的子空间,它是维度比所在空间小一维的空间。 如果数据空间本身是三维的, 则其超平面是二维平面,而如果数据空间本身是二维的,则其超平面是一维的直线。 在二分类问题中,如果一个超平面能够将数据划分为两个集合,其中每个集合中包含单独的一个类别,我们就说这个超平面是数据的“决策边界”。
SVM svm主要就是寻找边际最大的决策边界 0 = w T + b 0 = w^T + b 0=wT+b
支持向量 在支持向量机中,距离超平面bai最近的且满足一定条件的du几个训练样本点被称zhi为支持向量。
先展示一个线性核函数的分类案例,使用sklearn中的make_blobs函数生成一些聚类数据 导入模块和数据
from sklearn.datasets import make_blobs from sklearn.svm import SVC import matplotlib.pyplot as plt import numpy as np做一个散点图看看数据分布
X,y = make_blobs(n_samples = 50,centers = 2,random_state = 0,cluster_std = 0.6) plt.figure() #添加一个窗口 plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap = "rainbow") plt.xticks([]) # 固定x轴 plt.yticks([]) plt.show()接下来是为了画决策边界做准备的,他用了很巧妙的方法,先在画板上定义了许多等间距的网格点,再把网格点的x和y的数据做成shape是(900,2)的xy变量,把他当作test datasets 放到clf,decision_function() 当中,这个函数返回的是每个样本点到决策边界的距离,这样把xy放到里面就能得到网格上每个点到决策边界的距离,再把这个数据当作Z,做等高线。
xlim = ax.get_xlim() # x轴坐标的范围 ylim = ax.get_ylim() print(xlim) # 形成沿x轴,y轴的等差数列 分成30份 axisx = np.linspace(xlim[0],xlim[1], 30) axisy = np.linspace(ylim[0],ylim[1], 30) #形成网格数据 axisx,axisy = np.meshgrid(axisx,axisy) # print(axisx) # ravel()是降维函数 # np.vstack([axisx.ravel(), axisy.ravel()]).shape == (2, 900) # xy就是两列数轴的数据 xy.shape == (900,2) # np.vstack() 可以代替成 np.array() xy = np.vstack([axisx.ravel(), axisy.ravel()]).T理解函数meshgrid和vstack的作用
#理解函数meshgrid和vstack的作用 a = np.array([1,2,3]) b = np.array([7,8]) #两两组合,会得到多少个坐标? #答案是6个,分别是 (1,7),(2,7),(3,7),(1,8),(2,8),(3,8) v1,v2 = np.meshgrid(a,b) print(v1) print(v2)开始搭建SVC模型
# 建模 clf = SVC(kernel= "linear").fit(X,y) # clf.decision_function(X).shape == (900,) # clf.decision_function(xy) 得到网格点上每一个点到决策边界的距离 # clf.decision_function(X) 返回每个样本到决策边界的距离 # 把距离数据转换成axisx的shape,这是由于画图的函数contour要求Z的结构必须与X和Y保持一致 Z = clf.decision_function(xy).reshape(axisx.shape) # y == 0的数据恰好距离都是负的 print(clf.decision_function(X)) # [ 1.40603714 1.3591769 -2.51058235 -2.13942243 2.18430606 3.60280644 # 3.79864307 0.95048419 2.06722659 -4.36242933 -3.33917354 -3.33016044 # -2.80610364 2.78359637 -1.78232701 -1.7657915 -2.3183929 3.0466215 # -0.99999987 -2.86848992 2.63755919 1.89023869 2.25718297 2.62966853 # 2.41900855 -0.9999994 -2.22302791 -3.40694626 2.78168963 -1.44976041 # 1.64660828 -2.95807889 -2.72951819 -2.56144709 -3.51816073 2.33579112 # 2.11037259 -3.69706531 3.75909944 -1.60942589 1.81798745 -2.25885574 # 3.7399511 1.52000771 -2.3538635 1.44881542 1.06318842 -1.48566549 # 2.73630505 -2.69205979] print(clf.decision_function(X)[y == 0]) # [-2.51058235 -2.13942243 -4.36242933 -3.33917354 -3.33016044 -2.80610364 # -1.78232701 -1.7657915 -2.3183929 -0.99999987 -2.86848992 -0.9999994 # -2.22302791 -3.40694626 -1.44976041 -2.95807889 -2.72951819 -2.56144709 # -3.51816073 -3.69706531 -1.60942589 -2.25885574 -2.3538635 -1.48566549 # -2.69205979]画决策边界
#首先要有散点图 plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap = "rainbow") axx = plt.gca() # 获取当前子图如果不存在则创建新的子图 #画决策边界和平行于决策边界的超平面 axx.contour(axisx,axisy,Z ,colors="k" ,levels=[-1,0,1] #画三条等高线,分别是Z == -1,Z == 0和Z == 1的三条线 ,alpha=0.5#透明度 ,linestyles=["--","-","--"]) axx.set_xlim(xlim)#设置x轴取值 axx.set_ylim(ylim) # plt.show()随便找一个点,把那个点变成黑色,clf.decision_function([[X_t[0],X_t[1]]])显示了那个点到决策边界的距离。
x_c = 1 X_t = [X[x_c,0],X[x_c,1]] plt.scatter(X[:,0],X[:,1],c = y ,s = 50 ,cmap= "rainbow") plt.scatter(X_t[0],X_t[1],c = "black" ,s = 50 ,cmap = "rainbow") # print(X[10,0],X[10,1]) print(clf.decision_function([[X_t[0],X_t[1]]])) print(y[x_c])画出那一个点的等高线
clf.decision_function(X[10].reshape(1,2)) plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow") plt.scatter(X[10,0],X[10,1],c = "black" ,s = 50 ,cmap = "rainbow") ax = plt.gca() print(X[10]) print(Z[10,0]) ax.contour(axisx,axisy,Z ,colors="k" ,levels=[-3.33917354] ,alpha=0.5 ,linestyles=["--"])把上面的过程都封装进一个函数里
# 将上述过程包装成一个函数 def plot_svc_decision_function(model,ax = None): if ax is None: ax = plt.gca() xlim = ax.get_xlim() ylim = ax.get_ylim() # 形成沿x轴,y轴的等差数列 axisx = np.linspace(xlim[0],xlim[1], 30) axisy = np.linspace(ylim[0],ylim[1], 30) axisx,axisy = np.meshgrid(axisx,axisy) xy = np.vstack([axisx.ravel(), axisy.ravel()]).T #画出决策边界 P = model.decision_function(xy).reshape(axisx.shape) print(model.decision_function(xy).reshape(axisx.shape).shape ) ax.contour(axisx, axisy, P,colors="k",levels=[-1,0,1],alpha=0.5,linestyles=["--","-","--"]) ax.set_xlim(xlim) ax.set_ylim(ylim) #则整个绘图过程可以写作: clf = SVC(kernel = "linear").fit(X,y) plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow") plot_svc_decision_function(clf)模型的一些训练之后的参数
print(clf.predict(X)) # [1 1 0 0 1 1 1 1 1 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 0 0 0 1 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0 1 1 0 1 0] print(clf.score(X,y)) # 1.0 print(clf.support_vectors_) # [[0.44359863 3.11530945] # [2.33812285 3.43116792] # [2.06156753 1.96918596]] print(clf.support_) #[18 25 7]在非线性数据的情况下就要使用非线性的核函数了,上文中的一些包也要引入,在上文中包的基础上引入下面这个包,生成一些环形数据
from sklearn.datasets import make_circles X,y = make_circles(100,factor = 0.1 ,noise = 0.1) plt.scatter(X[:,0],X[:,1],c = y,s = 50,cmap = "rainbow")再次使用线性核函数就会变得一塌糊涂
clf = SVC(kernel = "linear").fit(X,y) plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="jet") plot_svc_decision_function(clf)将数据映射到更高维度,就可以直观看出分界
#定义一个由x计算出来的新维度r 高度 r = np.exp(-(X**2).sum(1)) rlim = np.linspace(min(r),max(r),100) from mpl_toolkits import mplot3d #定义一个绘制三维图像的函数 #elev表示上下旋转的角度 #azim表示平行旋转的角度 def plot_3D(elev=30,azim=30,X=X,y=y): ax = plt.subplot(projection="3d") ax.scatter3D(X[:,0],X[:,1],r,c=y,s=50,cmap='rainbow') ax.view_init(elev=elev,azim=azim) ax.set_xlabel("x") ax.set_ylabel("y") ax.set_zlabel("r") plt.show() plot_3D()使用高斯径向基核函数
clf = SVC(kernel= "rbf").fit(X,y) # 不使用线性核函数 plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow") plot_svc_decision_function(clf)除了"linear"以外的核函数都能够处理非线性情况,那究竟什么时候选择哪一个核函数呢?遗憾的是,关于核函数 在不同数据集上的研究甚少,谷歌学术上的论文中也没有几篇是研究核函数在SVM中的运用的,更多的是关于核函 数在深度学习,神经网络中如何使用。在sklearn中,也没有提供任何关于如何选取核函数的信息。 但无论如何,我们还是可以通过在不同的核函数中循环去找寻最佳的核函数来对核函数进行一个选取。接下来我们 就通过一个例子,来探索一下不同数据集上核函数的表现。我们现在有一系列线性或非线性可分的数据,我们希望通过绘制SVC在不同核函数下的决策边界并计算SVC在不同核函数下分类准确率来观察核函数的效用。
import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap from sklearn import svm#from sklearn.svm import SVC 两者都可以 from sklearn.datasets import make_circles, make_moons, make_blobs,make_classification n_samples = 100 datasets = [ make_moons(n_samples=n_samples, noise=0.2, random_state=0), make_circles(n_samples=n_samples, noise=0.2, factor=0.5, random_state=1), make_blobs(n_samples=n_samples, centers=2, random_state=5),#分簇的数据集 make_classification(n_samples=n_samples,n_features = 2,n_informative=2,n_redundant=0, random_state=5) #n_features:特征数,n_informative:带信息的特征数,n_redundant:不带信息的特征数 ] Kernel = ["linear","poly","rbf","sigmoid"] fig1,ax1 = plt.subplots(nrows=2, ncols=2,figsize=(10,8)) axes1 = ax1.flatten() #四个数据集分别是什么样子呢? for id,(X,Y) in enumerate(datasets): axes1[id].scatter(X[:,0],X[:,1],c = Y,s=50, cmap = "rainbow") plt.show() # fig1.savefig("./name1.png")查看不同核函数在不同数据集上的表现 注意fig, axes = plt.subplots(nrows, ncols,figsize=(20,16)) 要和画图函数写在一起,如果单独写的话JupyterLab会莫名其妙的把它输入,后面再使用的时候将拿不到子图
nrows=len(datasets) ncols=len(Kernel) + 1 fig, axes = plt.subplots(nrows, ncols,figsize=(20,16)) #第一层循环:在不同的数据集中循环 for ds_cnt, (X,Y) in enumerate(datasets): #在图像中的第一列,放置原数据的分布 ds_cnt 是行数 , 0 是列数 ax = axes[ds_cnt, 0] if ds_cnt == 0: ax.set_title("Input data") ax.scatter(X[:, 0], X[:, 1], c=Y, zorder=10, cmap=plt.cm.Paired,edgecolors='k') ax.set_xticks(()) ax.set_yticks(()) #第二层循环:在不同的核函数中循环 #从图像的第二列开始,一个个填充分类结果 for est_idx, kernel in enumerate(Kernel): #定义子图位置 ax = axes[ds_cnt, est_idx + 1] #建模 clf = svm.SVC(kernel=kernel, gamma=2).fit(X, Y) score = clf.score(X, Y) #绘制图像本身分布的散点图 ax.scatter(X[:, 0], X[:, 1], c=Y ,zorder=10 ,cmap=plt.cm.Paired,edgecolors='k') #绘制支持向量 ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=50, facecolors='none', zorder=10, edgecolors='k')# facecolors='none':透明的 #绘制决策边界 x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5 y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5 #np.mgrid,合并了我们之前使用的np.linspace和np.meshgrid的用法 #一次性使用最大值和最小值来生成网格 #表示为[起始值:结束值:步长] #如果步长是复数,则其整数部分就是起始值和结束值之间创建的点的数量,并且结束值被包含在内 XX, YY = np.mgrid[x_min:x_max:200j, y_min:y_max:200j] #np.c_,类似于np.vstack的功能 Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()]).reshape(XX.shape) #填充等高线不同区域的颜色 ax.pcolormesh(XX, YY, Z > 0, cmap=plt.cm.Paired) #绘制等高线 ax.contour(XX, YY, Z, colors=['k', 'k', 'k'], linestyles=['--', '-', '--'],levels=[-1, 0, 1]) #设定坐标轴为不显示 ax.set_xticks(()) ax.set_yticks(()) #将标题放在第一行的顶上 if ds_cnt == 0: ax.set_title(kernel) #为每张图添加分类的分数 ax.text(0.95, 0.06, ('%.2f' % score).lstrip('0') , size=15 , bbox=dict(boxstyle='round', alpha=0.8, facecolor='white') #为分数添加一个白色的格子作为底色 , transform=ax.transAxes #确定文字所对应的坐标轴,就是ax子图的坐标轴本身 , horizontalalignment='right' #位于坐标轴的什么方向 ) plt.tight_layout() plt.show()可以观察到,线性核函数和多项式核函数在非线性数据上表现会浮动,如果数据相对线性可分,则表现不错,如果是像环形数据那样彻底不可分的,则表现糟糕。在线性数据集上,线性核函数和多项式核函数即便有扰动项也可以表现不错,可见多项式核函数是虽然也可以处理非线性情况,但更偏向于线性的功能。 Sigmoid核函数就比较尴尬了,它在非线性数据上强于两个线性核函数,但效果明显不如rbf,它在线性数据上完全 比不上线性的核函数们,对扰动项的抵抗也比较弱,所以它功能比较弱小,很少被用到。 rbf,高斯径向基核函数基本在任何数据集上都表现不错,属于比较万能的核函数。我个人的经验是,无论如何先试试看高斯径向基核函数,它适用于核转换到很高的空间的情况,在各种情况下往往效果都很不错,如果rbf效果 不好,那我们再试试看其他的核函数。另外,多项式核函数多被用于图像处理之中。