Python 解决线性及非线性SVM决策过程的可视化

关注微信公共号:小程在线

关注CSDN博客:程志伟的博客

使用sklearn中的式子来为可视化我们的决策边界,支持向量,以及决策边界平行的两个超平面。

 

1. 导入需要的模块

from sklearn.datasets import make_blobs
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import numpy as np

 

2. 实例化数据集,可视化数据集

X,y = make_blobs(n_samples=50, centers=2, random_state=0,cluster_std=0.6)

plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
plt.xticks([])
plt.yticks([])
plt.show()

可以看出数据的分类呈现出2类

 

3. 画决策边界:理解函数contour
matplotlib.axes.Axes.contour([X, Y,] Z, [levels], **kwargs)Contour是我们专门用来绘制等高线的函数。等高线,本质上是在二维图像上表现三维图像的一种形式,其中两维X和Y是两条座标轴上的取值,而Z表示高度。Contour就是将由X和Y构成平面上的所有点中,高度一致的点连接成线段的函数,在同一条等高线上的点一定具有相同的Z值。我们可以利用这个性质来绘制我们的决策边界。

参数 含义
X,Y 选填。两维平面上所有的点的横纵座标取值,一般要求是二维结构并且形状需要与Z相同,往往通
过numpy.meshgrid()这样的函数来创建。如果X和Y都是一维,则Z的结构必须为(len(Y), len(X))。
如果不填写,则默认X = range(Z.shape[1]),Y = range(Z.shape[0])。
Z 必填。平面上所有的点所对应的高度。
levels 可不填,不填默认显示所有的等高线,填写用于确定等高线的数量和位置。如果填写整数n,则显
示n个数据区间,即绘制n+1条等高线。水平高度自动选择。如果填写的是数组或列表,则在指定
的高度级别绘制等高线。列表或数组中的值必须按递增顺序排列

我们只需要在我们的样本构成的平面上,把所有到决策边界的距离为0的点相连,就是我们的决策边界,而把所有到决策边界的相对距离为1的点相连,就是我们的两个平行于决策边界的超平面了。此时,我们的Z就是平面上的任意点到达超平面的距离。

plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
ax = plt.gca() #获取当前的子图,如果不存在,则创建新的子图

4. 画决策边界:制作网格,理解函数meshgrid
#获取平面上两条座标轴的最大值和最小值
xlim = ax.get_xlim()
ylim = ax.get_ylim()

 

#在最大值和最小值之间形成30个规律的数据

axisx = np.linspace(xlim[0],xlim[1],30)
axisy = np.linspace(ylim[0],ylim[1],30)
axisx,axisy = np.meshgrid(axisx,axisy)

#我们将使用这里形成的二维数组作为我们contour函数中的X和Y
#使用meshgrid函数将两个一维向量转换为特征矩阵
#核心是将两个特征向量广播,以便获取y.shape * x.shape这么多个座标点的横座标和纵座标

 

 

xy = np.vstack([axisx.ravel(), axisy.ravel()]).T
#其中ravel()是降维函数,vstack能够将多个结构一致的一维数组按行堆叠起来
#xy就是已经形成的网格,它是遍布在整个画布上的密集的点
plt.scatter(xy[:,0],xy[:,1],s=1);

可以看出一共有900个点

a = np.array([1,2,3])
b = np.array([7,8])

a,b
Out[8]: (array([1, 2, 3]), array([7, 8]))

v1,v2 = np.meshgrid(a,b)
v1
Out[9]: 
array([[1, 2, 3],
       [1, 2, 3]])

v2
Out[10]: 
array([[7, 7, 7],
       [8, 8, 8]])

v = np.vstack([v1.ravel(), v2.ravel()]).T
v
Out[11]: 
array([[1, 7],
       [2, 7],
       [3, 7],
       [1, 8],
       [2, 8],
       [3, 8]])

 

5. 建模,计算决策边界并找出网格上每个点到决策边界的距离

