[机器学习]支持向量机

目录

一、算法概述

1.1 算法简介

1.2 间隔与支持向量

1.3 算法流程

1.4 对偶问题

二、算法实现

2.1 实现SVM分类器

2.2 实现结果可视化

2.3 准备数据 训练模型

2.4 实验结果截图

2.5 调整参数C优化分类结果

2.6 实验结果截图(可调整参数C)

2.7 整体代码

三、总结分析

3.1 实验中出现的问题

3.2 实验小结

3.3 参考书籍


 

一、算法概述

1.1 算法简介

给定训练样本集eq?D%3D%5Cleft%20%5C%7B%28x_1%2Cy_1%29%2C%28x_2%2Cy_2%29...%2C%28x_m%2Cy_m%29%20%5Cright%20%5C%7Deq?y_i%5Cin%20%5Cleft%20%5C%7B%20-1%2C+1%20%5Cright%20%5C%7D支持向量机基于训练集D在样本空间中找到一个划分超平面,将不同类别的样本分开。支持向量机(SVM) 的基本思想就是想要找到一个划分超平面,以 “最好地” 区分这两类点,以至如果以后有了新的点也能做出很好的分类。因此支持向量机适用于二分类模型

1.2 间隔与支持向量

在样本空间中,划分超平面可以通过线性方程来描述:  eq?w%5E%7BT%7Dx+b%3D0

其中eq?w%3D%28w_1%3Bw_2%3B...%3Bw_d%29为法向量,决定了超平面的方向;b为位移项,决定了超平面与原点之间的距离。样本空间中任意点x到超平面(w,b)的距离表示为eq?r%3D%5Cfrac%7B%5Cleft%20%7C%20w%5E%7BT%7Dx+b%20%5Cright%20%7C%7D%7B%5Cleft%20%5C%7C%20w%20%5Cright%20%5C%7C%7D

假设超平面(w,b)能将训练样本正确分类,即对于eq?%5Cleft%20%28%20x_i%2Cy_i%20%5Cright%20%29%5Cin%20Deq?y_i%5Cleft%20%28%20wx_i%20+b%5Cright%20%29-1%5Cgeqslant%200

如下图所示,距离超平面最近的这几个训练样本使上式成立,则被称为“支持向量”

两个异类支持向量到超平面的距离之和为eq?%5Cgamma%20%3D%5Cfrac%7B2%7D%7B%5Cleft%20%5C%7C%20w%20%5Cright%20%5C%7C%7D,被称为“间隔”

具有“最大间隔”的划分超平面所产生的分类结果是最鲁棒的,对未见示例的泛化能力最强。想要找到具有“最大间隔”的超平面,即找到满足条件的参数w和b,使得γ最大,仅需最大化eq?%5Cleft%20%5C%7C%20w%20%5Cright%20%5C%7C%5E%7B-1%7D,这等价于最小化eq?%5Cleft%20%5C%7C%20w%20%5Cright%20%5C%7C%5E%7B2%7D

6cfc29bb5f86485dbd386c497e661a64.png

bb6085db9c76438ba60dee168f34fffb.jpeg

支持向量与间隔

1.3 算法流程

(1)收集数据

(2)准备数据:需要数值型数据

(3)分析数据:有助于可视化分割超平面

(4)训练算法:SVM的大部分时间都源自训练,该过程主要实现两个参数的调优

(5)测试算法

(6)使用算法:解决分类问题

1.4 对偶问题

对最小化eq?%5Cleft%20%5C%7C%20w%20%5Cright%20%5C%7C%5E%7B2%7D公式使用拉格朗日乘子法可得到其"对偶问题".即对公式中每条约束添加拉格朗日乘子eq?%5Calpha%20_%7Bi%7D≥0,则该问题的拉格朗日函数可写为

