Python手撸机器学习系列(十):软间隔SVM

软间隔SVM

对于硬间隔SVM,其目标函数为:
min ⁡ w , b 1 2 w T w s . t . y i ( w T x i + b ) ≥ 1 \min\limits_{w,b} \frac{1}{2}w^Tw \\ s.t. y_i(w^Tx_i+b)\geq1 w,bmin21wTws.t.yi(wTxi+b)1
具体细节可参考https://blog.csdn.net/qq_43601378/article/details/122027005
这个式子是建立在数据集线性可分的情况下得出的

那么问题来了,如果是线性不可分的情况,或者是有噪点的线性可分,该怎么办

在这种情况下,我们假设其是线性可分的,并允许其犯一点点错误,在最优化时给其加上一点点损失,这就是软间隔SVM(soft-margin SVM)

1.1 软间隔SVM模型原理

由上述我们可得软间隔SVM是在硬间隔SVM的基础上加上一点损失,即:
min ⁡ w , b 1 2 w T w + l o s s s . t .    y i ( w T x i + b ) ≥ 1 \min\limits_{w,b} \frac{1}{2}w^Tw + loss \\ s.t. \ \ y_i(w^Tx_i+b)\geq1 w,bmin21wTw+losss.t.  yi(wTxi+b)1
现在需要确定这个loss

从感知机的知识我们可以知道,有两种思路:一种是统计所有误分类的点,一种是计算误分类点到超平面的距离。

第一种不利于我们求导,一般选择第二种(此处可以恶补感知机)。

按距离设置loss,可以写作:
l o s s = m a x { 0 , 1 − y i ( w T x i + b ) } loss = max\{0,1-y_i(w^Tx_i+b)\} loss=max{0,1yi(wTxi+b)}
即当 y i ( w T x i + b ) ≥ 1 y_i(w^Tx_i+b)\geq1 yi(wTxi+b)1时分类正确,此时损失为0,反之分类有误,损失为 1 − y i ( w T x i + b ) 1-y_i(w^Tx_i+b) 1yi(wTxi+b)

这个损失也被称为合页损失(hinge loss,亦称为铰链损失)

将所有点的损失相加,就是所有误分类点的损失

当确定loss后,软间隔SVM的优化目标也确定了:
min ⁡ w , b 1 2 w T w + C ∑ i = 1 N m a x { 0 , 1 − y i ( w T x i + b ) } s . t .    y i ( w T x i + b ) ≥ 1 \min_{w,b}\frac{1}{2}w^Tw+C\displaystyle\sum_{i=1}^Nmax\{0,1-y_i(w^Tx_i+b)\} \\ s.t. \ \ y_i(w^Tx_i+b)\geq1 w,bmin21wTw+Ci=1Nmax{0,1yi(wTxi+b)}s.t.  yi(wTxi+b)1
引入 ξ = 1 − y i ( w T x i + b ) ,    ξ ≥ 0 \xi = 1-y_i(w^Tx_i+b),\ \ \xi\geq0 ξ=1yi(wTxi+b),  ξ0,则原问题重新表述为:
min ⁡ w , b , ξ 1 2 w T w + C ∑ i = 1 N ξ i s . t .    y i ( w T x i + b ) ≥ 1 − ξ i ξ ≥ 0 \min_{w,b,\xi}\frac{1}{2}w^Tw+C\displaystyle\sum_{i=1}^N\xi_i \\ s.t. \ \ y_i(w^Tx_i+b)\geq1-\xi_i \\ \xi\geq0 w,b,ξmin21wTw+Ci=1Nξis.t.  yi(wTxi+b)1ξiξ0

  • 这里的 ξ \xi ξ被称为松弛变量,即适当放松原来问题的要求,且每个 x i x_i xi都有一个对应的 ξ i \xi_i ξi
  • 这里的 C C C被称为惩罚参数,且 C > 0 C>0 C>0 C C C越大,表示对误分类点的惩罚越大,迫使所有样本均满足约束,即尽可能将其两组点分开,反之允许更多的点跨过分界线。

1.2 软间隔SVM模型求解

根据前面硬间隔的方式,依然将其转化为对偶形式进行求解。

