SVM模型的深入理解与运用(Python与C#实现)

今天在准备SVM的一个答辩。于是重新深入理解了SVM的原理,从去工作的学长那里了解到现在的面试都已经开始询问SVM的数学原理,而SVM 的学习难点可能在于算法的理解和理论推导,而几个月前由于数学水平的不足而一知半解,现重新回顾,也是有了不一样的体验。并运用C#和Python分别对SVM完成了一定场景情况下的实现预测。

现在机器学习的缺口已经接近饱和,深度学习也只剩于百分之八到九左右,相对于近些年的深度学习,SVM作为传统的机器学习现在仍然具有很深远的指导意义。

下面是对于SVM的简单介绍:

SVM Support Vector Machine 的简称,它的中文名为支持向量机,属于一种有监督的机器学习算法,可用于离散因变量的分类和连续因变量的预测。通常情况下,该算法相对于其他单一的 分类算法(如 Logistic 回归、决策树、朴素贝叶斯、 KNN 等)会有更好的预测准确率,主要是因 为它可以将低维线性不可分的空间转换为高维的线性可分空间。由于该算法具有较高的预测准确率,所以其备受企业界的欢迎,如利用该算法实现医疗诊断、图像识别、文本分类、市场营销等。
该算法的思想就是利用某些支持向量所构成的“超平面”,将不同类别的样本点进行划分。
不管样本点是线性可分的、近似线性可分的还是非线性可分的,都可以利用“超平面”将样本点以较高的准确度切割开来。需要注意的是,如果样本点为非线性可分,就要借助于核函数技术,实现样本在核空间下完成线性可分的操作。关键是“超平面”该如何构造,这在本章的内容中会有所介绍。
运用 SVM 模型对因变量进行分类或预测时具有几个显著的优点:例如,由于 SVM 模型最终所形成的分类器仅依赖于一些支持向量,这就导致模型具有很好的鲁棒性(增加或删除非支持向量 的样本点,并不会改变分类器的效果)以及避免“维度灾难”的发生(模型并不会随数据维度的提 升而提高计算的复杂度);模型具有很好的泛化能力,一定程度上可以避免模型的过拟合;也可以 避免模型在运算过程中出现的局部最优。当然,该算法的缺点也是明显的,例如模型不适合大样本 的分类或预测,因为它会消耗大量的计算资源和时间;模型对缺失样本非常敏感,这就需要建模前 清洗好每一个观测样本;虽然可以通过核函数解决非线性可分问题,但是模型对核函数的选择也同 样很敏感; SVM 为黑盒模型(相比于回归或决策树等算法),对计算得到的结果无法解释。
而SVM又可分为线性可分SVM、线性SVM、非线性可分SVM。当然对于绝大多数问题都是非线性的。 让我们先看一个利用Python实现的SVM预测问题的案例。
这个案例是一个关于森林火灾方面的预测,该数据集一共包含 517 条火灾记录和 13 个变量,其中变量 area 为因变量,表示火灾产生的森林毁坏面积,其余变量主要包含火灾发生的坐标位置、时间、各项火险天气指标、气温、湿度、风力等信息。接 下来利用 SVM 模型对该数据集的因变量做预测分析。所用数据集来自UCI网站。进行SVM预测分析时我们先得进行数据的处理。
# 读取外部数据
forestfires = pd.read_csv(r'C:\Users\Administrator\Desktop\forestfires.csv')
# 数据前 5 行
forestfires.head()

火灾发生的时间( month day )为字符型的变量,如果将这样的变量带入模型中,就必须对其做数值化转换。考虑到月份可能是火灾发生的一个因素,故将该变量做保留处理,而将 day 变量删除。数据清洗如下:
# 删除 day 变量
forestfires.drop('day',axis = 1, inplace = True)
# 将月份做数值化处理
forestfires.month = pd.factorize(forestfires.month)[0]
# 预览数据前 5 行
forestfires.head()

day 变量已被删除,而且 month 变量也成为数值型变量。表中的应变量为 area 是一个数值型变量,通常都需要对连续型的因变量做分布的探索性分析,如果数据呈现严重的偏态,而不做任何的修正时,直接带入到模型将会产生很差的效果。不妨这里使用直方图直观感受 area 变量的分布形态,由以上我们也看出了数据处理的重要性,操作代码如下:
# 导入第三方模块
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import norm
# 绘制森林烧毁面积的直方图
sns.distplot(forestfires.area, bins = 50, kde = True, fit = norm, 
 hist_kws = {'color':'steelblue'}, 
 kde_kws = {'color':'red', 'label':'Kernel Density'}, 
 fit_kws = {'color':'black','label':'Nomal', 'linestyle':'--'})
# 显示图例
plt.legend()
# 显示图形
plt.show()

这里呢大家如果对Pandas和Matplotlib不熟悉可以去了解一些它们的基本操作 。

对于直方图,从分布来看,数据呈现严重的右偏。建模时不能够直接使用该变量,一般都会将数据做对数处理,代码如下:

# 导入第三方模块
from sklearn import preprocessing
import numpy as np
# 对 area 变量做对数变换
y = np.log1p(forestfires.area)
# 将 X 变量做标准化处理
predictors = forestfires.columns[:-1]
X = preprocessing.scale(forestfires[predictors])
接下来基于上面清洗后的数据将其拆分为两部分,分别用于模型的构建和测试。需要注意的是,在建模时必须对参数 做调优处理,因为默认的 SVM 模型参数并不一定是最好的。代码如下:
# 将数据拆分为训练集和测试集
X_train,X_test,y_train,y_test = model_selection.train_test_split(X, y, test_size = 0.25, 
random_state = 1234)
# 构建默认参数的 SVM 回归模型
svr = svm.SVR()
# 模型在训练数据集上的拟合
svr.fit(X_train,y_train)
# 模型在测试上的预测
pred_svr = svr.predict(X_test)
# 计算模型的 MSE
metrics.mean_squared_error(y_test,pred__svr)

out:
1.9258635953335212

# 使用网格搜索法,选择 SVM 回归中的最佳 C 值、epsilon 值和 gamma 值
epsilon = np.arange(0.1,1.5,0.2)
C= np.arange(100,1000,200)
gamma = np.arange(0.001,0.01,0.002)
parameters = {'epsilon':epsilon,'C':C,'gamma':gamma}
grid_svr = model_selection.GridSearchCV(estimator = svm.SVR(),param_grid =parameters,
 scoring='neg_mean_squared_error',
 cv=5,verbose =1, n_jobs=2)
# 模型在训练数据集上的拟合
grid_svr.fit(X_train,y_train)
# 返回交叉验证后的最佳参数值
print(grid_svr.best_params_, grid_svr.best_score_)
out:
{'C': 300, 'gamma': 0.001, 'epsilon': 1.1000000000000003} -1.99405794977
# 模型在测试集上的预测
pred_grid_svr = grid_svr.predict(X_test)
# 计算模型在测试集上的 MSE 值
metrics.mean_squared_error(y_test, pred_grid_svr)
out:
1.7455012238826526
如上结果所示,经过 5 重交叉验证后,非线性 SVM 回归的最佳惩罚系数 C 300 、最佳的gama 值为 1.1 、最佳的 Y 值为 0.001 ,而且模型在训练数据集上的负 MSE 值为 -1.994 。为了实现模型之间拟合效果的对比,构建了一个不做任何参数调整的 SVM 回归模型,并计算得到该模型在测试数 据集上的 MSE 值为 1.926 ,相比于经过调参之后的模型来说,这个值要高于 1.746 。进而可以说明, 在利用 SVM 模型解决分类或预测问题时,需要对模型的参数做必要的优化。
这是一个简单的Python的预测例子,等会我会展示利用C#进行的另一个更大更复杂的预测。
下面开始进行对SVM的深入理解:
SVM 分类器实质上就是由某些支持向量构成的最大间隔的“超平面”,即分割平面。读者可能觉得“超平面”这个词比较抽象,其实说穿了就是不同维度空间下的分割,例如 在一维空间中,如需将数据切分为两段,只需要一个点即可;在二维空间中,对于线性可分的样本 点,将其切分为两类,只需一条直线即可;在三维空间中,将样本点切分开来,就需要一个平面; 以此类推,在更高维度的空间内,可能就需要构造一个“超平面”将数据进行划分。
SVM可以说是做到了结构风险最小与间隔最大化。

如图我们可能希望在左边的图中寻找到一个隔离带将蓝色与红色分隔开,或是对于右图尽管红色与蓝色已经有一部分重叠,但我们希望还是能寻找到一个尽可能的隔离带。亦是在寻找隔离带的过程中引入了非线性,使得更高维,此时的模型更为复杂,加入了一些核之后,SVM分类器可以做到更为复杂 ,每每看到这些我就会感叹编程之美与神奇。

 如上图左图中的蓝色的球与红色的球不再是点,因为此时是存在噪声的,对于误差我们可以利用以中心点为圆表示。而为了使得测试误差最小化,我们让边界远远地躲开每一个数据点,此时这个球可以变得更大,分类界离得足够远因此分类仍然是正确的,即为几何距离足够大。上图课展示为追求黄色的隔离带以此减小结构风险。而对于右图我们希望将绿色与橙色分开寻找一个最好的分类线。对此有无数种方法,但是我们通过先验信息,希望能够足够远,那此时问题就变成了唯一性。而如何解决这样的问题在SVM上就是一套数学工具了。

可以想象的是,“隔离带”代表了模型划分样本点的能力或可信度,“隔离带”越宽,说明模型能够将样本点划分得越清晰,进而保证模型泛化能力越强,分类的可信度越高;反之,“隔离带”越窄,说明模型的准确率越容易受到异常点的影响,进而理解为模型的预测能力越弱,分类的可信度越低。

下面进行数学原理的简单介绍,对于市面上的资料从复杂入手容易让人困惑,我现在从简单入手,希望能对读者有所启发:

首先我们需要计算间隔多大。在两边都有点的情况下使得点到间隔带距离越大越好,那么两边一定是在中垂线的位置上。

 

界限上的无疑是最为重要的,除去此之外的就显得没有那么举足轻重了。而对于其他的点符合上面的不等式。那么这就是对于不等式下的一个优化求值问题,而且是区间上的优化问题而不是全值下的优化问题。限制住的区域无疑会给我们带来麻烦,受限制的优化问题。那么如何解决非线性的不等式的优化问题呢:转化成无约束条件下的优化问题——拉格朗日。

Min与Max之间并没有矛盾,因为它们维度不同。错分类点会被非常严厉地惩罚,因此此时不能有错误点。

这个转化问题通过拉格朗日也是极为精彩。约束条件下求极值。在生活中也有很多地方有拉格朗日的影子。对于约束条件被破坏的条件下资源的购买的代价就是拉格朗日乘法子的意义,而在支持向量机中这个乘子就是正无穷(错误的点) ,付出无穷代价。

如果找不到间隔呢?

前面是基于线性可分或近似线性可分的 SVM ,如果样本点无法通过某个线性 的“超平面”对其分割时,使用这两种 SVM 将对样本的分类产生很差的效果。这个时候就需要构 建非线性可分的 SVM ,该模型的核心思想就是把原始数据扩展到更高维的空间,然后基于高维空 间实现样本的线性可分。
假设在左图的二维空间中存在两种类别的样本点,不管以何种线性的“超平面”都无法对其进行正确分类;但如果将其映射到右图中的三维空间中,就可以恰到好处地将其区 分开来,而图中的切割平面就是三维空间下的线性可分“超平面”。
所以,非线性 SVM 模型的构建需要经过两个步骤,一个是将原始空间中的样本点映射到高维 的新空间中,另一个是在新空间中寻找一个用于识别各类别样本点线性“超平面”。假设原始空间 中的样本点为 𝑥 ,将样本通过某种转换 ϕ(𝑥) 映射到高维空间中。

运用SVM思维,SVM本质是一种正则化而不是优化。Regulization,是实现一种规范。不是为了训练集而是为了测试集的误差最小,因为数据存在噪声。尽管正则是永恒的主题,但我们总是希望总是能够有一条分割线将两个点分开。而如果对于拉格朗日乘子有了更深入的理解,我们会发现从前我们对于错误会让它们付出无穷的代价之后我们才能不付出高昂的代价,而现在变了,需要容纳这些错误,但是还是对这些错误得进行惩罚,但不是无穷的代价了,而是一定的代价。 根据出错的大小来进行惩罚。这就是具有容错能力我们又称之为——软间隔。下面是数学公式的推导相对于其他资料很容易理解。

事实上SVM同样可以处理无穷维的问题。 下面引出Kernal trick。

核函数

对于核函数的定义,可以 这样理解:假设原始空间中的两个样本点 ( 𝑥 𝑖 , 𝑥 𝑗 ) ,在其扩展到高维空间后,它们的内积ϕ( 𝑥 𝑖 ) ∙ ϕ( 𝑥 𝑗 ) 如果 等于样本点 ( 𝑥 𝑖 , 𝑥 𝑗 )在原始空间中某个函数的输出,那么该函数就称为核函数。
关于非线性可分 SVM 模型的功能实现,可以利用 Python 中的 sklearn 模块,读者可以通过调
svm 子模块中的 SVC 类轻松搞定。

升维打击与非线性分类。尽管SVM逐渐被深度学习所替代但它的思想永不过时。核函数带来的VC维特别容易过拟合,拟合能力强,但是 SVM本身的正则化又控制这种过拟合(对出错的点的惩罚力度、也可以在非线性项之前加入乘子控制线性到非线性的转变)这就是SVM的强大之处。

C#部分代码展示:

 由于事情太多我会对于最近的两篇文章会进行再一次地完善和修改。接下来我打算写点最近在研究的基于改进的的EAST算法的文本识别和Vue尚硅谷的精华重点以及Flask框架,对于元学习的论文和算法也不会停,敬请各位对于文章中的错误进行斧正,对此表示感谢。

  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

talentstars

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

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

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

打赏作者

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

抵扣说明:

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

余额充值