sklearn:七、支持向量机(上)—22.9.9~9.12


七、支持向量机

7.1 概述

  • 功能

用的最多的是分类,不过做其他的也有不错的效果

对于三种不同的输入数据,每种分类器的表现。可以看出SVM最棒

SVM是最接近深度学习的机器学习算法。线性SVM可以看成是神经网络的单个神经元,非线性的SVM则与两层的神经网络相当,非线性的SVM中如果添加多个核函数,则可以模仿多层的神经网络


7.1.1 支持向量机分类器是如何工作的

这是一组两种标签的数据,两种标签分别由圆和方块代表

  • 支持向量机的分类方法:是在这组分布中找出一个超平面作为决策边界,使模型在数据上的分类误差尽量接近于小,尤其是在未知数据集上的分类误差(泛化误差)尽量小

把决策边界B1向两边平移,直到碰到离这条决策边界最近的方块和圆圈后停下,形成两个新的超平面,分别是b11 和b12——>在b11 和b12中间的距离,叫做B1这条决策边界的边际,记为d,图下图所示:

——>注意,边际很小的情况,是一种模型在 训练集上表现很好,却在测试集上表现糟糕 的情况,所以会“过拟合”(如下图的红点对于B2及其边际而言)。所以我们在找寻决策边界的时候,希望边际越大越好

支持向量机,就是通过找出边际最大的决策边界,来对数据进行分类的分类器
——>支持向量分类器又叫做最大边际分类器(但是实际上并不简单)


7.1.2 支持向量机原理的三层理解

  • 目标:"找出边际最大的决策边界",SVM也是通过最小化损失函数来求解一个用于后续模型使用的重要信息:决策边界

7.1.3 sklearn中的支持向量机

注意,除了特别表明是线性的两个类LinearSVC和LinearSVR之外,其他的所有类都是同时支持线性和非线性的;SCV/SVR是最常用的;Nu则是能够限制支持向量的数目;Libsvm是一个简单、易于使用和快速有效的英文的SVM库,若熟悉这些操作,可直接从lib调用函数来进行使用


7.2 sklearn.svm.SVC

class sklearn.svm.SVC (C=1.0, kernel=’rbf’, degree=3, gamma=’auto_deprecated’, 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)

7.2.1 线性SVM用于分类的原理

(1~3) 原理部分(看网课和pdf去吧)

(4) 线性SVM决策过程的可视化

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

  1. 导入需要的模块
#下面这个包可以用于制造一堆堆的数据堆
from sklearn.datasets import make_blobs
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import numpy as np
  1. 实例化数据集,可视化数据集
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()
  1. 画决策边界:理解函数contour
    matplotlib.axes.Axes.contour([X, Y,] Z, [levels], **kwargs)

Contour是我们专门用来绘制等高线的函数。等高线的本质在二维图像上表现三维图像的一种形式 ,其中两维X和Y是两条坐标轴上的取值,而Z表示高度;Contour就是将由X和Y构成平面上的所有点中,高度一致的点连接成线段的函数,在同一条等高线上的点一定具有相同的Z值,可以用它来绘制决策边界

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

首先,我们需要获取样本构成的平面,作为一个对象

#用contour画出决策边界
#不使用上面的散点图,而是用网格图来做点,再计算每个点到超平面的距离
#首先要有散点图
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
#获取当前的子图,如果不存在,则创建新的子图
ax = plt.gca() 
  1. 画决策边界:制作网格,理解函数meshgrid
# 画决策边界:制作网格,理解函数meshgrid

#获取平面上两条坐标轴的最大值和最小值(也就是坐标轴范围)
xlim = ax.get_xlim()
ylim = ax.get_ylim()

xlim
#(-0.7425578984849813, 3.3721920271976598)
ylim
#(-0.41872382476349596, 5.754870487889891)

#在最大值和最小值之间形成30个规律/均匀的数据
#linespace的第一个参数是起始点,第二个是终止点,第三个是指定范围内取得个数
axisx = np.linspace(xlim[0],xlim[1],30)
axisy = np.linspace(ylim[0],ylim[1],30)
axisy,axisx = np.meshgrid(axisy,axisx) 
#我们将使用这里形成的二维数组作为我们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,cmap="rainbow") 
#理解函数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)

