机器学习——支持向量机

支持向量机

1 SVM简介

在机器学习领域,SVM是一个有监督的学习模型,通常用来进行模式识别、分类以及回归分析。其由简至繁的模型包括:

  1. 当训练样本线性可分时,通过硬间距最大化,学习一个线性可分支持向量机;
  2. 当训练样本近似线性可分时,通过软间距最大化,学习一个线性支持向量机;
  3. 当训练样本线性不可分时,通过核技巧和软间距最大化,学习一个非线性支持向量机。

SVM的基本思想是:建立一个最优决策超平面,使得该平面两侧距离平面最近的两类样本之间的距离最大化,从而对分类问题提供良好的泛化能力。 即是指,当样本点的分布无法用一条直线或者几条直线分开时(线性不可分),SVM提供一种算法,求出一个曲面用于划分。这个曲面就是最优决策超平面。而且,SVM采用二次优化,因此最优解是唯一的,且全局最优。前文提到的距离最大化就是说,这个曲面让不同分类的样本点距离最远,即求最优分类超平面等价于求最大间隔。

SVM的原理大致分为:假设要把方形和圆点分为两类,那么无数多条线可以完成这个任务,在SVM中,我们需要寻找一条最优的分界线使得它到两边的边界都最大,在这种情况下边缘的几个数据点就叫做支持向量,这也是这个分类算法的由来。如下图所示:

在这里插入图片描述

2 线性可分支持向量机

2.1 线性可分支持向量机

给定线性可分训练数据集,通过间距最大化或等价地求解相应的凸二次规划问题学习得到的分离超平面wx+b=0,相应的分类决策函数f(x)=sig(wx+b)称为线性可分支持向量机。

如下图所示,这时有许多超平面能将两类数据正确划分,线性可分支持向量机的目的就是从中找到最佳的超平面,使得预测新数据时有较好的的表现。

在这里插入图片描述
以二维空间为例,相对于把超平面wx+b=0理解为一条平面直线y=kx+b,将其理解为空间平面z=ax+by+c与平面z=0的交线。将训练数据集中的样本点代入wx+b,得到的值表示平面空间z=ax+by+c上的点与z=0之间的距离,距离为正则样本为正例,距离为负则为负例,如下图所示。

在这里插入图片描述

2.2 函数间距和几何间距

如下图所示,其中x表示正例,圆圈表示负例。直线就是决策边界(它的方程表示为θ^T x=0),或者叫做分离超平面。

在这里插入图片描述
对于图中的A点来说,它距离决策边界很远。如果要预测一下A点对应的y值,应该很确定地说y=1.反过来对于C点来说,它距离决策边界很近。虽然它决策边界的上方,但是只要决策边界稍有改变,它就有可能在决策边界的下方。所以想比较而言,我们对于预测A点的自信要比C点高。函数间距和几何间距的提出,就是为找到一个最佳的超平面提供了依据。

2.2.1 函数间距

点x到直线的距离可以表示为L=β‖x‖。对于一个训练样本(x,y),定义相应的距离函数为:

在这里插入图片描述
注意,前面乘上类别y之后可以保证这个边界的非负性,因为g(x)>0对应y=-1的那些点。

所以,如果y=1,为了让函数间隔比较大(预测的确信度就大),需要wx+b是一个大的正数。反过来y=-1,为了让函数间隔比较大,就需要wx+b是一个大的负数。

接着就是要找所有点中间距离最小的点了。对于给定的数据集S=(x ^(i), y ^(i); i=1,2,···,m),定义γ是数据集中函数间距最小的,即:

在这里插入图片描述
但这里有一个问题就是,函数间距可以表示分类预测的正确性及确信度,但选择分离超平面时,只有函数间距还不够,对于函数间距来说,当w和b被替换成2w和2b时,有:

在这里插入图片描述
也就是说,此时超平面并没有改变,但是函数间距却变成了原来的2倍。为此引入了几何间距。

2.2.2 几何间距

