支持向量机(Support Vector Machine)

数学推导

1.优化目标

对于一个线性可分的二分类问题来说,分类决策面会有很多个,如何确定那一条是最优的决策面,SVM给出了思路:这个分类决策面要满足距离两类数据的间隔最大,方法就是让两类中距离决策面最近的点到决策面的距离最大,如图所示,要让标记的四个点到决策面的距离最大化,且这他们到决策面的距离相等,这些点被称为“支持向量”。
在这里插入图片描述
在空间解析几何中,若有点A(x0,y0),直线ax+by+c=0,则点到直线的距离为:
在这里插入图片描述
拓展到n维空间,设分类决策面的表达式为w^Tx+b=0,则某点到决策面的距离为:
在这里插入图片描述
在SVM中,为了把标签信息融进算法中,设定分类标签为{+1,-1},而非{0,1},激活函数为单位越阶函数:f(x)=sign(w^Tx+b),对应分类标签,即当w.Tx+b>0时,f(x)=1,w.Tx+b<0时,f(x)=-1,而w.Tx+b=0就是我们要寻找的最优分类决策面。所以我们可以将标签信息融入到距离公式中,从而把上述式子的绝对值去掉,设支持向量到决策面的距离为d,则对于任意数据点,有:
在这里插入图片描述
这里注意,在支持向量确定的两条线的区域内是不可以存在任何数据点的,然而这却是理想情况,实际应用中数据总会存在噪声,进而会出现一些离群点,即有些噪声点会出现在支持向量确定的两条线的区域内(见下灰色图圈红点),为了使得模型有更好的泛化能力,就引入了一个松弛变量对应离群点允许偏离的函数间隔的量,进而有
在这里插入图片描述
但是又不能使得松弛变量无限大,否则会任意的超平面都是符合条件的了,所以要在原优化函数后加一项,令这些松弛变量的总和也要最小,这样我们的优化目标就变为,其中C是需要调整的超参数,若没有离群点,此时可以设定C=0,具体调参在代码中展示:
在这里插入图片描述
在这里插入图片描述
2.拉格朗日乘数法,对偶变换
在这里插入图片描述
对于这样的带约束的最小优化问题,可以使用拉格朗日乘数法,将其转化为无约束优化问题的求解,引入拉格朗日乘子
在这里插入图片描述
上述的最小优化问题即为:
在这里插入图片描述
将上述带到minL中,得
在这里插入图片描述
而对于非线性问题,可以用核函数的方式,将非线性问题转换成线性问题,这里我们使用高斯核函数:
在这里插入图片描述
至此我们便将SVM的优化式子推导出来了,当然还有一个悬而未决的事情就是如何优化α!1998年微软研究院John C.platt提出了一个二次规划算法------SMO是最常用的优化算法,发表在他的论文Sequential Minimal Optimization: A Fast Algorithm for Training Support Vector Machines中,每次两个αi,αj做优化,让αi由αj表示出来,再带回到上述优化问题中,这样原式就变为αi一个未知数的函数,再求解问题的最优化,其中涉及较为复杂的数学推导,花了一周的时间也还没有完全搞明白,所以并不想简单复制粘贴论文里的公式介绍SMO算法,感兴趣的读者可以自行去下载这篇论文,待我推明白SMO再通过一篇博客单独整理它,接下来我将利用sklearn封装的SVM,通过调参的方式查看模型性能。


关键代码

1.数据线性可分
我们仍然使用iris数据集,并且为了实现可视化,只取前两个特征中分类位0,1的数据,使用sklearn封装的svm算法LinSVC,可以看到其中的参数C,即为我们要调整的超参数,default=1.0。当然在训练之前还要对数据进行归一化,并最终将分类决策面和支持向量确定的区域绘制出来:
在这里插入图片描述
在这里插入图片描述