有了网格后,我们需要计算网格所代表的“平面上所有的点”到我们的决策边界的距离。所以我们需要我们的模型和决策边界。
建模,计算决策边界并找出网格上每个点到决策边界的距离:
#建模,通过fit计算出对应的决策边界
clf = SVC(kernel = "linear").fit(X,y)
Z = clf.decision_function(xy).reshape(axisx.shape)

#重要接口decision_function,返回每个输入的样本所对应的到决策边界的距离
#然后再将这个距离转换为axisx的结构,这是由于画图的函数contour要求Z的结构必须与X和Y保持一致
#画决策边界和平行于决策边界的超平面

plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
ax = plt.gca() #获取当前子图
ax.contour(axisx,axisy,Z
    ,colors="k"
    ,levels=[-1,0,1] #画三条等高线,分别是Z为-1,Z为0和Z为1的三条线
    ,alpha=0.5
    ,linestyles=["--","-","--"])
ax.set_xlim(xlim)
ax.set_ylim(ylim)
plt.show()

 

Z的本质么是输入的样本到决策边界的距离,而contour函数中的level其实是输入了这个距离,现在让我们找一个点来试试
#以第10号样本为例,以黑色表示

plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
plt.scatter(X[10,0],X[10,1],c="black",s=100);

 

#计算第10号样本到决策边界的距离

clf.decision_function(X[10].reshape(1,2))
Out[15]: array([-3.33917354])

 

#绘制决策边界

plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
plt.scatter(X[10,0],X[10,1],c="black",s=100)
ax = plt.gca()
ax.contour(axisx,axisy,Z
            ,colors="k"
            ,levels=[-3.33917354]
            ,alpha=0.5
            ,linestyles=["--"]);

6. 将绘图过程包装成函数
#将上述过程包装成函数:
def plot_svc_decision_function(model,ax=None):
    if ax is None:
        ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()
    x = np.linspace(xlim[0],xlim[1],30)
    y = np.linspace(ylim[0],ylim[1],30)
    Y,X = np.meshgrid(y,x)
    xy = np.vstack([X.ravel(), Y.ravel()]).T
    P = model.decision_function(xy).reshape(X.shape)
    ax.contour(X, Y, P,colors="k",levels=[-1,0,1],alpha=0.5
               ,linestyles=["--","-","--"])
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)
    plt.show()

clf = SVC(kernel = "linear").fit(X,y)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
plot_svc_decision_function(clf)

 

7. 探索建好的模型
#根据决策边界,对X中的样本进行分类,返回的结构为n_samples

clf.predict(X)
Out[19]: 
array([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])

 

#返回给定测试数据和标签的平均准确度

clf.score(X,y)
Out[20]: 1.0

 

#返回支持向量

clf.support_vectors_
Out[21]: 
array([[0.44359863, 3.11530945],
       [2.33812285, 3.43116792],
       [2.06156753, 1.96918596]])

 

#返回每个类中支持向量的个数

clf.n_support_
Out[22]: array([2, 1])

 

8. 推广到非线性情况

from sklearn.datasets import make_circles
#创建环形数据
X,y = make_circles(100, factor=0.1, noise=.1, random_state=10)
X.shape
Out[23]: (100, 2)

y.shape
Out[24]: (100,)

plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
Out[25]: <matplotlib.collections.PathCollection at 0x1e4f5b69b00>

clf = SVC(kernel = "linear").fit(X,y)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
plot_svc_decision_function(clf)

现在线性SVM已经不适合于我们的状况了,我们无法找出一条直线来划分我们的数据集,让直线的两边分别
是两种类别。这个时候,如果我们能够在原本的X和y的基础上,添加一个维度r,变成三维,我们可视化这个数
据,来看看添加维度让我们的数据如何变化。



9. 为非线性数据增加维度并绘制3D图像
from mpl_toolkits import mplot3d #导入3D绘图包
#定义一个由x计算出来的新维度r
r = np.exp(-(X**2).sum(1))
#定义一个绘制三维图像的函数
#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()


 


 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章