在这里插入图片描述
如上图所示,直线为决策边界(由w,b决定)。向量w垂直于直线,这是因为θ^Tx=0,非零向量的内积为0,说明它们互相垂直。假设A点代表样本x,它的类别为y=1。假设A点到决策边界的距离为γ,也就是线段AB。那么,应该如何计算γ?

首先我们知道w/‖w‖表示的是在w方向上的单位向量。因为点A代表样本点x,所以B点为:

在这里插入图片描述
又因为B点是在决策边界上,所以B点满足w^Tx+b=0,也就是:

在这里插入图片描述
解方程得:
在这里插入图片描述
当然,上面这个方程对应的是正例的情况,负例时候上边方程的解就是一个负数,这与我们平常说的距离不相符,所以要乘上y:

在这里插入图片描述
可以看到当‖w‖=1时,函数间距与几何间距就是一样的了。

同样,有了几何间距的定义,接着就是寻找中间最小的点。对于给定的数据集S=(x ^(i), y ^(i); i=1,2,···,m),定义γ是数据集中函数间距最小的,即:

在这里插入图片描述
超平面(w,b)关于样本点(x,y)的几何距离一般是实例点到超平面的带符号距离,当样本点被超平面正确分类时就是实例点到超平面的距离。

讨论到这里,对于一组训练集,要找的就是看看哪个超平面的最近点的间距最大,因为这样的确信度是最大的,所以现在的问题就是:

在这里插入图片描述
在这里插入图片描述
这个问题就是说,我们想要最大化这个间距γ,而且必须保证每个训练集得到的间距都要大于或等于这个间距。‖w‖=1保证函数间距和几何间距是一样的。但问题是,‖w‖=1很难理解,所以根据函数距离与几何距离之间的关系将问题转化为:

在这里插入图片描述
在这里插入图片描述

此处,我们的目标是最大化γ/‖w‖,限制条件为所有样本的函数间距要大于或等于γ。

前面说过,对于函数间距来说,等比例缩放w和b不会改变超平面。因此可以令γ等于1,因为无论γ的值是多少,都可以通过缩放w和b来使γ变成1。所以最大化1/‖w‖。

2.3 间距最大化

支持向量机学习的基本思想是求解能够正确划分训练数据集并且几何间距最大的分离超平面。对于线性可分的数据集而言,线性可分分离超平面有无穷多个,但是几何间距最大的是唯一的。这里的间距最大化又称为硬间距最大化。

间距最大化的直观解释是:对训练数据集找到几何间距最大的超平面意味着以充分大的确信度训练数据进行分类。也就是说,不仅将正负实例点分开,而且对最难分的实例点也有足够大的确信度将它们分开。这样的超平面应该对未知的新实例有很好的分类预测能力。

其实对于上文所述的问题,如果那些式子都除一个γ,即变为:

在这里插入图片描述
在这里插入图片描述
最后令w=w/γ,b=b/γ,而最大化1/‖w‖相当于最小化‖w‖^2,所以问题就变为:

在这里插入图片描述
在这里插入图片描述

现在,问题就已经转换成了可以有效求解的问题了。上面的优化问题就是一个典型的二次凸优化问题,这种问题使用Quadratic Programming来求解。但是上面的问题有着特殊的结构,通过拉格朗日对偶变换到对偶变量的优化问题之后,可以找到一种更加有效的方法来进行求解,而且通常情况下这种方法比直接使用DP更高效。

2.4 支持向量和间距边界

在线性可分情况下,训练数据集样本点中与分离超平面最近的样本点的实例称为支持向量。支持向量是使约束条件成立的点,即:

在这里插入图片描述

如图所示,在H1和H2上的点就是支持向量。

在这里插入图片描述
注意到H1和H2平行,并且没有实例点落在它们之间。在H1和H2中间形成的一条长带,分离超平面与它们平行且位于它们中央,长带的宽度称为间距,而H1和H2就是间距边界。

在决定分离超平面时只有支持向量起作用,而其他实例点并不起作用。如果移动支持向量将改变所求的解,但是如果在间距边界以外移动其他实例点,甚至去掉这些点是不会改变解得结果的。