首先引入拉格朗日乘子(注意拉格朗日乘子法中限定不等式必须都是小于等于0的形式,所以有一点转化):
L ( w , b , ξ , α , β ) = 1 2 w T w + C ∑ i = 1 N ξ i + ∑ i = 1 N α i ( 1 − ξ i − y i ( w T x i + b ) ) + ∑ i = 1 N β ( − ξ ) L(w,b,\xi,\alpha,\beta) = \frac{1}{2}w^Tw+C\displaystyle\sum_{i=1}^N\xi_i+\displaystyle\sum_{i=1}^N\alpha_i(1-\xi_i-y_i(w^Tx_i+b))+\displaystyle\sum_{i=1}^N\beta(-\xi) L(w,b,ξ,α,β)=21wTw+Ci=1Nξi+i=1Nαi(1ξiyi(wTxi+b))+i=1Nβ(ξ)
原问题的对偶问题为(假设已满足KKT条件,交换了min和max的位置):
max ⁡ α , β min ⁡ w , b , ξ L ( w , b , ξ , α , β ) s . t .   α i ≥ 0 , i = 1 , 2 , . . . , N β i ≥ 0 , i = 1 , 2 , . . . , N \max_{\alpha,\beta}\min_{w,b,\xi}L(w,b,\xi,\alpha,\beta) \\ s.t. \ \alpha_i\geq0,i=1,2,...,N \\ \beta_i\geq0,i=1,2,...,N α,βmaxw,b,ξminL(w,b,ξ,α,β)s.t. αi0,i=1,2,...,Nβi0,i=1,2,...,N
求解 min ⁡ w , b , ξ L ( w , b , ξ , α , β ) \min\limits_{w,b,\xi}L(w,b,\xi,\alpha,\beta) w,b,ξminL(w,b,ξ,α,β)