v1
#array([[1, 2, 3],
#       [1, 2, 3]])
v2
#array([[7, 7, 7],
#       [8, 8, 8]])

#通过ravel来进行降维
v1.ravel()
#array([1, 2, 3, 1, 2, 3])
v2.ravel()
#array([7, 7, 7, 8, 8, 8])

v = np.vstack([v1.ravel(), v2.ravel()]).T
#array([[1, 7],
#       [2, 7],
#      [3, 7],
#       [1, 8],
#       [2, 8],
#       [3, 8]])
#vstack就是把两个特征向量中的一个当横坐标,另一个当纵坐标,来形成点
  1. 建模,计算决策边界并找出网格上每个点到决策边界的距离
#开始建模

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

#建模,通过fit计算出对应的决策边界
#fit的本质就是在找决策边界
clf = SVC(kernel = "linear").fit(X,y) 
Z = clf.decision_function(xy).reshape(axisx.shape) 
#重要接口decision_function,返回每个输入的样本所对应的到决策边界的距离
#然后再将这个距离转换为axisx的结构,这是由于画图的函数contour要求Z的结构必须与X和Y保持一致

#画决策边界和平行于决策边界的超平面
ax.contour(axisx,axisy,Z
               ,colors="k"
               ,levels=[-1,0,1] #画三条等高线,分别是Z为-1,0和Z为1的三条线
               ,alpha=0.5
               ,linestyles=["--","-","--"]) #--是虚线,-是实线

ax.set_xlim(xlim)
ax.set_ylim(ylim)
#记得Z的本质么?是输入的样本到决策边界的距离,而contour函数中的level其实是输入了这个距离
#让我们用一个点来试试看
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")
#计算上面那个黑色点到决策边界的距离,再用contour函数画一条直线
clf.decision_function(X[10].reshape(1,2))
#array([-3.33917354])
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
ax = plt.gca()
ax.contour(axisx,axisy,Z
               ,colors="k"
               ,levels=[-3.33917354]
               ,alpha=0.5
               ,linestyles=["--"])

虚线上所有点到决策边界的距离都是-3.33917354

  1. 将绘图过程包装成函数
#将绘图过程包装成函数

#将上述过程包装成函数:
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)
        
#则整个绘图过程可以写作:
clf = SVC(kernel = "linear").fit(X,y)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
plot_svc_decision_function(clf)

只要在上面的代码中修改X,y就可以画出不同数据集分布下的决策边界

  1. 探索建好的模型
clf.predict(X) 
#根据决策边界,对X中的样本进行分类,返回的结构为n_samples
#就是在根据决策边界来进行分类
#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) 
#返回给定测试数据和标签的平均准确度
# 1.0

clf.support_vectors_
#返回支持向量
#array([[0.44359863, 3.11530945],
#       [2.33812285, 3.43116792],
#       [2.06156753, 1.96918596]])
#返回了三个支持向量所对应的横,纵坐标

clf.n_support_
#返回每个类中支持向量的个数
#array([2, 1])
  1. 推广到非线性情况
from sklearn.datasets import make_circles
#画环,会生成两个环
X,y = make_circles(100, factor=0.1, noise=.1) 

X.shape
#(100, 2)

y.shape
#(100,)

plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
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)

clf.score(X,y)
# 0.55

明显,现在线性SVM已经不适合于我们的状况了。此时,如果我们能够在原本的X和y的基础上,添加一个维度r,变成三维,我们可视化这个数据,来看看添加维度让我们的数据如何变化

  1. 为非线性数据增加维度并绘制3D图像
# 9. 为非线性数据增加维度并绘制3D图像

#定义一个由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()

此时,我们可以使用一个平面来将数据完全分开,并使平面的上方的所有数据点为一类,平面下方的所有数据点为另一类。

  1. 将上述过程放到Jupyter Notebook中运行
#10. 将上述过程放到Jupyter Notebook中运行

#如果放到jupyter notebook中运行
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import numpy as np

from sklearn.datasets import make_circles
X,y = make_circles(100, factor=0.1, noise=.1)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")

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)
        
clf = SVC(kernel = "linear").fit(X,y)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap="rainbow")
plot_svc_decision_function(clf) 