2.5 实验

下面是一个简单的线性可分的例子,这里直接使用sklearn.datasets.make_blobs 生成数据。n_samples=50 表示取50个点,centers=2表示将数据分为两类生成数据代码如下:

from sklearn.datasets.samples_generator import make_blobs
from matplotlib import pyplot as plt

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='autumn')
plt.show()

在这里插入图片描述
我们尝试绘制分离两组数据的直线,从而创建分类模型,对于这里所示的数据,这是我们可以手动完成的任务。但是立马可以看出有很多分界线可以完美的区分两个类。下面画出决策边界:

xfit = np.linspace(-1, 3.5)
plt.plot([0.6], [2.1], 'x', color='red', markeredgewidth=2, markersize=10)
 
for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]:
    plt.plot(xfit, m * xfit + b, '-k')
 
plt.show()

在这里插入图片描述
注意,这三条直线是我随便画的,其实可以使用Logistic回归、线性回归等分类画出线。这里是三条不同的分割直线,并且这些分割直线能够完全区分这些样例。但是根据支持向量机的思想,哪一条直线是最优的分割线呢?支持向量机并不是简单的绘制一条直线,而是画出边距为一定宽度的直线,直到最近的点。下面我们对直线进行加粗,代码如下:

plt.fill_between(xfit, yfit - d, yfit + d, edgecolor='none',
                     color='#AAAAAA', alpha=0.4)  # alpha为透明度

在这里插入图片描述
在支持向量机中,边距最大化的直线是我们将选择的最优模型。支持向量机是这种最大边距估计器的一个例子。接下来,我们训练一个基本的SVM,我们使用sklearn的支持向量机,对这些数据训练SVM模型。目前我们将使用一个线性核并将C参数设置为一个默认的数值。如下:

from sklearn.svm import SVC  
 
model = SVC(kernel='linear') 
model.fit(X, y)

我们顺便看看SVC的所有参数情况:

SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
    kernel='linear', max_iter=-1, probability=False, random_state=None,
    shrinking=True, tol=0.001, verbose=False)

为了更好展现这里发生的事情,下面我们创建一个辅助函数,为我们绘制SVM的决策边界:

def plot_SVC_decision_function(model, ax=None, plot_support=True):
    '''Plot the decision function for a 2D SVC'''
    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=['--', '-', '--']) 
 
    if plot_support:
        ax.scatter(model.support_vectors_[:, 0],
                   model.support_vectors_[:, 1],
                   s=300, linewidth=1, facecolors='none')
 
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)

下面绘制决策边界:

def train_SVM():
    X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)
 
    model = SVC(kernel='linear')
    model.fit(X, y)
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(model)
    plt.show()
    return X, y

在这里插入图片描述
这是最大化两组点之间的间距的分界线,那中间这条线就是我们最终的决策边界了。其中一些训练点碰到了边缘,如图所示,在两个边界上包含两个红点和一个黄点,这三个点就是支持向量,是 alpha 值不为零的。在sklearn中,这些点存储在分类器的 support_vectors_ 属性中。

在支持向量机只有位于支持向量上面的点才会对决策边界有影响,也就是说不管有多少的点是非支持向量,那对最终的决策边界都不会产生任何影响。我们可以看到这一点,例如,如果我们绘制该数据集的前 60个点和前120个点获得的模型:

def plot_svm(N=10, ax=None):
    X, y = make_blobs(n_samples=200, centers=2, random_state=0, cluster_std=0.6)
    X, y = X[:N], y[:N]
    model = SVC(kernel='linear')
    model.fit(X, y)
 
    ax = ax or plt.gca()
    ax.scatter(X[:, 0], X[:, 1], c=y, cmap='autumn')
    ax.set_xlim(-1, 4)
    ax.set_ylim(-1, 6)
    plot_SVC_decision_function(model, ax)
 
