机器学习经典算法之SVM

SVM 的英文叫 Support Vector Machine,中文名为支持向量机。它是常见的一种分类方法,在机器学习中,SVM 是有监督的学习模型。
什么是有监督的学习模型呢?它指的是咱们须要事先对数据打上分类标签,这样机器就知道这个数据属于哪一个分类。一样无监督学习,就是数据没有被打上分类标签,这多是由于咱们不具有先验的知识,或者打标签的成本很高。因此咱们须要机器代咱们部分完成这个工做,好比将数据进行聚类,方便后续人工对每一个类进行分析。SVM 做为有监督的学习模型,一般能够帮咱们模式识别、分类以及回归分析。

/*请尊重做者劳动成果,转载请标明原文连接:*/html

/* https://www.cnblogs.com/jpcflyer/p/11082443.html * /git

 

1、SVM 的工做原理
用 SVM 计算的过程就是帮咱们找到一个超平面,可以将样本区分的过程,这个超平面就是咱们的 SVM 分类器。
好比下图所示的直线 A、直线 B 和直线 C,究竟哪一种才是更好的划分呢?
很明显图中的直线 B 更靠近蓝色球,可是在真实环境下,球再多一些的话,蓝色球可能就被划分到了直线 B 的右侧,被认为是红色球。一样直线 A 更靠近红色球,在真实环境下,若是红色球再多一些,也可能会被误认为是蓝色球。因此相比于直线 A 和直线 B,直线 C 的划分更优,由于它的鲁棒性更强。
那怎样才能寻找到直线 C 这个更优的答案呢?这里,咱们引入一个 SVM 特有的概念: 分类间隔
 
实际上,咱们的分类环境不是在二维平面中的,而是在多维空间中,这样直线 C 就变成了决策面 C。
在保证决策面不变,且分类不产生错误的状况下,咱们能够移动决策面 C,直到产生两个极限的位置:如图中的决策面 A 和决策面 B。极限的位置是指,若是越过了这个位置,就会产生分类错误。这样的话,两个极限位置 A 和 B 之间的分界线 C 就是最优决策面。极限位置到最优决策面 C 之间的距离,就是“分类间隔”,英文叫作 margin。
 
若是咱们转动这个最优决策面,你会发现可能存在多个最优决策面,它们都能把数据集正确分开,这些最优决策面的分类间隔多是不一样的,而那个拥有“最大间隔”(max margin)的决策面就是 SVM 要找的最优解。
 
点到超平面的距离公式
在上面这个例子中,若是咱们把红蓝两种颜色的球放到一个三维空间里,你发现决策面就变成了一个平面。这里咱们能够用线性函数来表示,若是在一维空间里就表示一个点,在二维空间里表示一条直线,在三维空间中表明一个平面,固然空间维数还能够更多,这样咱们给这个线性函数起个名称叫作“超平面”。超平面的数学表达能够写成:
在这个公式里,w、x 是 n 维空间里的向量,其中 x 是函数变量;w 是法向量。法向量这里指的是垂直于平面的直线所表示的向量,它决定了超平面的方向。
SVM 就是帮咱们找到一个超平面 ,这个超平面能将不一样的样本划分开,同时使得样本集中的点到这个分类超平面的最小距离(即分类间隔)最大化。
在这个过程当中, 支持向量 就是离 分类超平面 最近的样本点,实际上若是肯定了支持向量也就肯定了这个超平面。因此支持向量决定了分类间隔究竟是多少,而在最大间隔之外的样本点,其实对分类都没有意义。
因此说, SVM 就是求解最大分类间隔的过程,咱们还须要对分类间隔的大小进行定义。
 
首先,咱们定义某类样本集到超平面的距离是这个样本集合内的样本到超平面的最短距离。咱们用 di 表明点 xi 到超平面 wxi+b=0 的欧氏距离。所以咱们要求 di 的最小值,用它来表明这个样本到超平面的最短距离。di 能够用公式计算得出:
其中||w||为超平面的范数,di 的公式能够用解析几何知识进行推导,这里不作解释。
 
最大间隔的优化模型
咱们的目标就是找出全部分类间隔中最大的那个值对应的超平面。在数学上,这是一个凸优化问题(凸优化就是关于求凸集中的凸函数最小化的问题,这里不具体展开)。经过凸优化问题,最后能够求出最优的 w 和 b,也就是咱们想要找的最优超平面。中间求解的过程会用到拉格朗日乘子,和 KKT(Karush-Kuhn-Tucker)条件。数学公式比较多,这里不进行展开。
 
硬间隔、软间隔和非线性 SVM
假如数据是彻底的线性可分的,那么学习到的模型能够称为硬间隔支持向量机。 换个说法,硬间隔指的就是彻底分类准确,不能存在分类错误的状况。软间隔,就是容许必定量的样本分类错误。
咱们知道,实际工做中的数据没有那么“干净”,或多或少都会存在一些噪点。因此线性可分是个理想状况。这时,咱们须要使用到软间隔 SVM(近似线性可分),好比下面这种状况:
 
 
 
另外还存在一种状况,就是非线性支持向量机。
好比下面的样本集就是个非线性的数据。图中的两类数据,分别分布为两个圆圈的形状。那么这种状况下,不管是多高级的分类器,只要映射函数是线性的,就无法处理,SVM 也处理不了。这时,咱们须要引入一个新的概念: 核函数。它能够将样本从原始空间映射到一个更高维的特质空间中,使得样本在新的空间中线性可分 。这样咱们就可使用原来的推导来进行计算,只是全部的推导是在新的空间,而不是在原来的空间中进行。
 
因此在非线性 SVM 中,核函数的选择就是影响 SVM 最大的变量。最经常使用的核函数有线性核、多项式核、高斯核、拉普拉斯核、sigmoid 核,或者是这些核函数的组合。这些函数的区别在于映射方式的不一样。经过这些核函数,咱们就能够把样本空间投射到新的高维空间中。
固然软间隔和核函数的提出,都是为了方便咱们对上面超平面公式中的 w* 和 b* 进行求解,从而获得最大分类间隔的超平面。
 
2、 用 SVM 如何解决多分类问题
SVM 自己是一个二值分类器,最初是为二分类问题设计的,也就是回答 Yes 或者是 No。而实际上咱们要解决的问题,多是多分类的状况,好比对文本进行分类,或者对图像进行识别。
针对这种状况,咱们能够将多个二分类器组合起来造成一个多分类器,常见的方法有“一对多法”和“一对一法”两种。
 
1. 一对多法
假设咱们要把物体分红 A、B、C、D 四种分类,那么咱们能够先把其中的一类做为分类 1,其余类统一归为分类 2。这样咱们能够构造 4 种 SVM,分别为如下的状况:
(1)样本 A 做为正集,B,C,D 做为负集;
(2)样本 B 做为正集,A,C,D 做为负集;
(3)样本 C 做为正集,A,B,D 做为负集;
(4)样本 D 做为正集,A,B,C 做为负集。
这种方法,针对 K 个分类,须要训练 K 个分类器,分类速度较快,但训练速度较慢,由于每一个分类器都须要对所有样本进行训练,并且负样本数量远大于正样本数量,会形成样本不对称的状况,并且当增长新的分类,好比第 K+1 类时,须要从新对分类器进行构造。
 
2. 一对一法
一对一法的初衷是想在训练的时候更加灵活。咱们能够在任意两类样本之间构造一个 SVM,这样针对 K 类的样本,就会有 C(k,2) 类分类器。
好比咱们想要划分 A、B、C 三个类,能够构造 3 个分类器:
(1)分类器 1:A、B;
(2)分类器 2:A、C;
(3)分类器 3:B、C。    
当对一个未知样本进行分类时,每个分类器都会有一个分类结果,即为 1 票,最终得票最多的类别就是整个未知样本的类别。
这样作的好处是,若是新增一类,不须要从新训练全部的 SVM,只须要训练和新增这一类样本的分类器。并且这种方式在训练单个 SVM 模型的时候,训练速度快。
但这种方法的不足在于,分类器的个数与 K 的平方成正比,因此当 K 较大时,训练和测试的时间会比较慢。
 
3、 如何在 sklearn 中使用 SVM
在 Python 的 sklearn 工具包中有 SVM 算法,首先须要引用工具包:
1 from sklearn import svm
SVM 既能够作回归,也能够作分类器。
当用 SVM 作回归的时候,咱们可使用 SVR 或 LinearSVR。SVR 的英文是 Support Vector Regression。这篇文章只讲分类,这里只是简单地提一下。
当作分类器的时候,咱们使用的是 SVC 或者 LinearSVC。SVC 的英文是 Support Vector Classification。
我简单说一下这二者以前的差异。
从名字上你能看出 LinearSVC 是个线性分类器,用于处理线性可分的数据,只能使用线性核函数。上一节,我讲到 SVM 是经过核函数将样本从原始空间映射到一个更高维的特质空间中,这样就使得样本在新的空间中线性可分。
若是是针对非线性的数据,须要用到 SVC。在 SVC 中,咱们既可使用到线性核函数(进行线性划分),也能使用高维的核函数(进行非线性划分)。
 
如何建立一个 SVM 分类器呢?
咱们首先使用 SVC 的构造函数:model = svm.SVC(kernel=‘rbf’, C=1.0, gamma=‘auto’),这里有三个重要的参数 kernel、C 和 gamma。
kernel 表明核函数的选择,它有四种选择,只不过默认是 rbf,即高斯核函数。
linear:线性核函数
poly:多项式核函数
rbf:高斯核函数(默认)
sigmoid:sigmoid 核函数
这四种函数表明不一样的映射方式,你可能会问,在实际工做中,如何选择这 4 种核函数呢?我来给你解释一下:
线性核函数,是在数据线性可分的状况下使用的,运算速度快,效果好。不足在于它不能处理线性不可分的数据。
多项式核函数能够将数据从低维空间映射到高维空间,但参数比较多,计算量大。
高斯核函数一样能够将样本映射到高维空间,但相比于多项式核函数来讲所需的参数比较少,一般性能不错,因此是默认使用的核函数。
了解深度学习的同窗应该知道 sigmoid 常常用在神经网络的映射中。所以当选用 sigmoid 核函数时,SVM 实现的是多层神经网络。
 
上面介绍的 4 种核函数,除了第一种线性核函数外,其他 3 种均可以处理线性不可分的数据。
参数 C 表明目标函数的惩罚系数,惩罚系数指的是分错样本时的惩罚程度,默认状况下为 1.0。当 C 越大的时候,分类器的准确性越高,但一样容错率会越低,泛化能力会变差。相反,C 越小,泛化能力越强,可是准确性会下降。
参数 gamma 表明核函数的系数,默认为样本特征数的倒数,即 gamma = 1 / n_features。
在建立 SVM 分类器以后,就能够输入训练集对它进行训练。咱们使用 model.fit(train_X,train_y),传入训练集中的特征值矩阵 train_X 和分类标识 train_y。特征值矩阵就是咱们在特征选择后抽取的特征值矩阵(固然你也能够用所有数据做为特征值矩阵);分类标识就是人工事先针对每一个样本标识的分类结果。这样模型会自动进行分类器的训练。咱们可使用 prediction=model.predict(test_X) 来对结果进行预测,传入测试集中的样本特征矩阵 test_X,能够获得测试集的预测分类结果 prediction。
一样咱们也能够建立线性 SVM 分类器,使用 model=svm.LinearSVC()。在 LinearSVC 中没有 kernel 这个参数,限制咱们只能使用线性核函数。因为 LinearSVC 对线性分类作了优化,对于数据量大的线性可分问题,使用 LinearSVC 的效率要高于 SVC。
若是你不知道数据集是否为线性,能够直接使用 SVC 类建立 SVM 分类器。
在训练和预测中,LinearSVC 和 SVC 同样,都是使用 model.fit(train_X,train_y) 和 model.predict(test_X)。
 
4、 如何用 SVM 进行乳腺癌检测
在了解了如何建立和使用 SVM 分类器后,咱们来看一个实际的项目,数据集来自美国威斯康星州的乳腺癌诊断数据集, 点击这里进行下载。
医疗人员采集了患者乳腺肿块通过细针穿刺 (FNA) 后的数字化图像,而且对这些数字图像进行了特征提取,这些特征能够描述图像中的细胞核呈现。肿瘤能够分红良性和恶性。部分数据截屏以下所示:
数据表一共包括了 32 个字段,表明的含义以下:
上面的表格中,mean 表明平均值,se 表明标准差,worst 表明最大值(3 个最大值的平均值)。每张图像都计算了相应的特征,得出了这 30 个特征值(不包括 ID 字段和分类标识结果字段 diagnosis),其实是 10 个特征值(radius、texture、perimeter、area、smoothness、compactness、concavity、concave points、symmetry 和 fractal_dimension_mean)的 3 个维度,平均、标准差和最大值。这些特征值都保留了 4 位数字。字段中没有缺失的值。在 569 个患者中,一共有 357 个是良性,212 个是恶性。
好了,咱们的目标是生成一个乳腺癌诊断的 SVM 分类器,并计算这个分类器的准确率。 首先加载数据并对数据作部分的探索:
1 # 加载数据集,你须要把数据放到目录中
1 data = pd.read_csv("./data.csv")
1 # 数据探索
2 # 由于数据集中列比较多,咱们须要把 dataframe 中的列所有显示出来
3 pd.set_option('display.max_columns', None) 4 print(data.columns) 5 print(data.head(5)) 6 print(data.describe())
这是部分的运行结果,完整结果你能够本身跑一下。
 1 Index(['id', 'diagnosis', 'radius_mean', 'texture_mean', 'perimeter_mean',  2        'area_mean', 'smoothness_mean', 'compactness_mean', 'concavity_mean',  3        'concave points_mean', 'symmetry_mean', 'fractal_dimension_mean',  4        'radius_se', 'texture_se', 'perimeter_se', 'area_se', 'smoothness_se',  5        'compactness_se', 'concavity_se', 'concave points_se', 'symmetry_se',  6        'fractal_dimension_se', 'radius_worst', 'texture_worst',  7        'perimeter_worst', 'area_worst', 'smoothness_worst',  8        'compactness_worst', 'concavity_worst', 'concave points_worst',  9        'symmetry_worst', 'fractal_dimension_worst'], 10       dtype='object') 11  id diagnosis radius_mean texture_mean perimeter_mean area_mean \ 12 0    842302         M        17.99         10.38          122.80     1001.0   
13 1    842517         M        20.57         17.77          132.90     1326.0   
14 2  84300903         M        19.69         21.25          130.00     1203.0   
15 3  84348301         M        11.42         20.38           77.58      386.1   
16 4  84358402         M        20.29         14.34          135.10     1297.0
接下来,咱们就要对数据进行清洗了。
运行结果中,你能看到 32 个字段里,id 是没有实际含义的,能够去掉。diagnosis 字段的取值为 B 或者 M,咱们能够用 0 和 1 来替代。另外其他的 30 个字段,其实能够分红三组字段,下划线后面的 mean、se 和 worst 表明了每组字段不一样的度量方式,分别是平均值、标准差和最大值。
1 # 将特征字段分红 3 组
2 features_mean= list(data.columns[2:12]) 3 features_se= list(data.columns[12:22]) 4 features_worst=list(data.columns[22:32]) 5 # 数据清洗
6 # ID 列没有用,删除该列
7 data.drop("id",axis=1,inplace=True) 8 # 将 B 良性替换为 0,M 恶性替换为 1
9 data['diagnosis']=data['diagnosis'].map({'M':1,'B':0})
而后咱们要作特征字段的筛选,首先须要观察下 features_mean 各变量之间的关系,这里咱们能够用 DataFrame 的 corr() 函数,而后用热力图帮咱们可视化呈现。一样,咱们也会看总体良性、恶性肿瘤的诊断状况。
1 # 将肿瘤诊断结果可视化
2 sns.countplot(data['diagnosis'],label="Count") 3 plt.show() 4 # 用热力图呈现 features_mean 字段之间的相关性
5 corr = data[features_mean].corr() 6 plt.figure(figsize=(14,14)) 7 # annot=True 显示每一个方格的数据
8 sns.heatmap(corr, annot=True) 9 plt.show()
这是运行的结果:
热力图中对角线上的为单变量自身的相关系数是 1。颜色越浅表明相关性越大。因此你能看出来 radius_mean、perimeter_mean 和 area_mean 相关性很是大,compactness_mean、concavity_mean、concave_points_mean 这三个字段也是相关的,所以咱们能够取其中的一个做为表明。
 
那么如何进行特征选择呢?
特征选择的目的是降维,用少许的特征表明数据的特性,这样也能够加强分类器的泛化能力,避免数据过拟合。
咱们能看到 mean、se 和 worst 这三组特征是对同一组内容的不一样度量方式,咱们能够保留 mean 这组特征,在特征选择中忽略掉 se 和 worst。同时咱们能看到 mean 这组特征中,radius_mean、perimeter_mean、area_mean 这三个属性相关性大,compactness_mean、daconcavity_mean、concave points_mean 这三个属性相关性大。咱们分别从这 2 类中选择 1 个属性做为表明,好比 radius_mean 和 compactness_mean。
这样咱们就能够把原来的 10 个属性缩减为 6 个属性,代码以下:
 1 # 特征选择
 2 features_remain = ['radius_mean','texture_mean', 'smoothness_mean','compactness_mean','symmetry_mean', 'fractal_dimension_mean']  3 对特征进行选择以后,咱们就能够准备训练集和测试集:  4 # 抽取 30% 的数据做为测试集,其他做为训练集
 5 train, test = train_test_split(data, test_size = 0.3)# in this our main data is splitted into train and test
 6 # 抽取特征选择的数值做为训练和测试数据
 7 train_X = train[features_remain]  8 train_y=train['diagnosis']  9 test_X= test[features_remain] 10 test_y =test['diagnosis'] 11 在训练以前,咱们须要对数据进行规范化,这样让数据同在同一个量级上,避免由于维度问题形成数据偏差: 12 # 采用 Z-Score 规范化数据,保证每一个特征维度的数据均值为 0,方差为 1
13 ss = StandardScaler() 14 train_X = ss.fit_transform(train_X) 15 test_X = ss.transform(test_X) 16 最后咱们可让 SVM 作训练和预测了: 17 # 建立 SVM 分类器
18 model = svm.SVC() 19 # 用训练集作训练
20 model.fit(train_X,train_y) 21 # 用测试集作预测
22 prediction=model.predict(test_X) 23 print('准确率: ', metrics.accuracy_score(prediction,test_y))
运行结果:
1 准确率:  0.9181286549707602
准确率大于 90%,说明训练结果还不错。
 
搜索关注微信公众号“程序员姜小白”,获取更新精彩内容哦。
相关文章
相关标签/搜索