统计学习方法(1) 梯度下降法和SMO算法实现SVM

SVM

SVM是深度学习之前的一种最常用的监督学习方法, 可以用来做分类也可以做回归. 它的本质和感知机类似, 但是额外增加了大间隔的优化目标. 结合前面提到的核技巧, 我们可以把SVM推广到非线性. 这样实现的分类器将有着非常好的性质, 让它一度成为"最优解".

LibSVM

在线性二分类SVM中,我们不止设置一个决策平面 w T x + b = 0 w^Tx+b = 0 wTx+b=0,还会有两个支持平面 w T x + b = − 1 w^Tx+b = -1 wTx+b=1 w T x + b = 1 w^Tx+b = 1 wTx+b=1. 如果我们假设数据是线性可分的, 那么决策平面一定位于两类样本之间. 而现在我们想从两类样本之间确定一个最好的超平面,这个超平面满足的性质是到两类样本的最小距离最大.很自然的, 我们可以看出这个超平面到两类样本的最小距离应该相等. 这时上面的支持超平面就派上用场了. 设到超平面最近的两个样本是 x + x_+ x+ x − x_- x, 样本距离 d = w T x + b ∣ ∣ w ∣ ∣ d=\frac{w^T x+b}{||w||} d=wwTx+b, 也就是 w T x + + b = − ( w T x − + b ) = C w^T x_++b = -(w^T x_-+b) = C wTx++b=(wTx+b)=C. 又因为w的缩放对超平面的性质没有影响, 我们不妨让C = 1, 即两个样本 x + x_+ x+ x − x_- x会分别落在 w T x + b = 1 w^Tx+b = 1 wTx+b=1 w T x + b = − 1 w^Tx+b = -1 wTx+b=1超平面上. 其他样本都在这两个超平面以外.
如果用上面的一系列假设, 我们的损失函数就应该是: 如果正样本越过了 w T x + b = 1 w^Tx+b = 1 wTx+b=1超平面, 即 w T x + b < 1 w^Tx+b < 1 wTx+b<1那么它受到随距离线性增长的惩罚. 同样如果正样本越过了 w T x + b = − 1 w^Tx+b = -1 wTx+b=1超平面, 即 w T x + b > − 1 w^Tx+b > -1 wTx+b>1也受到惩罚. 如果我们设y是样本标签,正样本为+1,负样本为-1. 则我们有保证两类样本到决策超平面的最小距离相等的新型感知机
L ( w , b ) = R e L U ( − y i ( w T x i + b ) + 1 ) ∣ ∣ w ∣ ∣ L(w,b) = \frac{ReLU(-y_i (w^T x_i+b)+1)}{||w||} L(w,b)=wReLU(yi(wTxi+b)+1)
同样的道理, 等价于
L ( w , b ) = R e L U ( − y i ( w T x i + b ) + 1 ) L(w,b) = ReLU(-y_i (w^T x_i+b)+1) L(w,b)=ReLU(yi(wTxi+b)+1)
到此还不算结束, 因为我们还想要这个最小距离最大化. 我们知道 d = w T x + b ∣ ∣ w ∣ ∣ d=\frac{w^T x+b}{||w||} d=wwTx+b, 而$|w^T x_++b| = |w^T x_-+b| = 1 , 即 , 即 ,d=\frac{1}{||w||}$, 也就是最大化距离, 只需要最小化w即可. 最小化一个参数常用的做法是正则化, 也就是对w施加L2的惩罚(L1的惩罚性质较差, 容易让w为0).这样我们就得到了最大化最小距离的损失.
m i n i m i z e 1 2 ∣ ∣ w ∣ ∣ 2 2 minimize\qquad \frac{1}{2}||w||_2^2 minimize21w22
我们把上面两个损失函数加起来, 并给感知机正确分类的一项乘上一个常数系数, 用来调控(正确分类) vs (最大化间隔)两个目标的重要程度.
m i n i m i z e L ( w , b ) = 1 2 ∣ ∣ w ∣ ∣ 2 2 + C ∗ R e L U ( − y i ( w T x i + b ) + 1 ) minimize\quad L(w,b) = \frac{1}{2}||w||_2^2+C*ReLU(-y_i (w^T x_i+b)+1) minimizeL(w,b)=21w22+CReLU(yi(wTxi+b)+1)
优化这个无约束的优化问题就能得到线性的二分类SVM, 我们用Pytorch的自动求导来实现梯度下降, 优化这个目标函数看看效果.

import torch
import numpy as np
import matplotlib.pyplot as plt
import torch.nn.functional as F
import torch.nn as nn
import matplotlib
import random

torch.manual_seed(2020)
n = 50

Xp = torch.randn(n,2)
Xp[:,0] = (Xp[:,0]+3)
Xp[:,1] = (Xp[:,1]+4)
A = torch.tensor([[-1.,0.8],[2,1]])
Xp = Xp.mm(A)

Xn = torch.randn(n,2)
Xn[:,0] = (Xn[:,0]-1)
Xn[:,1] = (Xn[:,1]-2)
A = torch.tensor([[-0.5,1],[1,0.5]])
Xn = Xn.mm(A)

X = torch.cat((Xp,Xn),axis = 0)
y = torch.cat((torch.ones(n),-1*torch.ones(n)))
y = y.reshape(-1,1)


