soft-margin svm

一、Hard-Margin SVM 与Soft-Margin SVM

SVM 算法的一些缺点

  • 当数据点在特征空间中不可分的时候失效

在SVM的前一部分中我们提到在边缘到决策边界之间是没有数据点的,只有支持向量在边缘上,其他的数据点都在决策边界以外。

这是基于数据点在特征空间中是线性可分的。但是有可能我们并不能找到一个非常好的特征映射函数,或者说找不到合适的kernel function使得原空间中的点被映射到特征空间中可分。这时如果我们使用之前所介绍的SVM算法,就不能够找到合适的决策边界使得特征空间被分开,SVM算法失效。

为了处理这种情况,我们有必要允许部分数据点能够落在边缘与决策边界之间。

故而最原始版本的SVM算法被称为Hard-Margin SVM,而这种在边缘与决策边界之间允许数据点存在的SVM算法被称为Soft-Margin SVM

  • 容易过拟合

另外一个需要引入soft-margin SVM算法的原因是因为 hard-margin SVM 容易过拟合

从上面第一个图可以看出如果样本在特征空间中能够被线性分开,hard-margin SVM 能够非常完美地把 特征空间的数据点给分开,其决策边界映射回 原空间中就是那条s型曲线,但是因为由于对训练数据的正确度太高,容易造成过拟合的问题,因此当对新的输入点进行预测的时候,过拟合的情况就会对 正确性产生影响。如上面第二个图所示,s型决策边界不如线性边界的效果好。因此我们需要对SVM进行正则化处理。

而soft-margin 通过允许数据点落在边缘与决策边界之间就能够解决上述两个问题

二、 soft-margin的分类约束

soft-margin 引入了松弛变量(slcak variable)的概念,对每一个数据点都有一个slack variable,即

定义如下:

  • 当数据点落在边缘上或边缘两侧的时候,

  • 当数据点落在边缘与决策边界之间的时候,

由此我们可以发现:

对于决策边界上的点有,因此

而对于的点将会被错误分类。

故分类的约束条件变为:

从上述约束条件中可以看出,当的时候,就会造成从而造成误分类。

与分类的关系总结如下:

  • 的数据点处在边缘上或者在边缘的两侧,会被正确地分类
  • 的数据点处于边缘和决策边界之间,不过处于决策边界的正确一侧,会被正确分类
  • 的数据点将会处于决策边界的错误一侧并被错误分类,需要注意的是这些点有可能并不是处于边缘和决策边界之间,有可能超过了另一侧的边缘

以上情况请参照下图:

三、soft-margin的理论分析

1.使margin最大化的数学表示

在引入slack variable之后,我们的目标是使用slack variable来进行正则化,使得margin最大化,即对于处在margin错误一侧的数据点加以惩罚。

因此我们需要最小化

其中,控制着边缘和slack variable之间的权衡,因为对于错误分类的点其 是被错误分类的数据点的数目的上限,因此C就是正则化的参数,起到对最小化训练误差与控制模型复杂度的折衷作用。

的时候,soft-margin 变为 hard-margin

为了在约束:

下使得

最小化,我们可以使用拉格朗日乘数法:

相应的KKT条件为:

2.最优化函数的对称表示

分别对进行求导并令其等于0,有:

将其代回到原式中,利用来简化式子:

约束为:

我们发现上面这个拉格朗日乘数法的式子与hard-margin的完全相同,除了约束部分之外。

我们也可以如hard-margin中一样,将上述对称表示写成矩阵的形式:

约束为:

3.soft-margin中的支持向量是那些点

soft-margin对新的输入的预测函数依然是:

故那些的点对于预测模型并无影响,所以那些的点才被称为支持向量,

在 soft-margin中,支持向量不仅包括那些处于边缘处的点,也包括那些处于边缘和决策边界之间的点,以及被错误分类的店。

因为对于的点一定满足:

  • 如果,那么,根据约束条件有 ,那么这些支持向量位于边缘上
  • 如果,那么

    如果 ,支持向量在边缘和决策边界的中间,不过在决策边界的正确一边,将被正确分类

    如果 ,支持向量在决策边界的错误一边,将被错误分类

4.如何求y(x)中的b

对于那些的点,,这些点位于margin上,从而有,因此有:

又因,所以,那么将上式左右乘以有:

但是这里只使用了一个满足条件的点,我们可以使用所有的刚好处在margin上的支持向量并求平均:

其中表示所有的支持向量,表示刚好处在margin上的支持向量

如何实现一个非线性的soft-margin SVM

与上一篇博客中的hard-margin相比,soft-margin只用在二次规划部分加一个常量C使得即可

%matplotlib inline

import numpy as np
from scipy.linalg import norm
import cvxopt
import cvxopt.solvers
from pylab import *
data=np.loadtxt('data.txt')

# 获得训练数据
X = data[:,0:2]
t=data[:,2]
t = 2*t-3  # 将类别由1,2 转换成为-1,1

indexOfClass1=t==-1

indexOfClass2=t==1

scatter(X[indexOfClass1,0],X[indexOfClass1,1],color='r')
scatter(X[indexOfClass2,0],X[indexOfClass2,1],color='b')
<matplotlib.collections.PathCollection at 0x112a3d4d0>