r = np.exp(-(X**2).sum(1))

rlim = np.linspace(min(r),max(r),0.2)

from mpl_toolkits import mplot3d

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()
    
from ipywidgets import interact,fixed
interact(plot_3D,elev=[0,30],azip=(-180,180),X=fixed(X),y=fixed(y))
plt.show()

此时我们的数据在三维空间中,我们的超平面就是一个二维平面。
核变换:上面做的计算r,并将r作为数据的第三维度来将数据升维的过程。即是将数据投影到高维空间中,以寻找能够将数据完美分割的超平面,即是说寻找能够让数据线性可分的高维空间——>核函数


7.2.2 非线性SVM与核函数

(1) SVC在非线性数据上的推广

看pdf,网课去


(2) 重要参数kernel

核函数可以帮我们找到合适的高纬度空间

  • 核函数能够帮助我们解决三个问题
  1. 有了核函数之后,我们无需去担心新空间究竟应该是什么样,因为非线性SVM中的核函数都是正定核函数确保了高维空间中任意两个向量的点积一定可以被低维空间中的这两个向量的某种计算来表示(多数时候是点积的某种变换);
  2. 使用核函数计算低维度中的向量关系比计算原本的新空间点积要简单太多了;
  3. 因为计算是在原始空间中进行,所以避免了维度诅咒*(因为维度太高而无法计算)的问题;

在SVC中,这个功能由参数“kernel”和一系列与核函数相关的参数来进行控制。参数“kernel"在sklearn中可选以下几种选项:

而刚才我们使用的计算r的方法,其实是高斯径向基核函数所对应的功能,在参数”kernel“中输入”rbf“就可以使用这种核函数:

#用rbf核函数来处理非线性的情况

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

可以看到,决策边界被完美地找了出来。


(3) 探索核函数在不同数据集上的表现

  1. 导入所需要的库和模块
import numpy as np
import matplotlib.pyplot as plt
#引入Colormap是为了画图更好看
from matplotlib.colors import ListedColormap
from sklearn import svm
#引用各种数据类
from sklearn.datasets import make_circles, make_moons,make_blobs,make_classification
  1. 创建数据集,定义核函数的选择
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),
        #分类是一定要指定特征个数n_features,n_informative指明带信息特征的个数,n_redundant指明不带信息特征个数
        make_classification(n_samples=n_samples,n_features = 2,n_informative=2,n_redundant=0, random_state=5)
 ]

Kernel = ["linear","poly","rbf","sigmoid"] 

#四个数据集分别是什么样子呢?
for X,Y in datasets:
        plt.figure(figsize=(5,4))
        plt.scatter(X[:,0],X[:,1],c=Y,s=50,cmap="rainbow")

我们总共有四个数据集,四种核函数,我们希望观察每种数据集下每个核函数的表现。以核函数为列,以图像分布为行,我们总共需要16个子图来展示分类结果,还有4个是原始图,共计需要20个

  1. 构建子图
nrows=len(datasets)
ncols=len(Kernel) + 1

fig, axes = plt.subplots(nrows, ncols,figsize=(20,16))
  1. 开始进行子图循环
#4. 开始进行子图循环

#第一层循环:在不同的数据集中循环;enumerate返回的是对应的索引
for ds_cnt, (X,Y) in enumerate(datasets):

        #在图像中的第一列,放置原数据的分布
        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')#zorder是让图像显示在上/下面的参数,值越大/小就越上/下面
        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')

            #绘制决策边界
            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()

这里要3和4一起运行,不然没法出图;第三行这种混杂数据,可以试着用用看决策树


(4) 探索核函数的优势和缺陷

上面所画的图要求我们事先知道数据的分布情况,但是实际情况不一定是这样的。其实rbf和poly都有自己的弊端,我们使用乳腺癌数据集作为例子来展示一下:

#其实rbf和poly都有自己的弊端,我们使用乳腺癌数据集作为例子来展示一下

from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
#时间模块,可以用来计时
from time import time
#将时间戳转为真实时间
import datetime

#实例化
data = load_breast_cancer()
X = data.data
y = data.target

X.shape
#(569,30)
np.unique(y)
#array([0,1])

plt.scatter(X[:,0],X[:,1],c=y)
plt.show()

Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)