eq?L%28w%2Cb%2C%5Calpha%20%29%3D%5Cfrac%7B1%7D%7B2%7D%5Cleft%20%5C%7C%20w%20%5Cright%20%5C%7C%5E%7B2%7D+%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%5Calpha%20_%7Bi%7D%5Cleft%20%28%201-y_%7Bi%7D%5Cleft%20%28%20w%5E%7BT%7Dx_%7Bi%7D+b%20%5Cright%20%29%20%5Cright%20%29

其中eq?%5Calpha%20%3D%5Cleft%20%28%20%5Calpha%20_%7B1%7D%3B%5Calpha%20_%7B2%7D%3B%20...%3B%5Calpha%20_%7Bm%7D%5Cright%20%29.令L(w,b,α)对w和b的偏导为0可得    eq?w%3D%5Csum_%7Bi%3D1%7D%5E%7Bm%7Da_%7Bi%7Dy_%7Bi%7Dx_%7Bi%7D

eq?0%3D%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%5Calpha%20_%7Bi%7Dy_%7Bi%7D。代入到L(w,b,α)中可将w和b消去,得到对偶问题

eq?max_%7B%5Calpha%20%7D%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%5Calpha%20_%7Bi%7D-%5Cfrac%7B1%7D%7B2%7D%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%5Csum_%7Bj%3D1%7D%5E%7Bm%7D%5Calpha%20_%7Bi%7D%5Calpha%20_%7Bj%7Dy_%7Bi%7Dy_%7Bj%7Dx_%7Bi%7D%5E%7BT%7Dx_%7Bj%7D

eq?s.t.%20%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%5Calpha%20_%7Bi%7Dy_%7Bi%7D%3D0       eq?%5Calpha%20_%7Bi%7D%5Cgeqslant%200%2Ci%3D1%2C2%2C...%2Cm

解出α后,求出w和b即可得到模型eq?f%28x%29%3Dw%5E%7BT%7Dx+b%20%3D%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%5Calpha%20_%7Bi%7Dy_%7Bi%7Dx_%7Bi%7D%5E%7BT%7Dx+b

对于任意样本eq?%28x_i%2Cy_i%29,总有eq?%5Calpha%20_%7Bi%7D=0或eq?y_%7Bi%7Df%28x_%7Bi%7D%29%3D1

eq?%5Calpha%20_%7Bi%7D=0,则该样本将不会再上式模型求和公式中出现,也就不会对f(x)有任何影响

eq?%5Calpha%20_%7Bi%7D>0,则必有eq?y_%7Bi%7Df%28x_%7Bi%7D%29%3D1,所对应的样本点位于最大间隔边界上,是一个支持向量。

这显示出支持向量机的一个重要性质:训练完成后,大部分的训练样本都不需要保留,最终模型仅与支持向量有关。

二、算法实现

2.1 实现SVM分类器

定义一个名为LinearSVM的类,用于实现线性支持向量机算法。其中包含类的初始化方法、fit方法和predict方法。

在类的初始化方法中,根据公式eq?L%28w%2Cb%29%3D%5Csum_%7Bi%3D1%7D%5E%7Bn%7Dmax%280%2C1-y_%7Bi%7D%28wx_%7Bi%7D+b%29%29+%5Clambda%20%5Cleft%20%5C%7C%20w%20%5Cright%20%5C%7C%5E%7B2%7D设置学习率、正则化参数和迭代次数等参数。

fit方法,用于训练模型。首先获取输入数据的样本数和特征数,然后将标签y转换为-1和1。接着初始化权重w和偏置b为0。然后进行迭代,遍历每个样本,检查分类条件。如果满足条件,即样本正确分类且在正确的间隔之外(eq?y_%7Bi%7D%28wx_%7Bi%7D%29+b%5Cgeq%201),则根据公式eq?%5Cfrac%7B%5Cpartial%20L%7D%7B%5Cpartial%20w%7D%3D2%5Clambda%20w仅通过正则项更新权重;否则,当样本错误分类或在间隔内(eq?y_%7Bi%7D%28wx_%7Bi%7D%29%3C%201),则根据公式eq?%5Cfrac%7B%5Cpartial%20L%7D%7B%5Cpartial%20w%7D%3D2%5Clambda%20w-y_%7Bi%7Dx_%7Bi%7Deq?%5Cfrac%7B%5Cpartial%20L%7D%7B%5Cpartial%20b%7D%3D-y_%7Bi%7D 同时更新权重和偏置。