X, y = load_data()
    # 数据归一化
    scaler = StandardScaler()
    scaler.fit(X)
    X_scaler = scaler.transform(X)

    # 训练svm模型
    svc = LinearSVC(C=1000)
    svc.fit(X_scaler, y)

    w = svc.coef_[0]                 # w = (w1,w2)
    b = svc.intercept_[0]            # b

    x = np.linspace(-3, 3, 100)

    # w0 * x0 + w1 * x1 + b = 0
    # => x1 = - w0 / w1 * x0 - b / w1
    plt.plot(x, -w[0] / w[1] * x - b / w[1], c='r')
    # w0 * x0 + w1 * x1 + b = 1
    # => x1 = - w0 / w1 * x0 - b / w1 + 1 / w[1]
    plt.plot(x, -w[0] / w[1] * x - b / w[1] + 1 / w[1], c='black')
    # w0 * x0 + w1 * x1 + b = -1
    # => x1 = - w0 / w1 * x0 - b / w1 - 1 / w[1]
    plt.plot(x, -w[0] / w[1] * x - b / w[1] - 1 / w[1], c='black', label='C=1000')

    plt.scatter(X_scaler[y == 0, 0], X_scaler[y == 0, 1])
    plt.scatter(X_scaler[y == 1, 0], X_scaler[y == 1, 1])

    plt.axis([-3, 3, -3, 3])
    plt.legend()
    plt.show()

分类结果如下图所示,可以看到,当C的值越大,模型就月趋向于硬间隔,即在支持向量区域内再无数据点,随着C的值不断减小,分类的容错空间就越大,如下图C从1000到1再到0.01,支持向量区域越来越大。iris数据集较为简单,在实际问题中,还要依照具体数据来调整C的大小,是模型泛化能力达到最优。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.数据线性不可分
对于线性不可分的问题,就要使用核函数在训练练模型,核函数有多种,这里只举RBF核,使用sklearn封装的SVC,其中有几个比较常用的参数:
C:优化方法中的超参数,默认为1.0
kernel:可以选择核函数,linear(线性核函数),poly(多项式核函数),rbf(高斯核函数)等,默认为rbf
degree:多项式中的degree值,其他核函数会自动忽略掉该值,只有在kernel=‘poly’时有效
gamma:是‘rbf’, ‘poly’ and ‘sigmoid’的核系数
在这里插入图片描述
使用make_moons方法生成测试用例,为了使得每次运行的用例结果相同,设置一个随机种子123,并利用Pipeline封装数据归一化和SVC(kernel=‘rbf’,gamma=‘…’)方法,最后将分类决策面绘制出来:

X, y = datasets.make_moons(noise=0.15, random_state=123)

    svc_pipe = Pipeline([
        ("std_scaler", StandardScaler()),
        ("svc", SVC(kernel="rbf", gamma=100))
    ])
    svc_pipe.fit(X, y)

决策面如下图所示,当gamma=1时,分类效果较好,随着gamma减小,模型的决策面会越来越趋向于线性,这显然会有欠拟合问题,当gamma逐渐增大,模型决策面会“照顾”到每个数据点,这显然会有过拟合会问,当然这里gamma=1可能并非是最优的rbf参数,接下来还可以利用分割数据集,交叉验证等方式计算模型的准确率,调整gamma的值,进而的到argmax_gamma(score),这里只为说明gamma的变化趋势对决策面的影响,所以调参的问题就不多赘述。
在这里插入图片描述

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


总结

1.svm模型泛化能力强,精度高,原始分类器若不加修改仅适用于处理二分类的问题,加入核方法还可以有效地处理非线性问题,可以说svm极其强大,但这背后也是有较为复杂的数学理论做支撑的,例如优化算法SMO就是博主这段时间未跨过去的一个坎,只是从sklearn封装的方法中看到了svm的效果,但对其方法内部貌似还是个黑盒,并未从数学层面上真正吃透SMO算法,之后我会专门找时间整理一下。
2.至此机器学习中几个重要的监督学习方法也整理到尾声了,接下来就是非监督的方法,还有一些其他的对数据预处理的方法例如PCA等需要整理,任重而道远。
3.中秋节快乐,家人身体健康!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值