png

"""
非线性SVM

使用了cvxopt中的Quadratic Programming,要先安装cvxopt:sudo -H pip install cvxopt
"""

N = len(X)         #训练数据的个数
P = 3           # 多项式核的阶数
C = 1000
SIGMA = 0.125     # RBF kernel 中的sigma数,至于为什么将sigma设定为0.125就涉及到另外一个话题,参数寻找,可以查看我的其他博客

# 多项式 kernel
def polynomial_kernel(x, y):
    return (1 + np.dot(x, y)) ** P

# RBF kernel
def gaussian_kernel(x, y):
    return np.exp(- norm(x-y)**2 / (2 * (SIGMA ** 2)))

# 选定RBF kenel
kernel = gaussian_kernel

# 对于新的输入x求y(x)
def f(x, a, t, X, b):
    summation = 0.0
    for n in range(N):
        summation += a[n] * t[n] * kernel(x, X[n])
    return summation + b



# 计算gram matrix 注意这里根据 L(a)的公式部分将t_nt_jK(X_n,X_m)直接写在了一起
K = np.zeros((N, N))
for i in range(N):
    for j in range(N):
        K[i, j] = t[i] * t[j] * kernel(X[i], X[j])

        
#属于数值优化部分,不详解,请参考相关文档
Q = cvxopt.matrix(K)
p = cvxopt.matrix(-np.ones(N))
temp1 = np.diag([-1.0] * N)
temp2 = np.identity(N)
G = cvxopt.matrix(np.vstack((temp1, temp2)))
temp1 = np.zeros(N)
temp2 = np.ones(N) * C
h = cvxopt.matrix(np.hstack((temp1, temp2)))
A = cvxopt.matrix(t, (1, N))
b = cvxopt.matrix(0.0)
sol = cvxopt.solvers.qp(Q, p, G, h, A, b)
a = array(sol['x']).reshape(N)

# 找到支持向量,对于支持向量而言,a_n >0
#而恰好在边缘上的点 0<a_n<C
S = []
M = []
for n in range(len(a)):
    # 0.001より小さいのは0とみなしてサポートベクトルとみなさない
    if a[n] > 0.001:
        S.append(n)
    if 0.001 < a[n] < C:
        M.append(n)

# 计算b
summation = 0
for n in M:
    temp = 0
    for m in S:
        temp += a[m] * t[m] * kernel(X[n], X[m])
    summation += (t[n] - temp)
b = summation / len(M)

print S, b

# 绘制训练数据
for n in range(N):
    if t[n] > 0:
        scatter(X[n,0], X[n,1], c='b', marker='o')
    else:
        scatter(X[n,0], X[n,1], c='r', marker='o')



# 绘制决策边界
X1, X2 = meshgrid(linspace(-2,2,50), linspace(-2,2,50))
w, h = X1.shape
X1.resize(X1.size)
X2.resize(X2.size)
Z = array([f(array([x1, x2]), a, t, X, b) for (x1, x2) in zip(X1, X2)])
X1.resize((w, h))
X2.resize((w, h))
Z.resize((w, h))
CS = contour(X1, X2, Z, [0.0], colors='k', linewidths=1, origin='lower')

xlim(-0.2, 1.2)
ylim(-0.2, 1.2)
show()
     pcost       dcost       gap    pres   dres
 0:  1.1017e+07 -1.4106e+08  2e+08  1e-01  5e-13
 1:  6.5628e+06 -1.6014e+07  3e+07  1e-02  2e-12
 2:  1.8209e+06 -3.5314e+06  6e+06  2e-03  6e-13
 3:  3.7936e+05 -6.9480e+05  1e+06  1e-04  2e-13
 4:  7.1866e+04 -1.2283e+05  2e+05  1e-05  1e-13
 5:  9.1597e+03 -1.7963e+04  3e+04  2e-12  9e-14
 6:  2.1474e+03 -8.3742e+03  1e+04  2e-13  6e-14
 7: -6.5775e+02 -6.0722e+03  5e+03  8e-13  8e-14
 8: -2.0261e+03 -4.8622e+03  3e+03  2e-12  1e-13
 9: -2.7831e+03 -3.9420e+03  1e+03  1e-12  1e-13
10: -3.1904e+03 -3.4766e+03  3e+02  6e-13  1e-13
11: -3.2749e+03 -3.3663e+03  9e+01  7e-13  1e-13
12: -3.3072e+03 -3.3269e+03  2e+01  8e-13  1e-13
13: -3.3158e+03 -3.3163e+03  6e-01  1e-12  1e-13
14: -3.3160e+03 -3.3160e+03  7e-03  1e-13  1e-13
15: -3.3160e+03 -3.3160e+03  9e-05  7e-13  1e-13
Optimal solution found.
[33, 61, 62, 66, 90, 91, 93, 94, 95, 121, 131, 138, 152, 160, 165, 172, 202, 206, 211, 215, 216, 233, 243, 249, 254, 334, 352, 366, 367, 370, 390, 407, 421, 423, 446, 456, 459, 461, 474, 489, 495, 497] -0.168699827192

png