Kernel = ["linear","poly","rbf","sigmoid"]

for kernel in Kernel:
        time0 = time()
        clf= SVC(kernel = kernel
                 , gamma="auto"
                # , degree = 1
                 , cache_size=5000
               ).fit(Xtrain,Ytrain)
        print("The accuracy under kernel %s is %f" % (kernel,clf.score(Xtest,Ytest)))
        print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))

然后我们发现,怎么跑都跑不出来。模型一直停留在线性核函数之后,就没有再打印结果了。这证明,多项式核函数此时此刻要消耗大量的时间,运算非常的缓慢。让我们在循环中去掉多项式核函数,再试试看能否跑出结果:

Kernel = ["linear","rbf","sigmoid"]

for kernel in Kernel:
        time0 = time()
        clf= SVC(kernel = kernel
                 , gamma="auto"
                # , degree = 1
                 , cache_size=5000
               ).fit(Xtrain,Ytrain)
        print("The accuracy under kernel %s is %f" % (kernel,clf.score(Xtest,Ytest)))
        print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))

输出结果为:

可以看出,linear最好,说明是个线性分布的数据集;不过线性的运行速度最慢
——>设定degree=1的多项式核函数

Kernel = ["linear","poly","rbf","sigmoid"]
for kernel in Kernel:
    time0 = time()
    clf= SVC(kernel = kernel
             , gamma="auto"
             , degree = 1
             , cache_size=5000
           ).fit(Xtrain,Ytrain)
    print("The accuracy under kernel %s is %f" % (kernel,clf.score(Xtest,Ytest)))
    print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))

为什么rbf在这里的发挥这么拉?这是因为数据的量纲问题,先来探索一下乳腺癌数据集的量纲:

import pandas as pd
data = pd.DataFrame(X)
#describe可以看到一大堆的描述性统计,用于查看数据是否有偏态
#偏态分布,是指统计数据峰值与平均值不相等的频率分布
data.describe([0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.99]).T
#量纲不统一
#数据较大的特征大都有偏态问题

可以看出上面这些数据的均值、方差,最大/小值等的分布都十分不稳定,说明其量纲不统一——>数据标准化

from sklearn.preprocessing import StandardScaler
#将所有数据转化为(0,1)间的正态分布
X = StandardScaler().fit_transform(X)
data = pd.DataFrame(X)
data.describe([0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.99]).T
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)

Kernel = ["linear","poly","rbf","sigmoid"]

for kernel in Kernel:
        time0 = time()
        clf= SVC(kernel = kernel
                 , gamma="auto"
                 , degree = 1
                 , cache_size=5000
               ).fit(Xtrain,Ytrain)
        print("The accuracy under kernel %s is %f" % (kernel,clf.score(Xtest,Ytest)))
        print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))

可以看到,运行速度和准确率都得到了很大的提升。
结论/缺点

  1. 线性核,尤其是多项式核函数在高次项时计算非常缓慢
  2. rbf和多项式核函数都不擅长处理量纲不统一的数据集

不过上面这俩问题都可以拿无量纲化来解决。因此,SVM执行之前,非常推荐先进行数据的无量纲化

加下来就可以看看相关参数了;


(5) 选取与核函数相关的参数:degree & gamma & coef0

参数gamma就是表达式中的γ,degree就是多项式核函数的次数d,参数coef0就是常数项r。其中,高斯径向基核函数受到gamma的影响,而多项式核函数受到全部三个参数的影响;

很难去界定具体每个参数如何影响了SVM的表现。我们往往避免去真正探究这些参数如何影响了我们的核函数,而直接使用学习曲线或者网格搜索来帮助我们查找最佳的参数组合;

对于高斯径向基核函数,调整gamma的方式其实比较容易,那就是画学习曲线。我们来试试看高斯径向基核函数rbf的参数gamma在乳腺癌数据集上的表现:

score = []
#返回在对数刻度上均匀间隔的数字;第一个是起始值,第二个值是终止值,第三个值是取得个数
gamma_range = np.logspace(-10, 1, 50) 

for i in gamma_range:
        clf = SVC(kernel="rbf",gamma = i,cache_size=5000).fit(Xtrain,Ytrain)
        score.append(clf.score(Xtest,Ytest))