class LibSVM:
    def __init__(self, C = 1, lr = 0.01):
        '''
        超参数包括松弛变量C和学习率lr
        要学习的参数是一个线性超平面的权重和偏置
        '''
        self.C = C
        self.lr = lr
        self.weights = None
        self.bias = None
    
    def train(self):
        self.weights.requires_grad = True
        self.bias.requires_grad = True
        
    def eval(self):
        self.weights.requires_grad = False
        self.bias.requires_grad = False
    
    def fit(self, X, y, max_iters = 1000):
        '''
        X是数据张量, size(n,m)
        数据维度m, 数据数目n
        y是二分类标签, 只能是1或-1
        '''
        n,m = X.shape
        y = y.reshape(-1,1)
        self.weights = torch.randn(m,1)
        self.bias = torch.randn(1)
        self.train()
        
        for step in range(max_iters):
            out = X.mm(self.weights)+self.bias # 前向计算
            # 损失计算
            loss = 0.5*self.weights.T.mm(self.weights)+\
            self.C*torch.sum(F.relu(-y*out+1))
            # 自动求导
            loss.backward()
            # 梯度下降
            self.weights.data -= self.lr*self.weights.grad.data
            self.bias.data -= self.lr*self.bias.grad.data
            self.weights.grad.data.zero_()
            self.bias.grad.data.zero_()
            
        return loss
    
    def predict(self, x, raw = False):
        self.eval()
        out = x.mm(self.weights)+self.bias
        if raw: return out
        else: return torch.sign(out)
        
    def show_boundary(self, low1, upp1, low2, upp2):
        # meshgrid制造平面上的点
        x_axis = np.linspace(low1,upp1,100)
        y_axis = np.linspace(low2,upp2,100)
        xx, yy = np.meshgrid(x_axis, y_axis)
        data = np.concatenate([xx.reshape(-1,1),yy.reshape(-1,1)],axis = 1)
        data = torch.tensor(data).float()
        # 用模型预测
        out = self.predict(data, raw = True).reshape(100,100)
        out = out.numpy()
        Z = np.zeros((100,100))
        Z[np.where(out>1)] = 1.
        Z[np.where(out<-1)] = -1.
        # 绘制支持边界
        plt.figure(figsize=(8,6))
        colors = ['aquamarine','seashell','palegoldenrod']
        plt.contourf(xx, yy, Z, cmap=matplotlib.colors.ListedColormap(colors))
        # 绘制决策边界
        slope = (-self.weights[0]/self.weights[1]).data.numpy()
        b = (-self.bias/self.weights[1]).data.numpy()
        xx = np.linspace(low1, upp1)
        yy = slope*xx+b
        plt.plot(xx,yy, c = 'black')

model = LibSVM()
model.fit(X,y)
model.show_boundary(-5,12,-5,10)
plt.scatter(X[:n,0].numpy(),X[:n,1].numpy() , marker = '+', c = 'r')
plt.scatter(X[n:,0].numpy(),X[n:,1].numpy() , marker = '_', c = 'r')

在这里插入图片描述

Kernel SVM

有时, 我们需要的是超越超平面的决策边界, 它应该能容忍非线性可分的两类样本, 并给出非线性的解. 这一点仅靠上面的方法是无法实现的, 为此我们可以把核技巧带到LibSVM中, 得到二分类的KerbSVM. 复习一下核方法, 我们使用核方法的核心思想是在向量内积得到的标量上进行非线性变换, 从而间接实现高维映射.
那么怎么把内积带到上面的线性模型里呢? 观察到权重w和数据点x的维度相同, 那么如果我们把w写成x的线性组合, 就能得到x和x的内积. 即
w = ∑ i = 1 N α i x i T w = \sum_{i=1}^{N}\alpha_i x_i^T w=i=1NαixiT
y = ∑ i = 1 N α i x x i T y = \sum_{i=1}^{N} \alpha_i x x_i^T y=i=1NαixxiT
在内积上套用核函数就有非线性的广义线性模型, 常用的非线性核函数有高斯核, 多项式核等等
k ( x , y ) = ( x y T ) d k(x,y) = (xy^T)^d k(x,y)=(xyT)d
k ( x , y ) = e x p ( − σ ∣ ∣ x − y ∣ ∣ 2 ) k(x,y) = exp(-\sigma||x-y||^2) k(x,y)=exp(σxy2)
y = ∑ i = 1 N α i ϕ ( x ) ϕ ( x i ) T + b = ∑ i = 1 N α i k ( x , x i ) + b y = \sum_{i=1} ^{N} \alpha_i \phi(x) \phi(x_i)^T+b = \sum_{i=1} ^{N} \alpha_i k(x,x_i)+b y=

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SVM支持向量机)是一种常用的机器学习算法,在其核方法中使用了SMO(序列最小最优化)算法进行优化。对于使用Python实现SVM算法,可以借助一些常见的机器学习库如sklearn或者使用自己编写的代码。 使用sklearn库,可以通过以下步骤来实现SVM算法: 1. 导入数据:将需要进行分类的数据导入Python中,可以使用pandas或者numpy库来处理数据。 2. 数据预处理:对导入的数据进行预处理,包括缺失值处理、归一化、标准化等。 3. 划分训练集和测试集:使用sklearn.model_selection库中的train_test_split方法,将数据集划分为训练集和测试集。 4. 构建SVM模型:导入sklearn.svm库,使用其中的SVC类来构建SVM分类器模型。 5. 训练模型:使用训练集数据对SVM模型进行训练。 6. 预测:使用测试集数据对训练好的模型进行预测,得到分类结果。 7. 模型评估:使用准确率、精确率、召回率等指标对模型进行评估。 另外,也可以通过编写Python代码来实现SVM算法,包括SMO算法实现SMO算法涉及到对拉格朗日乘子、核函数等的处理,需要深入了解SVM的原理和数学推导。其实现过程比较复杂,需要编写大量的代码来进行优化。 总的来说,实现SVM算法可以选择使用现成的机器学习库,也可以通过自己编写代码实现。前者更为方便快捷,后者可以更深刻理解SVM算法的原理和实现细节。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值