if __name__ == '__main__':
    # train_SVM()
    fig, ax = plt.subplots(1, 2, figsize=(16, 6))
    fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
    for axi, N in zip(ax, [60, 120]):
        plot_svm(N, axi)
        axi.set_title('N = {0}'.format(N))

在这里插入图片描述
上面就是我们绘制的该数据集前60个点和前120个点获得的模型,可以发现无论使用60,还是使用120个数据点,决策边界都没有发生变换,所有只要支持向量没变,其他的数据怎么加都无所谓。

这个分类器成功的关键在于:为了拟合,只有支持向量的位置是最重要的;任何远离边距的点都不会影响拟合的结果,边界之外的点无论有多少都不会对其造成影响,也就是说不管有多少点是非支持向量,对最终的决策边界都不会产生任何影响。

3 线性支持向量机

线性支持向量机是针对线性不可分的数据集的,这样的数据集通过近似可分的方法实现分类。对于这样的数据集,类似线性可分支持向量机,通过求解对应的凸二次规划问题,同样求得分离超平面:

在这里插入图片描述
以及相应的分类决策函数:

在这里插入图片描述

3.1 线性支持向量机的原理

线性不可分意味着某些样本点(x,y)不能满足函数间距大于或等于1的约束条件。为了解决这个问题,可以对每个样本点引入一个松弛变量ξ≥0,使函数间距加上松弛变量大于等于1。

设置惩罚参数C,一般由应用问题决定,C值大时对误分类的惩罚增大,C值小时对误分类的惩罚减小。最小化目标函数有两层含义:使1/2‖w‖^2尽量小,即间距尽量大,同时使误分类的点的个数尽量少,C即使调合二者的系数。

有了上面的思路,可以和训练数据集线性可分时一样考虑相应的间距最大化,线性不可分的线性支持向量机的学习问题变成如下凸二次规划。

线性支持向量机的原始问题:

在这里插入图片描述
在这里插入图片描述

接着的问题就变成如何求解一个最优化问题(称为原始问题)。

引入拉格朗日函数:

在这里插入图片描述
此时原始问题就变成:

在这里插入图片描述
利用拉格朗日函数的对偶性,将问题变成一个极大极小优化问题:

在这里插入图片描述
首先求解minL(w,b,ξ,α,μ),将拉格朗日函数分别对w,b,ξ求偏导,并令其为0:

在这里插入图片描述

即为:

在这里插入图片描述

将其代入拉格朗日函数,即有:

在这里插入图片描述

第二步求maxL(w,b,ξ,α,μ),即求:

在这里插入图片描述
在这里插入图片描述
由C-αi-μi=0得知0≦αi≤C,因为在第二步求极大值的过程中,函数至于α有关。

最后将上述的极大值问题转化为极小值问题:

在这里插入图片描述
在这里插入图片描述

这就是原始问题的对偶问题。

3.2 线性支持向量机的过程

  1. 设置惩罚参数C,并求解对偶问题。

在这里插入图片描述在这里插入图片描述

  1. 计算原始问题的最优解,并选择α* 中满足0的分量,计算b*。 在这里插入图片描述 在这里插入图片描述
  2. 求分离超平面和分类决策边界。
    分离超平面为:在这里插入图片描述
    分类决策函数为:在这里插入图片描述

3.3 支持向量

在线性不可分情况下,将对偶问题的解对应于样本点的实例称为支持向量。如下图所示,这时的支持向量要比线性可分的情况复杂一些。

在这里插入图片描述
图中,分离超平面由实线表示,间距边界由虚线表示。软间隔的支持向量或在间隔边界上,或在间隔边界与分离超平面之间,或者在分超平面误分一侧。

3.4 合页损失函数

对于线性支持向量机学习来说,其模型为分离超平面及决策函数,但还有另一种解释,就是最小化以下目标函数:

在这里插入图片描述
目标函数的第一项就是经验损失或经验风险,函数为合页损失函数,其应该带有下标,若下标取“+”则表示取正值的函数。

这就是说,当样本点被正确分类且确信度大于1时,损失为0,否则损失为1-y(wx+b)。

