目录
一、引言
支持向量机(Support Vector Machine, SVM)是一种强大的机器学习算法,广泛应用于分类、回归以及异常值检测等任务中。本次实验旨在通过实践操作,深入了解SVM算法的原理、实现方式及其在实际应用中的效果。
二、算法概述
2.1 算法简介
支持向量机(SVM)是一种基于统计学习理论的分类方法,其核心思想是通过寻找一个超平面来对样本进行分割,使得不同类别的样本位于超平面的两侧,并且使得离超平面最近的样本点到超平面的距离最大化。这个超平面被称为最大间隔超平面,而离超平面最近的样本点被称为支持向量。
SVM算法可以分为线性可分SVM、线性SVM和非线性SVM三种类型。当训练样本线性可分时,通过硬间隔最大化学习一个线性可分SVM;当训练样本近似线性可分时,通过软间隔最大化学习一个线性SVM;当训练样本线性不可分时,则通过核技巧和软间隔最大化学习一个非线性SVM。
2.2 间隔和支持向量
支持向量:距离超平面最近的样本点;
间隔:两个异类支持向量到超平面的距离之和。
三、算法实现
3.1实验步骤
本次实验采用Python编程语言,使用sklearn库中的SVM模块进行算法实现。具体实现步骤如下:
- 数据准备:选择适当的数据集,并进行预处理,包括数据清洗、特征选择等。
- 导入SVM模块:使用sklearn库中的SVC类实现SVM算法。
- 数据划分:将数据集划分为训练集和测试集,以便进行模型训练和评估。
- 训练SVM分类器:使用训练集对SVM分类器进行训练,并调整模型参数以获得最佳性能。
- 模型评估:使用测试集对训练好的SVM分类器进行评估,计算分类准确率等指标。
3.2 实现SVM分类器
定义一个名为LinearSVM的类,用于实现线性支持向量机算法。其中包含类的初始化方法、fit方法和predict方法。
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)
3.3 实现结果可视化
定义一个名为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()
3.4 准备数据 训练模型
使用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
3.5 实验结果截图
3.6 调整参数C优化分类结果
参数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
3.7 实验结果截图(可调整参数C)
当C=20时,SVM分类结果如下图所示:
参数C=20
当C=100时,SVM分类结果如下图所示:
参数C=100
当C=200时,SVM分类结果如下图所示:
3.8 整体代码
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
四、总结分析
通过本次实验,我们深入了解了SVM算法的原理和实现方式,并成功地将其应用于手写数字识别任务中。实验结果表明,SVM算法在处理高维数据时具有较好的性能,并且能够通过核函数技巧处理非线性分类问题。此外,SVM算法还具有较好的泛化能力,能够有效地避免过拟合现象。
然而,SVM算法也存在一些缺点。首先,SVM算法的计算复杂度较高,特别是在处理大规模数据集时,其训练速度可能会受到较大影响。其次,SVM算法对参数的选择较为敏感,不同的参数设置可能会导致模型性能的较大差异。因此,在实际应用中,我们需要根据具体任务和数据特点来选择合适的SVM算法和参数设置。
综上所述,SVM算法是一种强大而有效的机器学习算法,在分类任务中具有较好的性能和泛化能力。在未来的研究中,我们可以进一步探索SVM算法的优化和改进方法,以提高其在处理大规模数据集和复杂任务时的性能和效率。