predict方法,用于预测输入数据X的标签。计算线性输出,然后返回其符号作为预测结果。

class LinearSVM:
    def __init__(self,learning_rate=0.0001,lambda_param=0.1,n_iters=1000):
        self.learning_rate=learning_rate#学习率
        self.lambda_param=lambda_param#正则化参数
        self.n_iters=n_iters#迭代次数
        self.w=None
        self.b=None

    def fit(self,X,y):#fit方法用于模型训练
        n_samples,n_features=X.shape
        y_ =np.where(y<=0,-1,1)

        #初始化权重和偏置为0
        self.w=np.zeros(n_features)
        self.b=0

        for _ in range(self.n_iters):
            for idx,x_i in enumerate(X):
                condition = y_[idx]*(np.dot(x_i,self.w)-self.b)>=1 #遍历每个样本,检查分类条件
                if condition:
                    self.w-=self.learning_rate*(2*self.lambda_param*self.w)#如果样本被正确分类且在正确的间隔外,仅通过正则项更行权重(防止过拟合)
                else:
                    self.w-=self.learning_rate*(2*self.lambda_param*self.w-np.dot(x_i,y_[idx]))#样本被错误分类或者在间隔内,则权重更新包括误差项,同时更新偏置,使样本在未来被正确分类
                    self.b-=self.learning_rate*y_[idx]

    def predict(self,X):
        Linear_output=np.dot(X,self.w)-self.b
        return np.sign(Linear_output)

2.2 实现结果可视化

定义一个名为plot_hyperplane的函数,用于绘制数据集和分类超平面。首先绘制散点图,然后获取坐标轴的范围,生成网格点,计算网格点的Z值,最后绘制等高线。

#结果可视化    
def plot_hyperplane(X,y,w,b):
        plt.scatter(X[:,0],X[:,1],marker='o',c=y,s=100,edgecolors='k',cmap='winter')
        ax=plt.gca()
        xlim=ax.get_xlim()
        ylim=ax.get_ylim()
        xx=np.linspace(xlim[0],xlim[1],30)
        yy=np.linspace(ylim[0],ylim[1],30)
        YY,XX=np.meshgrid(yy,xx)
        xy=np.vstack([XX.ravel(),YY.ravel()]).T
        Z=(np.dot(xy,w)-b).reshape(XX.shape)
        ax.contour(XX,YY,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestyles=['--','-','--'])
        plt.show()

2.3 准备数据 训练模型

使用sklearn.datasets中的make_blobs函数生成一个包含100个样本、2个中心点的数据集。将标签y调整为-1和1,然后创建一个LinearSVM实例,调用fit方法进行训练。再调用plot_hyperplane函数,可视化训练结果。

#数据准备和模型训练
X,y=datasets.make_blobs(n_samples=100,centers=2,random_state=6)
y=np.where(y==0,-1,1)#调整标签为-1和1
svm0=LinearSVM()
svm0.fit(X,y)
plot_hyperplane(X,y,svm0.w,svm0.b) # type: ignore

2.4 实验结果截图

0b1edd06d82b44b4bb13b5370db31b38.png

SVM(含学习率、正则化参数)

2.5 调整参数C优化分类结果

eq?L%28w%2Cb%29%3DC%5Csum_%7Bi%3D1%7D%5E%7Bm%7Dmax%280%2C1-y_i%28wx_i&plus;b%29%29&plus;%5Clambda%20%5Cleft%20%5C%7C%20w%20%5Cright%20%5C%7C%5E%7B2%7D

