支持向量机(SVM)算法-补充说明

之前我有写过一篇关于svm的使用流程和基本概念讲解——支持向量机(SVM)算法之分类实操。不过最近又接触了一些关于svm的基础概念和预处理数据的使用,所以在这里做一下简单地补充。在接触本篇文章之前,建议先去看完支持向量机(SVM)算法之分类实操,一些我之前讲过的东西,这里就不在赘述了。

核技巧

首先需要声明的一点是,向数据表示中添加非线性特征,可以让线性模型变得更强大。但是,通常来说我们并不知道要添加哪些特征,而添加许多特征(比如100维特征空间所有可能的交互项)的计算开销可能会很大。幸运的是,有一种巧妙的数学技巧,让我们可以在更高维空间中学习分类器,而不用实际计算可能非常大的新的数据表示。这种技巧叫做核技巧,它的原理是直接计算特征表示中数据点之间的距离(更准确地说是内积),而不用实际对扩展进行计算。

对于支持向量机,将数据映射到更高维空间中有两种常用的办法,也就是在支持向量机(SVM)算法之分类实操中提到过的核参数:一种是多项式核,在一定阶数内计算原始特征所有可能的多项式(比如feature1*2+feature2*2);另一种是径向基函数(RBF)核,也叫高斯核。高斯核有点难以解释,因为它对应无限维的特征空间。一种对高斯核的解释是它考虑所有阶数的所有可能的多项式,但阶数越高,特征的重要性越小。

当然在实际中,核svm背后的数学细节并不是很重要。

理解SVM

与线性模型相比,核支持向量机(通常简称为SVM)是可以推广到更复杂模型的扩展,这些模型无法被输入线性函数进行定义,譬如下面的数据:

在这里插入图片描述

在这里插入图片描述

如上图所示,在训练过程中,SVM学习每个数据点对于表示两个类别之间的决策边界的重要性。通常只有一部分训练数据点对于定义决策边界来说很重要:位于类别之间边界上的那些点。这些点叫做支持向量,支持向量机正是由此得名。

想要对新样本点进行预测,需要测量它与每个支持向量之间的距离。分类决策是基于它与支持向量之间的距离以及在训练过程中学到的支持向量重要性来做出的。

数据点之间的距离有高斯核给出:

k(x1,x2)=exp(-y||x1-x2||^2)
x1,x2——数据点;||x1-x2||——表示欧式距离;y(gamma)——控制高斯核宽度的参数

预处理数据-数据缩放