#输出最高分数以及其对应的值
print(max(score), gamma_range[score.index(max(score))])
plt.plot(gamma_range,score)
plt.show()

通过学习曲线,很容就找出了rbf的最佳gamma值;

但对于多项式核函数来说,一切就没有那么容易了,因为三个参数共同作用在一个数学公式上影响它的效果,因此我们往往使用网格搜索来共同调整三个对多项式核函数有影响的参数。依然使用乳腺癌数据集:

#ploy调参

from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.model_selection import GridSearchCV

time0 = time()

#从前面的代码可知,degree为1时最好,所以只用调另外两个
#两个参数就用网课搜索来做
gamma_range = np.logspace(-10,1,20)
#coef不能为负数
coef0_range = np.linspace(0,5,10)

#创建一个字典
param_grid = dict(gamma = gamma_range
                  ,coef0 = coef0_range)

cv = StratifiedShuffleSplit(n_splits=5, test_size=0.3, random_state=420)
grid = GridSearchCV(SVC(kernel = "poly",degree=1,cache_size=5000), 
param_grid=param_grid, cv=cv)
grid.fit(X, y)

print("The best parameters are %s with a score of %0.5f" % (grid.best_params_, 
grid.best_score_))
print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
#The best parameters are {'coef0': 0.0, 'gamma': 0.18329807108324375} with a score of 0.96959
#00:07:088303

虽然比调参前略有提高,但依然没有超过线性核函数核rbf的结果。可见,如果最初选择核函数的时候,你就发现多项式的结果不如rbf和线性核函数,那就不要挣扎了,试试看调整rbf或者直接使用线性


7.2.3 硬间隔与软间隔:重要参数C

(1) SVM在软间隔数据上的推广

看网课,pdf

(2) 重要参数C

用于权衡”训练样本的正确分类“与”决策函数的边际最大化“两个不可同时完成的目标,希望找出一个平衡点来让模型的效果最佳;

在实际使用中,C和核函数的相关参数(gamma,degree等等)们搭配,往往是SVM调参的重点。与gamma不同,C没有在对偶函数中出现,并且是明确了调参目标的,所以我们可以 明确我们究竟是否需要训练集上的高精确度来调整C的方向。默认情况下C为1,通常来说这都是一个合理的参数。 如果我们的数据很嘈杂,那我们往往减小C。当然,我们也可以使用网格搜索或者学习曲线来调整C的值

#重要参数C

#调线性核函数
score = []
C_range = np.linspace(0.01,30,50)
for i in C_range:
        clf = SVC(kernel="linear",C=i,cache_size=5000).fit(Xtrain,Ytrain)
        score.append(clf.score(Xtest,Ytest))

print(max(score), C_range[score.index(max(score))])
plt.plot(C_range,score)
plt.show()

0.9766081871345029 1.2340816326530613

可以看出,准确率就是原来啥都没干的值。并且从学习曲线可以看出越后面值越低,基本可以确定这个就是最大值;

#换rbf
score = []
C_range = np.linspace(0.01,30,50)
for i in C_range:
    clf = SVC(kernel="rbf",C=i,gamma = 0.012742749857031322,cache_size=5000).fit(Xtrain,Ytrain)
    score.append(clf.score(Xtest,Ytest))
        
print(max(score), C_range[score.index(max(score))])
plt.plot(C_range,score)
plt.show()

0.9824561403508771 6.130408163265306

可以看出10之后就下降之后并保持平稳,一开始那里则是疯狂上升,那么再细化:

#进一步细化
score = []
C_range = np.linspace(5,7,50)
for i in C_range:
        clf = SVC(kernel="rbf",C=i,gamma = 0.012742749857031322,cache_size=5000).fit(Xtrain,Ytrain)
        score.append(clf.score(Xtest,Ytest))
        
print(max(score), C_range[score.index(max(score))])
plt.plot(C_range,score)
plt.show()

0.9824561403508771 5.938775510204081

此时,我们找到了乳腺癌数据集上的最优解:rbf核函数下的98.24%的准确率。我们还可以用交叉验证来进一步优化;


7.3 总结

向量机的运用要对数据进行无量纲化预处理,可以对结果有着很大的提升。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容