如图所示,横轴是函数间距y(wx+b),纵轴是损失,由于函数形状像一个合页,故称为合页损失函数。图中还画出了0-1损失函数,可以认为它是二分类问题真正的损失函数,而合页损失函数是0-1损失函数的上界。由于0-1损失函数不是连续可导的,直接优化由其构成的目标函数比较困难,可以认为线性支持向量机是优化由0-1损失函数的上界构成的目标函数,这时上界损失函数又称为代理损失函数。

在这里插入图片描述
图中虚线显示的是感知机的损失函数。这时,当样本点被正确分类时,损失是0,否则损失是-y(wx+b)。相比之下,合页损失函数不仅要正确分类,而且确信度足够高时损失才是0,也就是说合页损失函数对学习有更高的要求。

3.5 实验

SVM模型有两个非常重要的参数C与gamma,其中C是惩罚系数,即对误差的宽容忍,C越高,说明越不能容忍出现误差,容易过拟合。C越小,容易欠拟合。C过大或过小,泛化能力变差。

gamma 是选择 RBF 函数作为kernel后,该函数自带的一个参数。隐含的决定了数据映射到新的特征空间后的分布,gamma越大,支持向量越小,gamma值越小,支持向量越多。

下面我们分别调剂一下C和gamma来看一下对结果的影响。首先我们调节C,先做一个有噪音的数据分布:

X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)
 
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')

在这里插入图片描述
上面的分布看起来要划分似乎有点困难,所以我们可以进行调整看看:

fig, ax = plt.subplots(1, 2, figsize=(16, 6))
fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
 
for axi, C in zip(ax, [10.0, 0.1]):
    model = SVC(kernel='linear', C=C)
    model.fit(X, y)
    axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(model, axi)
    axi.scatter(model.support_vectors_[:, 0],
                model.support_vectors_[:, 1],
                s=300, lw=1, facecolors='none')
    axi.set_title('C={0:.1f}'.format(C), size=14)

在这里插入图片描述
可以看到左边这幅图C值比较大,要求比较严格,不能分错东西,隔离带中没有进入任何一个点,但是隔离带的距离比较小,泛化能力比较差。右边这幅图C值比较小,要求相对来说比较松一点,隔离带较大,但是隔离带中进入了很多的黄点和红点。那么C大一些好还是小一些好呢?这需要考虑实际问题,可以进行K折交叉验证来得到最合适的C值。

下面再看看另一个参数gamma值,这个参数值只是在高斯核函数里面才有,这个参数控制着模型的复杂程度,这个值越大,模型越复杂,值越小,模型就越精简。代码如下:

for axi, gamma in zip(ax, [10.0, 1.0, 0.1]):
    model = SVC(kernel='rbf', gamma=gamma)
    model.fit(X, y)
    axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(model, axi)
    axi.scatter(model.support_vectors_[:, 0],
                model.support_vectors_[:, 1],
                s=300, lw=1, facecolors='none')
    axi.set_title('gamma={0:.1f}'.format(gamma), size=14)

在这里插入图片描述
可以看出,当这个参数较大时,可以看出模型分类效果很好,但是泛化能力不太好。当这个参数较小时,可以看出模型里面有些分类是错误的,但是这个泛化能力更好,一般也应有的更多。

4 非线性支持向量机

如果训练输入线性不可分,可以使用非线性支持向量机,利用核技巧将输入空间非线性问题转化为特征空间线性可分问题。

4.1 核技巧

非线性分类问题是指通过利用非线性模型才能很好地进行分类的问题。如下图所示,在这个分类问题中,无法用直线将正负例正确分开,但可以用一条椭圆曲线来将它们正确分开。

在这里插入图片描述
一般来说,对给定的一个训练数据集T={(x1,y1),(x2,y2),···,(xn,yn)},其中实例xi属于输入空间,

在这里插入图片描述
对应的标记有两类

在这里插入图片描述
如果能用R中的一个超曲面将正负例分开,则称这个问题为非线性可分问题。