参数C控制了错误分类训练示例的惩罚。参数C越大,则告诉SVM要对所有的例子进行正确的分类。

当C比较小时,模型对错误分类的惩罚较小,比较松弛,样本点之间的间隔就比较大,可能产生欠拟合的情况

当C比较大时,模型对错误分类的惩罚较大,两组数据之间的间隔就小,容易产生过拟合的情况

C值越大,越不容易放弃那些离群点;C值越小,越不重视那些离群点。

#调整C参数看不同的效果
class LinearSVM1:
    def __init__(self,C=1.0,learning_rate=0.0001,n_iters=1000):
        self.C=C
        self.learning_rate=learning_rate
        self.n_iters=n_iters
        self.w=None
        self.b=None

    def fit(self,X,y):
        n_samples,n_features=X.shape
        y_ =np.where(y<=0,-1,1)

        #初始化权重和偏置为0
        self.w=np.zeros(n_features)
        self.b=0

        for _ in range(self.n_iters):
            for idx,x_i in enumerate(X):
                condition = y_[idx]*(np.dot(x_i,self.w)-self.b)>=1 #遍历每个样本,检查分类条件
                if condition:
                    self.w-=self.learning_rate*(2*self.w)#如果样本被正确分类且在正确的间隔外,仅通过正则项更行权重(防止过拟合)
                else:
                    self.w-=self.learning_rate*((2*self.w)-self.C*np.dot(x_i,y_[idx]))#样本被错误分类或者在间隔内,则权重更新包括误差项,同时更新偏置,使样本在未来被正确分类
                    self.b-=self.learning_rate*self.C*y_[idx]

    def predict(self,X):
        Linear_output=np.dot(X,self.w)-self.b
        return np.sign(Linear_output)
    
    #数据准备和模型训练
X,y=datasets.make_blobs(n_samples=100,centers=2,random_state=6)
y=np.where(y==0,-1,1)#调整标签为-1和1
svm1=LinearSVM1(C=200)
svm1.fit(X,y)
plot_hyperplane(X,y,svm1.w,svm1.b) # type: ignore

2.6 实验结果截图(可调整参数C)

当C=20时,SVM分类结果如下图所示

cb6ffc49196049a7b6ea2920913e3704.png

参数C=20

当C=100时,SVM分类结果如下图所示

4461cb0e8dfa4ee1a6abff3563b5e39e.png

参数C=100

当C=200时,SVM分类结果如下图所示

5fd388c636e74821bfaec1e536e56a2b.png

参数C=200

2.7 整体代码

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

class LinearSVM:
    def __init__(self,learning_rate=0.0001,lambda_param=0.1,n_iters=1000):
        self.learning_rate=learning_rate#学习率
        self.lambda_param=lambda_param#正则化参数
        self.n_iters=n_iters#迭代次数
        self.w=None
        self.b=None

    def fit(self,X,y):#fit方法用于模型训练
        n_samples,n_features=X.shape
        y_ =np.where(y<=0,-1,1)

        #初始化权重和偏置为0
        self.w=np.zeros(n_features)
        self.b=0

        for _ in range(self.n_iters):
            for idx,x_i in enumerate(X):
                condition = y_[idx]*(np.dot(x_i,self.w)-self.b)>=1 #遍历每个样本,检查分类条件
                if condition:
                    self.w-=self.learning_rate*(2*self.lambda_param*self.w)#如果样本被正确分类且在正确的间隔外,仅通过正则项更行权重(防止过拟合)
                else:
                    self.w-=self.learning_rate*(2*self.lambda_param*self.w-np.dot(x_i,y_[idx]))#样本被错误分类或者在间隔内,则权重更新包括误差项,同时更新偏置,使样本在未来被正确分类
                    self.b-=self.learning_rate*y_[idx]

    def predict(self,X):
        Linear_output=np.dot(X,self.w)-self.b
        return np.sign(Linear_output)