首先我们先对一个数据集胎儿健康分类进行简单的建模,这个数据之前有讲到过,在这里就不在赘述了,具体代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd
import numpy as np
import winreg
from sklearn.model_selection import train_test_split
from sklearn import svm
from sklearn.metrics import accuracy_score
###################
real_address = winreg.OpenKey(winreg.HKEY_CURRENT_USER,r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',)
file_address=winreg.QueryValueEx(real_address, "Desktop")[0]
file_address+='\\'
file_origin=file_address+"\\源数据-分析\\fetal_health.csv"
health=pd.read_csv(file_origin)
#设立桌面绝对路径,读取源数据文件,这样将数据直接下载到桌面上就可以了,省得还要去找
###################
train=health.drop(["fetal_health"],axis=1)
X_train,X_test,y_train,y_test=train_test_split(train,health["fetal_health"],random_state=1)
###考虑到接下来可能需要进行其他的操作,所以定了一个随机种子,保证接下来的train和test是同一组数
svm_Notlinear=svm.SVC(C=1,kernel="rbf",decision_function_shape="ovr")
svm_Notlinear.fit(X_train,y_train)
print("SVM训练模型评分:"+str(accuracy_score(y_train,svm_Notlinear.predict(X_train))))
print("SVM待测模型评分:"+str(accuracy_score(y_test,svm_Notlinear.predict(X_test))))

结果如下所示:

在这里插入图片描述

虽然SVM的表现通常都很好,但它对参数的设定和数据的缩放非常敏感。特别的,它要求所有特征有相似的变化范围。解决这个问题的一种方法就是对每个特征进行缩放,使其大致都位于一个范围。核SVM常用的缩放方法就是将所有特征缩放到0到1之间,实际上MinMaxScaler预处理方法就可以做到这一点,但是为了大家更好地理解,这里我们手动缩放一下,代码如下所示:

1
2
3
4
5
6
7
8
range_train=(X_train-X_train.min()).max(axis=0)###计算各个特征值的最大距离
scaled_train=(X_train-X_train.min(axis=0))/range_train###看看各个特征维度里面的个体数值距离与最大距离的比,这样所有的特征个体数据都在0到1之间
scaled_test=(X_test-X_train.min(axis=0))/range_train###测试集进行同样的缩放,注意是利用训练集缩放测试集
##############################################
svm_Notlinear=svm.SVC(C=1,kernel="rbf",decision_function_shape="ovr")
svm_Notlinear.fit(scaled_train,y_train)
print("SVM训练模型评分:"+str(accuracy_score(y_train,svm_Notlinear.predict(scaled_train))))
print("SVM待测模型评分:"+str(accuracy_score(y_test,svm_Notlinear.predict(scaled_test))))

在这里插入图片描述

从上面的结果可以看出,模型精度较之前有了提高。在实际应用过程中,我们会遇到各种各样的数据,如果我们不能保证数据特征的统一性,那么数据缩放就会取到非常大的作用!

参数

之前我们在支持向量机(SVM)算法之分类实操中已经讲过各种核函数的适用范围,这里就不在多说了,如下图所示:

在这里插入图片描述

下面说一下另外两个参数C和gamma。

gamma是核技巧中给出的公式中的参数,用于控制高斯核的宽度。它决定了点与点之间“靠近”是指多大的距离。C是正则化参数,与线性模型中用到的类似。它限制每个点的重要性。

gamma较小,说明高斯核的半径较大,许多点都被看作比较接近。小的gamma值表示决策边界变化很慢,生成的是复杂度较低的模型,而大的gamma值则会生成更为复杂的模型。

至于参数C,与线性模型相同,如果C值很小,说迷你跟模型非常受限,每个数据点的影响范围都有限。

一般情况下,SVM中默认C=1,gamma=1/n_features。

接下来,我们来分别调节这两个参数:

1
2
3
4
5
6
result_gamma=pd.DataFrame(columns=["gamma","SVM训练模型评分","SVM待测模型评分"])
for i in range(1,100):
svm_Notlinear=svm.SVC(C=1,gamma=i/10,kernel="rbf",decision_function_shape="ovr")
svm_Notlinear.fit(scaled_train,y_train)
result_gamma=result_gamma.append([{"gamma":i/10,"SVM训练模型评分":accuracy_score(y_train,svm_Notlinear.predict(scaled_train)),"SVM待测模型评分":accuracy_score(y_test,svm_Notlinear.predict(scaled_test))}])
result_gamma[result_gamma["SVM待测模型评分"]==result_gamma["SVM待测模型评分"].max()]

在这里插入图片描述

接下来,在保证gamma值不变的情况下,调节C参数:

1
2
3
4
5
6
result_C=pd.DataFrame(columns=["C","SVM训练模型评分","SVM待测模型评分"])
for i in range(1,500,10):
svm_Notlinear=svm.SVC(C=i,gamma=3.1,kernel="rbf",decision_function_shape="ovr")
svm_Notlinear.fit(scaled_train,y_train)
result_C=result_C.append([{"C":i,"SVM训练模型评分":accuracy_score(y_train,svm_Notlinear.predict(scaled_train)),"SVM待测模型评分":accuracy_score(y_test,svm_Notlinear.predict(scaled_test))}])
result_C[result_C["SVM待测模型评分"]==result_C["SVM待测模型评分"].max()]

在这里插入图片描述

至此,这个SVM的模型就简单地调节好了。

小结

核支持向量机是非常强大的模型,在各种数据集上的表现都很好。SVM允许决策边界很复杂,即使数据只有几个特征。它在低维数据和高维数据(即很少特征和很多特征)上的表现都很好,但对样本个数的缩放表现不好。在有多达10000个样本的数据上运行SVM可能表现良好,但如果数据量达到1000000甚至更大,在运行时间和内存使用方面可能会面临挑战。

SVM的另一个缺点是,预处理数据和调参都需要非常小心。这也是为什么如今很多应用中用的都是基于树的模型,比如随机森林或梯度提升(需要很少的预处理,甚至不需要预处理)。此外,SVM模型很难检查,可能很难理解为什么会这么预测,而且也很难将模型向非专家进行解释。

不过SVM仍然是值得尝试的,特别是所有特征的测量单位相似(比如都是像素密度),而且范围也差不多时。

核SVM的重要参数是正则化参数C,核的选择以及与核相关的参数。虽然我们主要讲的是RBF核,但scikit-learn中还有其它选择。RBF核只有一个参数gamma,它是高斯核宽度的倒数。gamma和C控制的都是模型复杂度,较大的值都对应更为复杂的模型。因此,这两个参数的设定通常是强烈相关的,应该同时调节。

有很多地方做的不是很好,欢迎网友来提出建议,也希望可以遇到些朋友来一起交流讨论。