非线性问题往往不好求解,所以希望能用解线性分类问题的方法解决这个问题。所采取的是进行一个线性变换,将非线性问题变换为线性问题,通过解变换后的线性问题的方法求解原来的非线性问题。

设原空间为:

在这里插入图片描述
新空间为:

在这里插入图片描述
定义一个从原空间到新空间的变换:

在这里插入图片描述
经过变换后,原空间中的点相应的变换为新空间中的点,原空间中的椭圆:

在这里插入图片描述
变换为新空间中的直线:

在这里插入图片描述
这样,在变换后的新空间中直线可以将正负实例点正确分开,即原来的非线性可分问题就变成了新空间的线性可分问题。

上面的例子说明用线性分类方法求解非线性分类问题分为两步:首先使用一个变换将原空间的数据映射到新空间;然后在新空间中用线性分类学习方法从训练数据中学习分类模型。核技巧就属于这样的方法。

核技巧应用到支持向量机,其基本思想就是通过一个非线性变换将输入空间对应于一个特征空间,使得在输入空间中的超曲面模型对应于特征空间中的超平面模型。这样,分类问题的学习任务通过在特征空间中求解线性支持向量机就可以完成。

核函数定义如下:设χ是输入空间,又设Η为特征空间,如果存在一个从χ到Η的映射:

在这里插入图片描述
使得对所有的x,z∈χ,函数K(x,z)满足条件:

在这里插入图片描述
则K(x,z)为核函数,φ(x)为映射函数。

核技巧的想法是,在学习与预测中只定义核函数K(x,z),而不是显式地定义映射函数φ。通常,直接计算K(x,z)比较容易,而通过φ(x)和φ(z)计算K(x,z)并不容易。注意,φ是输入空间到特征空间的映射,特征空间一般是高维的甚至无穷维。可以看到,对于给定的核K(x,z),特征空间和映射函数的取法并不唯一,可以取不同的特征空间,即便是在同一特征空间里也可以取不同的映射。

4.2 核技巧在支持向量机中的应用

我们注意到在线性支持向量机的对偶问题中,无论是目标函数还是决策函数都只涉及输入实例与实例之间的内积。在对偶问题目标函数中的内积可以用核函数来代替,此时对偶问题的目标函数成为:

在这里插入图片描述
同样,分类决策函数中的内积也可以用核函数代替,而分类决策函数式成为:

在这里插入图片描述
这等价于经过映射函数将原来的输入空间变换到一个新的特征空间,将输入空间中的内积变换成特征空间中的内积,在新的特征空间里从训练样本中学习线性支持向量机。当映射函数是非线性函数时,学习到的含有核函数的支持向量机是非线性分类模型。

4.3 常用核函数

1)多项式核函数:

在这里插入图片描述
对应的支持向量机是一个p次多项式分类器,在此情形下,分类决策函数成为:

在这里插入图片描述
2)高斯核函数:

在这里插入图片描述
对应的支持向量机是高斯径向基函数分类器,在此情形下,分类决策函数成为:

在这里插入图片描述

4.4 非线性支持向量机算法

如上所述,利用核技巧,可以将线性分类的学习方法应用到非线性分类问题中去。将线性支持向量机扩展到非线性支持向量机,只需要线性支持向量机对偶性始终的内积换成核函数。

从非线性分类训练集,通过核函数与软间距最大化,或凸二次规划,学习得到的分类决策函数

在这里插入图片描述
称为非线性支持向量机,K(x,z)是正定核函数。

首先选取适当的核函数K(x,z)和适当的参数C,构造并求解最优化问题:

在这里插入图片描述在这里插入图片描述
这样,求得最优解:

在这里插入图片描述
选择α* 的一个正分量0<αj*<C,计算:

在这里插入图片描述
最后构造决策函数:

在这里插入图片描述
当K(x,z)是正定核函数时,该问题为凸二次规划问题,解是存在的。

4.5 实验

首先我们导入一个线性不可分的数据集:

def train_svm_plus():
    X, y = make_circles(100, factor=0.1, noise=0.1)
     
    clf = SVC(kernel='linear')
    clf.fit(X, y)
     
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(clf, plot_support=False)