分别对 w , b , ξ w,b,\xi w,b,ξ求导并令其为0:
{ ∂ L ∂ w = w − ∑ i = 1 N α i y i x i = 0 ∂ L ∂ b = − ∑ i = 1 N α i y i = 0 ∂ L ∂ ξ = C − α i − β i = 0 \begin{cases} \frac{\partial L}{\partial w} = w-\displaystyle\sum_{i=1}^N\alpha_iy_ix_i = 0 \\ \frac{\partial L}{\partial b} = -\displaystyle\sum_{i=1}^N\alpha_iy_i =0 \\ \frac{\partial L}{\partial \xi} = C-\alpha_i-\beta_i = 0 \end{cases} wL=wi=1Nαiyixi=0bL=i=1Nαiyi=0ξL=Cαiβi=0
可得:
{ w = ∑ i = 1 N α i y i x i ∑ i = 1 N α i y i = 0 β i = C − α i \begin{cases} w = \displaystyle\sum_{i=1}^N\alpha_iy_ix_i \\ \displaystyle\sum_{i=1}^N\alpha_iy_i = 0 \\ \beta_i = C-\alpha_i \end{cases} w=i=1Nαiyixii=1Nαiyi=0βi=Cαi
带入 L ( w , b , ξ , α , β ) L(w,b,\xi,\alpha,\beta) L(w,b,ξ,α,β),得:
L ( w , b , ξ , α , β ) = − 1 2 ∑ i = 1 N ∑ j = 1 N α i α j y i y j x i x j + ∑ i = 1 N α i L(w,b,\xi,\alpha,\beta) = -\frac{1}{2}\displaystyle\sum_{i=1}^N\displaystyle\sum_{j=1}^N\alpha_i\alpha_jy_iy_jx_ix_j+\displaystyle\sum_{i=1}^N\alpha_i L(w,b,ξ,α,β)=21i=1Nj=1Nαiαjyiyjxixj+i=1Nαi
原问题简化为:
max ⁡ α    − 1 2 ∑ i = 1 N ∑ j = 1 N α i α j y i y j x i x j + ∑ i = 1 N α i s . t .    ∑ i = 1 N α i y i = 0 0 ≤ α i ≤ C \max_\alpha \ \ -\frac{1}{2}\displaystyle\sum_{i=1}^N\displaystyle\sum_{j=1}^N\alpha_i\alpha_jy_iy_jx_ix_j+\displaystyle\sum_{i=1}^N\alpha_i \\ s.t. \ \ \displaystyle\sum_{i=1}^N\alpha_iy_i = 0 \\ 0\leq\alpha_i\leq C αmax  21i=1Nj=1Nαiαjyiyjxixj+i=1Nαis.t.  i=1Nαiyi=00αiC
转化一下,可得到:

min ⁡ α    1 2 ∑ i = 1 N ∑ j = 1 N α i α j y i y j x i x j − ∑ i = 1 N α i s . t .    ∑ i = 1 N α i y i = 0 0 ≤ α i ≤ C \min_\alpha \ \ \frac{1}{2}\displaystyle\sum_{i=1}^N\displaystyle\sum_{j=1}^N\alpha_i\alpha_jy_iy_jx_ix_j-\displaystyle\sum_{i=1}^N\alpha_i \\ s.t. \ \ \displaystyle\sum_{i=1}^N\alpha_iy_i = 0 \\ 0\leq\alpha_i\leq C αmin  21i=1Nj=1Nαiαjyiyjxixji=1Nαis.t.  i=1Nαiyi=00αiC
对于全部的样本,存在 ( x k , y k ) (x_k,y_k) (xk,yk),使得 1 − ξ k − y k ( w T x k + b ) = 0 1-\xi_k-y_k(w^Tx_k+b) = 0 1ξkyk(wTxk+b)=0

此时可得 b = y k − y k ξ k − w T x k b = y_k-y_k\xi_k-w^Tx_k b=ykykξkwTxk

如果 ξ k > 0 \xi_k > 0 ξk>0,两边同时乘上 y k y_k yk,则可得 ξ k = 1 − y k ( w T x k + b ) \xi_k=1-y_k(w^Tx_k+b) ξk=1yk(wTxk+b),无解

如果 ξ k = 0 \xi_k = 0 ξk=0,则可求出b,此时 0 < α k < C 0<\alpha_k<C 0<αk<C

求导时我们以及得到
w ∗ = ∑ i = 1 N α i y i x i w^* = \displaystyle\sum_{i=1}^N\alpha_iy_ix_i w=i=1Nαiyixi
求得
b ∗ = y k − w ∗ T x k = y k − ∑ i = 1 N y i α i ( x i , x k ) b^* = y_k-w^*{^T}x_k =y_k-\displaystyle\sum_{i=1}^Ny_i\alpha_i(x_i,x_k) b=ykwTxk=yki=1Nyiαi(xi,xk)
即可求出超平面 w ∗ T x + b w^*{^T}x+b wTx+b

后面就是和硬间隔SVM一样的使用SMO算法求得 α \alpha α,再求出 w w w b b b,SMO算法的实现推导可以参考我的上一篇博客,点击跳转

与硬间隔SVM的对偶形式相比,仅多了 0 ≤ α i ≤ C 0\leq\alpha_i\leq C 0αiC这一个限制条件,而这个参数一般由人为设定,即超参数

1.3 代码实现

import numpy as np
import random
import matplotlib.pyplot as plt

def simple_smo(dataset, labels, C, max_iter):
    ''' 简化版SMO算法实现,未使用启发式方法对alpha对进行选择.
    :param dataset: 所有特征数据向量
    :param labels: 所有的数据标签
    :param C: 软间隔常数, 0 <= alpha_i <= C
    :param max_iter: 外层循环最大迭代次数
    '''
    dataset = np.array(dataset)
    m, n = dataset.shape #样本数量,特征数量
    labels = np.array(labels)

    # 初始化参数λ,b为0
    lambds = np.zeros(m) #每个样本都有一个λ乘子
    b = 0
    it = 0


    while it < max_iter:
        pair_changed = 0 #选取的一对值相较于之前是否有变化,没有的话可以停止迭代
        for i in range(m):
            λ_i, x_i, y_i = lambds[i], dataset[i], labels[i] #选取一组λ
            fx_i = SVM_predict(x_i,lambds,dataset,labels,b)
            E_i = fx_i - y_i
            j = select_j(i, m) #选取另一个λ
            λ_j, x_j, y_j = lambds[j], dataset[j], labels[j]
            fx_j = SVM_predict(x_j,lambds,dataset,labels,b)
            E_j = fx_j - y_j
            K_ii, K_jj, K_ij = np.dot(x_i, x_i), np.dot(x_j, x_j), np.dot(x_i, x_j)
            eta = K_ii + K_jj - 2*K_ij #
            if eta <= 0:
                print('WARNING  eta <= 0')
                continue
            # 获取更新的alpha对
            λ_i_old, λ_j_old = λ_i, λ_j #未更新前的参数
            λ_j_new = λ_j_old + y_j*(E_i - E_j)/eta
            # 对alpha进行修剪
            if y_i != y_j:
                L = max(0, λ_j_old - λ_i_old)
                H = min(C, C + λ_j_old - λ_i_old)
            else:
                L = max(0, λ_i_old + λ_j_old - C)
                H = min(C, λ_j_old + λ_i_old)
            λ_j_new = clip(λ_j_new, L, H) #根据上下界修剪
            λ_i_new = λ_i_old + y_i*y_j*(λ_j_old - λ_j_new) #根据公式反推另一个参数
            if abs(λ_j_new - λ_j_old) < 0.00001: #这个参数已经优化到最佳,换下一个
                #print('WARNING   alpha_j not moving enough')
                continue

            #更新b
            lambds[i], lambds[j] = λ_i_new, λ_j_new
            b_i = -E_i - y_i*K_ii*(λ_i_new - λ_i_old) - y_j*K_ij*(λ_j_new - λ_j_old) + b
            b_j = -E_j - y_i*K_ij*(λ_i_new - λ_i_old) - y_j*K_jj*(λ_j_new - λ_j_old) + b
            if 0 < λ_i_new < C:
                b = b_i
            elif 0 < λ_j_new < C:
                b = b_j
            else:
                b = (b_i + b_j)/2
            pair_changed += 1
            print('INFO   iteration:{}  i:{}  pair_changed:{}'.format(it, i, pair_changed))
        if pair_changed == 0: #参数优化完成,下一轮
            it += 1
        else: #参数没有优化完成,继续迭代
            it = 0
        print('iteration number: {}'.format(it))
    return lambds, b

def SVM_predict(x,lambds,data,label,b):
    "SVM分类器函数 y = w^Tx + b,即文中的f(x)"
    res = 0
    for i in range(data.shape[0]):
        res += lambds[i]*label[i]*(data[i].dot(x.T))
    return res + b

def get_w(lambdas, dataset, labels):
    #通过λ求w
    w = 0
    for i in range(len(dataset)):
        w += lambdas[i]*y[i]*dataset[i]
    return w

def clip(alpha, L, H):
    #修建λ的值到L和H之间.

    if alpha < L:
        return L
    elif alpha > H:
        return H
    else:
        return alpha

def select_j(i, m):
    #在m中随机选择除了i之外剩余的
    l = list(range(m))
    seq = l[: i] + l[i+1:]
    return random.choice(seq)

def get_point():
    x_true =  [[1,1.5],[1,3],[4,5],[2,4]]
    x_false = [[1,0.5],[4,2],[5,1],[4,1]]
    x_true =  [[1,2],[1,3],[4,5],[2,4],[2,1.5]]
    x_false = [[1,0.5],[4,2],[5,1],[4,1],[3,2]]
    x_all = np.array(x_true+x_false)
    y = [1]*len(x_true) + [-1]*len(x_false)
    return x_all,y,x_true,x_false

def plot(x_true,x_false,w,b):
    plot_x = np.arange(0,7,0.1)
    plot_y = -(w[0]*plot_x+b)/w[1]
    plt.scatter([x[0] for x in x_true],[x[1] for x in x_true] , c='r' , label='+1')
    plt.scatter([x[0] for x in x_false],[x[1] for x in x_false] , c='b',label='-1')
    plt.plot(plot_x,plot_y,c = 'green')
    plt.xlim(0,7)
    plt.ylim(0,7)
    plt.legend()
    plt.plot()
    plt.show()

if __name__ == '__main__':
    x,y,x_true,x_false = get_point()
    MAX_EPOCHES = 20 #最大迭代次数
    C = 50 #λ边界
    #调节C的值会有不同的改变
    lambdas, b = simple_smo(x, y, C, MAX_EPOCHES)
    w = get_w(lambdas, x, y)
    print('-'*40+'result'+'-'*40)
    print('lambdas:{}\nw:{}\nb:{}'.format(lambdas,w,b))
    plot(x_true,x_false,w,b)

结果:

当C分别取50,10,1,0.1时:

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

1.4 参考文献

  1. https://zhuanlan.zhihu.com/p/309944707
  2. https://zhuanlan.zhihu.com/p/227896319
  3. https://zhuanlan.zhihu.com/p/29212107
  • 9
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

锌a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值