#结果可视化    
def plot_hyperplane(X,y,w,b):
        plt.scatter(X[:,0],X[:,1],marker='o',c=y,s=100,edgecolors='k',cmap='winter')
        ax=plt.gca()
        xlim=ax.get_xlim()
        ylim=ax.get_ylim()
        xx=np.linspace(xlim[0],xlim[1],30)
        yy=np.linspace(ylim[0],ylim[1],30)
        YY,XX=np.meshgrid(yy,xx)
        xy=np.vstack([XX.ravel(),YY.ravel()]).T
        Z=(np.dot(xy,w)-b).reshape(XX.shape)
        ax.contour(XX,YY,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestyles=['--','-','--'])
        plt.show()
    
#数据准备和模型训练
X,y=datasets.make_blobs(n_samples=100,centers=2,random_state=6)
y=np.where(y==0,-1,1)#调整标签为-1和1
svm0=LinearSVM()
svm0.fit(X,y)
plot_hyperplane(X,y,svm0.w,svm0.b) # type: ignore


#调整C参数看不同的效果
class LinearSVM1:
    def __init__(self,C=1.0,learning_rate=0.0001,n_iters=1000):
        self.C=C
        self.learning_rate=learning_rate
        self.n_iters=n_iters
        self.w=None
        self.b=None

    def fit(self,X,y):
        n_samples,n_features=X.shape
        y_ =np.where(y<=0,-1,1)

        #初始化权重和偏置为0
        self.w=np.zeros(n_features)
        self.b=0

        for _ in range(self.n_iters):
            for idx,x_i in enumerate(X):
                condition = y_[idx]*(np.dot(x_i,self.w)-self.b)>=1 #遍历每个样本,检查分类条件
                if condition:
                    self.w-=self.learning_rate*(2*self.w)#如果样本被正确分类且在正确的间隔外,仅通过正则项更行权重(防止过拟合)
                else:
                    self.w-=self.learning_rate*((2*self.w)-self.C*np.dot(x_i,y_[idx]))#样本被错误分类或者在间隔内,则权重更新包括误差项,同时更新偏置,使样本在未来被正确分类
                    self.b-=self.learning_rate*self.C*y_[idx]

    def predict(self,X):
        Linear_output=np.dot(X,self.w)-self.b
        return np.sign(Linear_output)
    
    #数据准备和模型训练
X,y=datasets.make_blobs(n_samples=100,centers=2,random_state=6)
y=np.where(y==0,-1,1)#调整标签为-1和1
svm1=LinearSVM1(C=200)
svm1.fit(X,y)
plot_hyperplane(X,y,svm1.w,svm1.b) # type: ignore

三、总结分析

3.1 实验中出现的问题

问题一:在定义LinearSVM类时,__init__方法的名称写错了,导致运行时在尝试访问LinearSVM对象的n_iters属性时,发现该对象并没有这个属性。

0ded7091e73841a59c0f57a5ae6b266c.png

问题二:在调用plot_hyperplane函数时,没有使用类的实例名svm来调用。

b3a74646a97d4a2083b2b63dada7e280.png

3.2 实验小结

本次实验我们完成了对于支持向量机SVM分类器的基本实现。SVM 适合中小型数据样本、非线性、高维的分类问题。SVM是一种基于边界的算法,它依赖于少数支持向量,因此对于小样本数据集具有较好的泛化能力。SVM使用核函数将输入数据映射到高维空间,从而可以解决非线性问题。常用的核函数包括线性核、多项式核和径向基函数(RBF)核等。但是对于大规模数据集,SVM的计算复杂度随着样本数量的增加而增加。同时,当SVM在处理含有缺失数据的样本时,分类结果可能不佳。

3.3 参考书籍

《机器学习》 周志华

《机器学习实战》     Peter Harrington

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值