在这里插入图片描述
很明显,用线性分类器无论怎么画线也不能分好,那咋办呢?下面试试高斯核变换吧。在进行核变换之前,先看看数据在高维空间下的映射:

def plot_3D(X, y, elev=30, azim=30):
    r = np.exp(-(X ** 2).sum(1))
    ax = plt.subplot(projection='3d')
    ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
    ax.view_init(elev=elev, azim=azim)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
 
 
if __name__ == '__main__':
    X, y = train_svm_plus()
    plot_3D(elev=30, azim=30, X=X, y=y)

在这里插入图片描述
引入径向基函数,进行核变换,再进行分类:

SVC(C=1000000.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
    kernel='rbf', max_iter=-1, probability=False, random_state=None,
    shrinking=True, tol=0.001, verbose=False)
def train_svm_plus():
    X, y = make_circles(100, factor=0.1, noise=0.1)

    clf = SVC(kernel='rbf')
    clf.fit(X, y)
 
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(clf, plot_support=False)
    plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
                s=300, lw=1, facecolors='none')
    return X, y

在这里插入图片描述
可以清楚的看到效果很好,我们将线性不可分的两对数据分割开来。

自己构造的非线性数据如下:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_moons, make_circles
from sklearn.preprocessing import StandardScaler
 
X1D = np.linspace(-4, 4, 9).reshape(-1, 1)
X2D = np.c_[X1D, X1D ** 2]
y = np.array([0, 0, 1, 1, 1, 1, 1, 0, 0])
 
plt.figure(figsize=(11, 4))
 
plt.subplot(121)
plt.grid(True, which='both')
plt.axhline(y=0, color='k')
plt.plot(X1D[:, 0][y == 0], np.zeros(4), 'bs')
plt.plot(X1D[:, 0][y == 1], np.zeros(5), 'g*')
plt.gca().get_yaxis().set_ticks([])
plt.xlabel(r'$x_1$', fontsize=20)
plt.axis([-4.5, 4.5, -0.2, 0.2])
 
plt.subplot(122)
plt.grid(True, which='both')
plt.axhline(y=0, color='k')
plt.axvline(x=0, color='k')
plt.plot(X2D[:, 0][y == 0], X2D[:, 1][y == 0], 'bs')
plt.plot(X2D[:, 0][y == 1], X2D[:, 1][y == 1], 'g*')
plt.xlabel(r'$x_1$', fontsize=20)
plt.ylabel(r'$x_2$', fontsize=20, rotation=0)
plt.gca().get_yaxis().set_ticks([0, 4, 8, 12, 16])
plt.plot([-4.5, 4.5], [6.5, 6.5], 'r--', linewidth=3)
plt.axis([-4.5, 4.5, -1, 17])
 
plt.subplots_adjust(right=1)
plt.show()

在这里插入图片描述
从这个图可以看到,我们利用对数据的变换,可以对数据的维度增加起来,变成非线性。

假设我们不使用核函数的思想,先对数据做变换,看能不能达到一个比较好的结果,首先我们做一个测试的数据,代码如下:

X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
 
def plot_dataset(X, y, axes):
    plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs')
    plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g*')
    plt.axis(axes)
    plt.grid(True, which='both')
    plt.xlabel(r'$x_1$', fontsize=20)
    plt.ylabel(r'$x_2$', fontsize=20, rotation=0)
 
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()

在这里插入图片描述
下面代码将两类数据分出来:

Polynomial_svm_clf = Pipeline((('poly_features', PolynomialFeatures(degree=3)),
                               ('scaler', StandardScaler()),
                               ('svm_clf', LinearSVC(C=10))
                               ))
Polynomial_svm_clf.fit(X, y)
 
def plot_predictions(clf, axes):
    x0s = np.linspace(axes[0], axes[1], 100)
    x1s = np.linspace(axes[2], axes[3], 100)
    x0, x1 = np.meshgrid(x0s, x1s)
    X = np.c_[x0.ravel(), x1.ravel()]
    y_pred = clf.predict(X).reshape(x0.shape)

    plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
 
plot_predictions(Polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()

在这里插入图片描述
从结果来看,我们使用线性支持向量机将两类数据区分开是没有问题的。而最重要的是我们如何使用核函数呢?

首先取核函数为多项式核函数看看效果:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC, SVC
 
X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
 
def plot_dataset(X, y, axes):
    plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs')
    plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g*')
    plt.axis(axes)
    plt.grid(True, which='both')
    plt.xlabel(r'$x_1$', fontsize=20)
    plt.ylabel(r'$x_2$', fontsize=20, rotation=0)

def plot_predictions(clf, axes):
    x0s = np.linspace(axes[0], axes[1], 100)
    x1s = np.linspace(axes[2], axes[3], 100)
    x0, x1 = np.meshgrid(x0s, x1s)
    X = np.c_[x0.ravel(), x1.ravel()]
    y_pred = clf.predict(X).reshape(x0.shape)

    plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
 
 
Poly_kernel_svm_clf = Pipeline((('scaler', StandardScaler()),
                                ('svm_clf', SVC(kernel='poly', degree=3, coef0=1, C=5))
                                ))
Poly_kernel_svm_clf.fit(X, y)

Poly_kernel_svm_clf_plus = Pipeline((('scaler', StandardScaler()),
                                     ('svm_clf', SVC(kernel='poly', degree=10, coef0=1, C=5))
                                     ))
Poly_kernel_svm_clf_plus.fit(X, y)
 
plt.subplot(121)
plot_predictions(Poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.title(r'$d=3, r=1, C=5$', fontsize=18)
 
plt.subplot(122)
plot_predictions(Poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.title(r'$d=10, r=100, C=5$', fontsize=18)
plt.show()

在这里插入图片描述
我们是把数据映射到高维空间,然后再拿回来看效果,实际上并没有去高维空间做运算。这就是我们想要展示的多项式核函数,下面学习高斯核函数。

rbf_kernel_svm_clf = Pipeline((('scaler', StandardScaler()),
                               ('svm_clf', SVC(kernel='rbf', gamma=5, C=0.001))
                               ))
 
gamma1, gamma2 = 0.1, 5
C1, C2 = 0.001, 1000
hyperparams = (gamma1, C1), (gamma1, C2), (gamma2, C1), (gamma2, C2)
 
svm_clfs = []
for gamma, C in hyperparams:
    rbf_kernel_svm_clf.fit(X, y)
    svm_clfs.append(rbf_kernel_svm_clf)
 
plt.figure(figsize=(11, 7))
 
for i, svm_clfs in enumerate(svm_clfs):
    plt.subplot(221 + i)
    plot_predictions(svm_clfs, [-1.5, 2.5, -1, 1.5])
    plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
    gamma, C = hyperparams[i]
    plt.title(r'$\gamma={}, C={}$'.format(gamma, C), fontsize=16)
plt.show()

在这里插入图片描述
可以看出增加gamma 使高斯曲线变窄,因此每个实例的影响范围都较小,决策边界最终变得不规则,在个别实例周围摆动;减少gamma 使高斯曲线变宽,因此实例具有更大的影响范围,并且决策边界更加平滑。也就是我们看第一幅图,边界比较平稳,没有过拟合的风险,我们看当gamma比较大的时候,过拟合的风险却比较大了。

总结

支持向量机是一种二类分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机。支持向量机还包括核技巧,这使其成为一个非线性分类器。其学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失函数的最小化问题。支持向量机的学习算法是求解凸二次规划的最优化问题。

当输入空间为欧氏空间或者离散集合、特征空间为希尔伯特空间时,核函数表示将输入从输入空间映射到特征空间得到的特征向量之间的内积。通过使用核函数可以学习非线性支持向量机,等价于隐式地在高维的特征空间中学习线性支持向量机。这种核方法是比支持向量机更为一般的机器学习方法。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值