• 第六章 支持向量机

    • 6.1 什么是支持向量机

      • 6.1.1 线性SVM
      • 6.1.2 函数间隔和几何间隔
      • 6.1.3 最大间隔分离超平面
      • 6.1.4 支持向量和间隔边界
      • 6.1.4 学习的对偶算法
    • 6.2 线性支持向量机与软间隔最大化
      • 6.2.1 线性支持向量机
      • 6.2.2 学习的对偶算法
      • 6.2.3 支持向量
      • 6.2.4 合页损失函数
      • 6.2.5 编程求解线性SVM
      • 6.2.6 简化版SMO算法
    • 6.3 非线性支持向量机与核函数
      • 6.3.1 核技巧
      • 6.3.2 正定核
      • 6.3.3 常用核函数
      • 6.3.4 非线性支持向量分类机
      • 6.3.5 编程实现非线性SVM
    • 6.4 序列最小最优化算法(SMO算法)
      • 6.4.1 两个变量二次规划的求解方法
      • 6.4.2 变量的选择方法
      • 6.4.3 SMO算法步骤
      • 6.4.4 SMO算法优化
      • 6.4.5 完整版SMO算法
    • 6.5 使用sklearn构建SVM分类器
      • 6.5.1 Sklearn.svm.SVC
      • 6.5.2 编写代码
    • 6.6 SVM的优缺点

第六章 支持向量机

6.1 什么是支持向量机

支持向量机(Support Vector Machines)是目前被认为最好的现成的算法之一

在很久以前的情人节,大侠要去救他的爱人,但魔鬼和他玩了一个游戏。

魔鬼在桌子上似乎有规律放了两种颜色的球,说:“你用一根棍分开它们?要求:尽量在放更多球之后,仍然适用。”

于是大侠这样放,干的不错?


然后魔鬼,又在桌上放了更多的球,似乎有一个球站错了阵营。

SVM就是试图把棍放在最佳位置,好让在棍的两边有尽可能大的间隙。

现在即使魔鬼放了更多的球,棍仍然是一个好的分界线。

然后,在SVM 工具箱中有另一个更加重要的 trick。 魔鬼看到大侠已经学会了一个trick,于是魔鬼给了大侠一个新的挑战。

现在,大侠没有棍可以很好帮他分开两种球了,现在怎么办呢?当然像所有武侠片中一样大侠桌子一拍,球飞到空中。然后,凭借大侠的轻功,大侠抓起一张纸,插到了两种球的中间。

现在,从魔鬼的角度看这些球,这些球看起来像是被一条曲线分开了。

再之后,无聊的大人们,把这些球叫做 「data」,把棍子 叫做 「classifier」, 最大间隙trick 叫做「optimization」, 拍桌子叫做「kernelling」, 那张纸叫做「hyperplane」。

图片来源:Support Vector Machines explained well

当数据为线性可分的时候,也就是可以用一根棍子将两种小球分开的时候,只要将棍子放在让小球距离棍子的距离最大化的位置即可,寻找该最大间隔的过程就叫做最优化。

但是一般的数据是线性不可分的,所以要将其转化到高维空间去,用一张纸将其进行分类,空间转化就是需要核函数,用于切分小球的纸就是超平面。

什么是SVM:

分类作为数据挖掘领域中一项非常重要的任务,它的目的是学会一个分类函数或分类模型(或者叫做分类器),而支持向量机本身便是一种监督式学习的方法(至于具体什么是监督学习与非监督学习,请参见此系列Machine L&Data Mining第一篇),它广泛的应用于统计分类以及回归分析中。

支持向量机(SVM)是90年代中期发展起来的基于统计学习理论的一种机器学习方法,通过寻求结构化风险最小来提高学习机泛化能力,实现经验风险和置信范围的最小化,从而达到在统计样本量较少的情况下,亦能获得良好统计规律的目的。

通俗来讲,它是一种二类分类模型,其基本模型定义为特征空间上的间隔最大的线性分类器,即支持向量机的学习策略便是间隔最大化,最终可转化为一个凸二次规划问题的求解。

6.1.1 线性SVM

线性可分的二分类问题:

上图中红色和蓝色分别表示不同的两个类别,数据为线性可分,但是将其分开的直线不止一条,(b)(c)分别给出了不同的方法。黑色的实现为“决策面”,每个决策面对应一个线性分类器,两者的性能是有差距的。

决策面不同的情况下,添加一个红色的点,显然(b)仍然能够很好的分类,但是(c)已经分类错误了,所以决策面(b)优于(c)。

如何选择较好的决策面:

在保证决策面方向不变且不会出现错分样本的情况下移动决策面,会在原来的决策面两侧找到两个极限位置(越过该位置就会产生错分现象),如虚线所示。

虚线的位置由决策面的方向和距离原决策面最近的几个样本的位置决定。而这两条平行虚线正中间的分界线就是在保持当前决策面方向不变的前提下的最优决策面。

两条虚线之间的垂直距离就是这个最优决策面对应的分类间隔。显然每一个可能把数据集正确分开的方向都有一个最优决策面(有些方向无论如何移动决策面的位置也不可能将两类样本完全分开),而不同方向的最优决策面的分类间隔通常是不同的,那个具有“最大间隔”的决策面就是SVM要寻找的最优解。

而这个真正的最优解对应的两侧虚线所穿过的样本点,就是SVM中的支持样本点,称为”支持向量”。

学习的目标是在特征空间中找到一个分离超平面,能将实例分到不同的类中。

分离超平面的方程:w⋅x+b=0 w⋅x+b=0w\cdot x+b=0

方程由法向量w ww和截距b'>b bb决定,可以用(w,b) (w,b)(w,b)来表示。

分离超平面将特征空间划分为两部分,一部分为正类,一部分为负类,法向量指向的一侧为正类,另一侧为负类。

6.1.2 函数间隔和几何间隔

备注:

上图7.1中有A,B,C三个点,表示3个实例,均在分离超平面的正类一侧,预测他们的类,点A距离超平面较远,若预测该类为正类,就比较确信为正确的,点C距离分离超平面较近,不是很确信。

函数间隔(function margin):

一般来说,一个点距离分离超平面的远近可以表示分类预测的确信程度,在超平面w⋅x+b=0 w⋅x+b=0w\cdot x+b=0确定的情况下,|w⋅x+b| |w⋅x+b||w\cdot x+b|能够相对的表示点x距离超平面的远近,而w⋅x+b w⋅x+bw\cdot x+b的符号与类标记y的符号是否一致能够表示分类是否正确,所以可以量y(w⋅x+b) y(w⋅x+b)y(w\cdot x+b)来表示分离的正确性和确信度。

从函数间隔变为几何间隔:

虽然函数间隔可以表示分类预测的正确性即确信度,但是选择分离超平面时,只有函数间隔远远不够,因为只要成比例的改变w ww和b'>b bb,例如将它们改变为2w 2w2w和2b 2b2b,超平面并没有改变(w∗x+b=0 w∗x+b=0w*x+b=0,右边为0,不会因为系数而改变),但是函数间隔(y(w∗x+b) y(w∗x+b)y(w*x+b))却变为原来的2倍。

对分离超平面的法向量w ww加某些约束,如规范化,||w||=1'>||w||=1 ||w||=1||w||=1,使得间隔是确定的,此时函数间隔变为几何间隔。

上图给出了超平面(w,b) (w,b)(w,b)和其法向量w ww,点A'>A AA表示某一实例x i  xix_i,其类标记为y i =+1 yi=+1y_i=+1,点A AA与超平面的距离由线段AB'>AB ABAB给出,记作y i  yiy_i:

y i =w||w|| ⋅x i +b||w||  yi=w||w||⋅xi+b||w||y_i=\frac {w}{||w||}\cdot x_i+\frac {b}{||w||}

其中,||w|| ||w||||w||为w ww的L2'>L 2  L2L_2范数,如果点A AA在超平面的负一侧,即yi=−1'>y i =−1 yi=−1y_i=-1,则:

y i =−(w||w|| ⋅b||w|| ) yi=−(w||w||⋅b||w||)y_i=- \left ( \frac {w}{||w||}\cdot \frac {b}{||w||}\right)

一般情况,当样本点(x i ,y i ) (xi,yi)(x_i,y_i)被超平面(w,b) (w,b)(w,b)正确分类时,点x i  xix_i与超平面(w,b) (w,b)(w,b)的距离是:

γ i =y i (w||w|| ⋅x i +b||w|| ) γi=yi(w||w||⋅xi+b||w||)\gamma _i=y_i \left ( \frac {w}{||w||}\cdot x_i+\frac {b}{||w||}\right)

几何间隔的概念:

6.1.3 最大间隔分离超平面

最大间隔分离超平面,即为下面的约束最优化问题:

取γ=1 γ=1\gamma =1,最大化1||w||  1||w||\frac {1}{||w||},和最小化12 ||w|| 2  12||w||2\frac{1}{2}||w||^2是等价的,所以变为:

这就变为一个凸二次规划问题,求出上式的解w ∗  w∗w^*和b ∗  b∗b^*,就可以得到最大间隔分离超平面w ∗ ⋅x+b ∗  w∗⋅x+b∗w^*\cdot x+b^*及分类决策函数f(x)=sign(w ∗ ⋅x+b ∗ ) f(x)=sign(w∗⋅x+b∗)f(x)=sign(w^*\cdot x+b^*),即线性可分支持向量机模型。

总结:

线性可分训练数据集的最大间隔分离超平面是存在且唯一的

6.1.4 支持向量和间隔边界

支持向量:

在线性可分情况下,训练数据集的样本点中与分离超平面距离最近的样本点的实例称为支持向量,支持向量是使约束条件等号成立的点,即:

y i (w×x i +b)−1=0 yi(w×xi+b)−1=0y_i(w\times x_i+b)-1=0

对y i =+1 yi=+1y_i=+1的正例点,支持向量在超平面:

H 1 :w×x i +b=1 H1:w×xi+b=1H_1:w\times x_i+b=1

对y i =+1 yi=+1y_i=+1的正例点,支持向量在超平面:

H 2 :w×x i +b=1 H2:w×xi+b=1H_2:w\times x_i+b=1

如下图所示,在H 1  H1H_1和H 2  H2H_2上的点就是支持向量。

间隔边界:

H 1  H1H_1和H 2  H2H_2平行,且没有实例点落在它们中间,分离超平面与它们平行且位于中央,间隔即为H 1  H1H_1和H 2  H2H_2直接的距离,依赖于分离超平面的法向量w ww,等于2||w||'>2||w||  2||w||\frac {2}{||w||},H 1  H1H_1和H 2  H2H_2称为间隔边界。

分离超平面是由支持向量决定的,其他实例点并不起作用,移动支持向量将会改变所求平面,移动其他实例点所求平面不会改变。

由于支持向量在确定分离超平面中起着决定性作用,所以将这种分离模型称为支持向量机。

支持向量的个数一般很少,所以支持向量机由很少的“重要的”训练样本确定。

6.1.4 学习的对偶算法

为了求解线性可分支持向量机的最优化问题,将它作为原始最优化问题,应用拉格朗日对偶性,通过求解对偶问题得到原始问题的最优解,这就是线性可分支持向量机的对偶算法。

对偶算法的优点:

1)对偶问题往往更容易求解
2)自然引入核函数,进而推广到非线性分类问题

首先,我们先要从宏观的视野上了解一下拉格朗日对偶问题出现的原因和背景。

我们知道我们要求解的是最小化问题,所以一个直观的想法是如果我能够构造一个函数,使得该函数在可行解区域内与原目标函数完全一致,而在可行解区域外的数值非常大,甚至是无穷大,那么这个没有约束条件的新目标函数的优化问题就与原来有约束条件的原始目标函数的优化问题是等价的问题。这就是使用拉格朗日方程的目的,它将约束条件放到目标函数中,从而将有约束优化问题转换为无约束优化问题。

随后,人们又发现,使用拉格朗日获得的函数,使用求导的方法求解依然困难。进而,需要对问题再进行一次转换,即使用一个数学技巧:拉格朗日对偶。

所以,显而易见的是,我们在拉格朗日优化我们的问题这个道路上,需要进行下面二个步骤:

  • 将有约束的原始目标函数转换为无约束的新构造的拉格朗日目标函数
  • 使用拉格朗日对偶性,将不易求解的优化问题转化为易求解的优化

而求解这个对偶学习问题,可以分为三个步骤:首先要让L(w,b,α) L(w,b,α)L(w,b,α)关于w ww和b'>b bb最小化,然后求对α的极大,最后利用SMO算法求解对偶问题中的拉格朗日乘子。

  • KKT条件

假设一个最优化模型能够表示成下列标准形式:


KKT条件的全称是Karush-Kuhn-Tucker条件,KKT条件是说最优值条件必须满足以下条件:
1)条件一:经过拉格朗日函数处理之后的新目标函数L(w,b,α)对α求导为零:
2)条件二:h(x)=0 h(x)=0h(x) = 0;
3)条件三:α∗g(x)=0 α∗g(x)=0α*g(x) = 0;

对于我们的优化问题:

上述优化问题满足三个条件,故满足了凸优化问题和KKT条件。

示例:

6.2 线性支持向量机与软间隔最大化

6.2.1 线性支持向量机

线性可分问题的支持向量机学习方法对线性不可分训练数据是不适用的,因为上述方法在那个的不等式约束并不成立。

如何将其扩展到线性不可分问题:

修改硬间隔最大化,使其成为软间隔最大化。

假设给定一个特征空间上的训练数据集:

T={(X 1 ,Y 1 ),(X 2 ,Y 2 ),...,(x N ,y N )} T={(X1,Y1),(X2,Y2),...,(xN,yN)}T=\{ (X_1,Y_1),(X_2,Y_2),...,(x_N,y_N) \}
其中,x−i∈χ=R n ,y i ∈Y={+1,−1},i=1,2,...,N x−i∈χ=Rn,yi∈Y={+1,−1},i=1,2,...,Nx-i \in \chi=R^n,y_i \in Y=\{+1,-1\},i=1,2,...,N ,x i  xix_i为第i个特征向量,y i  yiy_i为x i  xix_i的类标记,假设训练数据集是线性不可分的,通常情况是,训练数据中有一些特异点(outlier),将这些特异点去除后,剩下的大部分样本点组成的几何是线性可分的。

线性支持向量机定义:

给定的线性不可分的训练数据集,通过求解凸二次规划问题,即软间隔最大化问题(7.32)~(7.34),得到的分离超平面为:

w ∗ ⋅x+b ∗ =0 w∗⋅x+b∗=0w^*\cdot x+b^*=0

以及相应的分类决策函数:

f(x)=sign(w ∗ ⋅x+b ∗ ) f(x)=sign(w∗⋅x+b∗)f(x)=sign(w^*\cdot x+b^*)

称为线性支持向量机

6.2.2 学习的对偶算法

步骤(2)中,对任一适合条件0<α ∗ j <C 0<αj∗<C0的α ∗ j  αj∗\alpha_j^*都可以求出b ∗  b∗b^*,但是由于问题(7.32)~(7.34)对b bb的解不唯一,所以实际计算时可以取在所有符合条件的样本点上的平均值。

6.2.3 支持向量

6.2.4 合页损失函数



6.2.5 编程求解线性SVM

可视化数据集

import matplotlib.pylab as plt
import numpy as np"""
函数说明:读取数据
"""# readlines() 自动将文件内容分析成一个行的列表,该列表可以由 Python 的 for... in...结构进行处理
# readlines()一次读取整个文件,readline() 每次只读取一行,通常比 .readlines() 慢得多def loadDataSet(fileName):dataMat = []; labelMat = []fr = open(fileName)for line in fr.readlines():                                     #逐行读取,滤除空格等lineArr = line.strip().split('\t')dataMat.append([float(lineArr[0]), float(lineArr[1])])      #添加数据labelMat.append(float(lineArr[2]))                          #添加标签return dataMat,labelMat"""
函数说明:数据可视化"""def showDataSet(dataMat, labelMat):data_plus = []  # 正样本data_minus = []  # 负样本for i in range(len(dataMat)):if labelMat[i] > 0:data_plus.append(dataMat[i])else:data_minus.append(dataMat[i])data_plus_np = np.array(data_plus)  # 转换为numpy矩阵data_minus_np = np.array(data_minus)  # 转换为numpy矩阵plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1])  # 正样本散点图plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1])  # 负样本散点图plt.show()if __name__ == '__main__':dataMat, labelMat = loadDataSet('testSet.txt')showDataSet(dataMat, labelMat)

结果:

6.2.6 简化版SMO算法

from time import sleep
import matplotlib.pylab as plt
import numpy as np
import random
import types"""
函数说明:读取数据"""def loadDataSet(fileName):dataMat = []labelMat = []fr = open(fileName)for line in fr.readlines():  # 逐行读取,滤除空格等lineArr = line.strip().split('\t')dataMat.append([float(lineArr[0]), float(lineArr[1])])  # 添加数据labelMat.append(float(lineArr[2]))  # 添加标签return dataMat, labelMat"""
函数说明:随机选择alphaParameters:i:alpham:alpha参数个数
"""def selectJrand(i, m):j = i# 选择一个不等于i的jwhile (j == i):j = int(random.uniform(0, m))return j"""
函数说明:修剪alpha
Parameters:aj:alpha的值H:alpha上限L:alpha下限Returns:aj:alpha的值
"""def clipAlpha(aj, H, L):if aj > H:aj = Hif L > aj:aj = Lreturn aj"""
函数说明:简化版SMO算法Parameters:dataMatIn:数据矩阵classLabels:数据标签C:松弛变量toler:容错率maxIter:最大迭代次数
"""def smoSimple(dataMatIn, classLabels, C, toler, maxIter):# 转换为numpy的mat存储dataMatrix = np.mat(dataMatIn)labelMat = np.mat(classLabels).transpose()# 初始化b参数,统计dataMatrix的维度b = 0;m, n = np.shape(dataMatrix)# 初始化alpha参数,设为0alphas = np.mat(np.zeros((m, 1)))# 初始化迭代次数iter_num = 0# 最多迭代matIter次while (iter_num < maxIter):alphaPairsChanged = 0for i in range(m):# 步骤1:计算误差EifXi = float(np.multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[i, :].T)) + bEi = fXi - float(labelMat[i])# 优化alpha,设定一定的容错率。if ((labelMat[i] * Ei < -toler) and (alphas[i] < C)) or ((labelMat[i] * Ei > toler) and (alphas[i] > 0)):# 随机选择另一个与alpha_i成对优化的alpha_jj = selectJrand(i, m)# 步骤1:计算误差EjfXj = float(np.multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[j, :].T)) + bEj = fXj - float(labelMat[j])# 保存更新前的aplpha值,使用深拷贝alphaIold = alphas[i].copy()alphaJold = alphas[j].copy()# 步骤2:计算上下界L和Hif (labelMat[i] != labelMat[j]):L = max(0, alphas[j] - alphas[i])H = min(C, C + alphas[j] - alphas[i])else:L = max(0, alphas[j] + alphas[i] - C)H = min(C, alphas[j] + alphas[i])if L == H: print("L==H"); continue# 步骤3:计算etaeta = 2.0 * dataMatrix[i, :] * dataMatrix[j, :].T - dataMatrix[i, :] * dataMatrix[i, :].T -\dataMatrix[j,:] * dataMatrix[j, :].Tif eta >= 0: print("eta>=0"); continue# 步骤4:更新alpha_jalphas[j] -= labelMat[j] * (Ei - Ej) / eta# 步骤5:修剪alpha_jalphas[j] = clipAlpha(alphas[j], H, L)if (abs(alphas[j] - alphaJold) < 0.00001): print("alpha_j变化太小"); continue# 步骤6:更新alpha_ialphas[i] += labelMat[j] * labelMat[i] * (alphaJold - alphas[j])# 步骤7:更新b_1和b_2b1 = b - Ei - labelMat[i] * (alphas[i] - alphaIold) * dataMatrix[i, :] * dataMatrix[i, :].T - labelMat[j] * (alphas[j] - alphaJold) * dataMatrix[i, :] * dataMatrix[j, :].Tb2 = b - Ej - labelMat[i] * (alphas[i] - alphaIold) * dataMatrix[i, :] * dataMatrix[j, :].T - labelMat[j] * (alphas[j] - alphaJold) * dataMatrix[j, :] * dataMatrix[j, :].T# 步骤8:根据b_1和b_2更新bif (0 < alphas[i]) and (C > alphas[i]):b = b1elif (0 < alphas[j]) and (C > alphas[j]):b = b2else:b = (b1 + b2) / 2.0# 统计优化次数alphaPairsChanged += 1# 打印统计信息print("第%d次迭代 样本:%d, alpha优化次数:%d" % (iter_num, i, alphaPairsChanged))# 更新迭代次数if (alphaPairsChanged == 0):iter_num += 1else:iter_num = 0print("迭代次数: %d" % iter_num)return b, alphas"""
函数说明:分类结果可视化
"""def showClassifer(dataMat, w, b):# 绘制样本点data_plus = []  # 正样本data_minus = []  # 负样本for i in range(len(dataMat)):if labelMat[i] > 0:data_plus.append(dataMat[i])else:data_minus.append(dataMat[i])data_plus_np = np.array(data_plus)  # 转换为numpy矩阵data_minus_np = np.array(data_minus)  # 转换为numpy矩阵plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1], s=30,alpha=0.7)  # 正样本散点图plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1], s=30,alpha=0.7)  # 负样本散点图# 绘制直线x1 = max(dataMat)[0]x2 = min(dataMat)[0]a1, a2 = wb = float(b)a1 = float(a1[0])a2 = float(a2[0])y1, y2 = (-b - a1 * x1) / a2, (-b - a1 * x2) / a2plt.plot([x1, x2], [y1, y2])# 找出支持向量点for i, alpha in enumerate(alphas):if abs(alpha) > 0:x, y = dataMat[i]plt.scatter([x], [y], s=150, c='none', alpha=0.7, linewidth=1.5, edgecolor='red')plt.show()"""
函数说明:计算w
"""def get_w(dataMat, labelMat, alphas):alphas, dataMat, labelMat = np.array(alphas), np.array(dataMat), np.array(labelMat)w = np.dot((np.tile(labelMat.reshape(1, -1).T, (1, 2)) * dataMat).T, alphas)return w.tolist()if __name__ == '__main__':dataMat, labelMat = loadDataSet('testSet.txt')b, alphas = smoSimple(dataMat, labelMat, 0.6, 0.001, 40)w = get_w(dataMat, labelMat, alphas)showClassifer(dataMat, w, b)

结果:

蓝色实线是所求分类器,红色圈表示支持向量机

6.3 非线性支持向量机与核函数

当分类问题是非线性的时候,可以使用非线性支持向量机,其主要特点是利用核技巧。

6.3.1 核技巧

一、非线性分类问题

非线性问题的解决方法:通过非线性变换,将非线性问题变为线性问题

举例说明:
假设二维平面x-y上存在若干点,其中点集A服从{x,y|x^2+y^2=1},点集B服从{x,y|x^2+y^2=9},那么这些点在二维平面上的分布是这样的:

蓝色的是点集A,红色的是点集B,他们在xy平面上并不能线性可分,即用一条直线分割( 虽然肉眼是可以识别的) 。采用映射(x,y)->(x,y,x^2+y^2)后,在三维空间的点的分布为:

用线性分类方法求解非线性问题分为两步:

1)使用一个变换将原空间的数据映射到新空间
2)在新空间用线性分类学习方法从训练数据中学习分类模型

二、核函数的定义

设χ'>χ χ\chi为输入空间,又设H HH为特征空间(希尔伯特空间),如果存在一个从χ'>χ χ\chi到H HH的映射:ϕ(x)=χ→H'>ϕ(x)=χ→H ϕ(x)=χ→H\phi(x)=\chi \to H

使得所有的x,z∈χ x,z∈χx,z\in \chi,函数K(x,z) K(x,z)K(x,z)满足条件:K(x,z)=ϕ(x)⋅ϕ(z) K(x,z)=ϕ(x)⋅ϕ(z)K(x,z)=\phi(x) \cdot \phi(z)

则K(x,z) K(x,z)K(x,z)称为核函数,ϕ(x) ϕ(x)\phi(x)称为映射函数。

三、核技巧在支持向量机中的应用

6.3.2 正定核

通常所说的核函数就是正定核函数

6.3.3 常用核函数

6.3.4 非线性支持向量分类机

利用核技巧可以将线性分类的学习方法应用到非线性分类问题中去,将线性支持向量机扩展到非线性支持向量机,只需将线性支持向量机对偶形式的内积换成核函数即可

非线性支持向量机:
从非线性分类训练集,通过核函数与软间隔最大化,或凸二次规划(7.95)~(7.97)学习得到的分类决策函数:

称为非线性支持向量,K(x,z) K(x,z)K(x,z)是正定核函数。

非线性支持向量机学习算法:

6.3.5 编程实现非线性SVM

接下来,我们将使用testSetRBF.txttestSetRBF2.txt进行实验,前者作为训练集,后者作为测试集。

import matplotlib.pylab as plt
import numpy as npdef loadDataSet(fileName):""":param fileName::return: dataMat, labelMat"""dataMat = []labelMat = []fr = open(fileName)for line in fr.readlines():  # 逐行读取,滤除空格等lineArr = line.strip().split('\t')dataMat.append([float(lineArr[0]), float(lineArr[1])])  # 添加数据labelMat.append(float(lineArr[2]))  # 添加标签return dataMat, labelMatdef showDataSet(dataMat,labelMat):"""数据可视化:param dataMat: 数据矩阵:param labelMat: 数据标签:return: 无"""#正样本data_plus=[]#负样本data_minus=[]for i in range(len(dataMat)):if labelMat[i]>0:data_plus.append(dataMat[i])else:data_minus.append(dataMat[i])data_plus_np=np.array(data_plus)data_minus_np=np.array(data_minus)#正样本散点图plt.scatter(np.transpose(data_plus_np)[0],np.transpose(data_plus_np)[1])#负样本散点图plt.scatter(np.transpose(data_minus_np)[0],np.transpose(data_minus_np)[1])plt.show()if __name__=='__main__':dataArr,labelArr=loadDataSet('testSetRBF.txt')showDataSet(dataArr,labelArr)

结果:

6.4 序列最小最优化算法(SMO算法)

支持向量机问题可以转化为求解凸二次规划问题,这样的问题具有全局最优解,并且有许多算法可以用于这个问题的求解,但是当训练样本容量很大时,这些算法往往变得非常低效,以至于无法使用。

1996年,John Platt发布了一个称为SMO的强大算法,用于训练SVM。SM表示序列最小化(Sequential Minimal Optimizaion)。Platt的SMO算法是将大优化问题分解为多个小优化问题来求解的。这些小优化问题往往很容易求解,并且对它们进行顺序求解的结果与将它们作为整体来求解的结果完全一致的。在结果完全相同的同时,SMO算法的求解时间短很多。

SMO算法的目标是求出一系列alpha和b,一旦求出了这些alpha,就很容易计算出权重向量w并得到分隔超平面。

SMO算法的工作原理是:每次循环中选择两个alpha进行优化处理。一旦找到了一对合适的alpha,那么就增大其中一个同时减小另一个。这里所谓的”合适”就是指两个alpha必须符合以下两个条件,条件之一就是两个alpha必须要在间隔边界之外,而且第二个条件则是这两个alpha还没有进进行过区间化处理或者不在边界上。

6.4.1 两个变量二次规划的求解方法

6.4.2 变量的选择方法

6.4.3 SMO算法步骤

1)步骤一:计算误差:

2)步骤二:计算上下界L和H:

3)步骤三:计算η η\eta

4)步骤四:更新α j  αj\alpha _j

5)步骤五:根据取值范围修剪α j  αj\alpha _j

6)步骤六:更新α i  αi\alpha _i

7)步骤七:更新b 1  b1b_1和b 2  b2b_2

8)步骤八:根据b 1  b1b_1和b 2  b2b_2更新b b<script id="MathJax-Element-97" type="math/tex">b</script>

6.4.4 SMO算法优化

启发选择方式:

在几百个点组成的小规模数据集上,简化版SMO算法的运行是没有什么问题的,但是在更大的数据集上的运行速度就会变慢。简化版SMO算法的第二个α的选择是随机的,针对这一问题,我们可以使用启发式选择第二个α值,来达到优化效果。

下面这两个公式想必已经不再陌生:

在实现SMO算法的时候,先计算η,再更新a_j。为了加快第二个α_j乘子的迭代速度,需要让直线的斜率增大,对于α_j的更新公式,其中η值没有什么文章可做,于是只能令:

因此,我们可以明确自己的优化方法了:

  • 最外层循环,首先在样本中选择违反KKT条件的一个乘子作为最外层循环,然后用”启发式选择”选择另外一个乘子并进行这两个乘子的优化
  • 在非边界乘子中寻找使得|E_i - E_j|最大的样本
  • 如果没有找到,则从整个样本中随机选择一个样本

6.4.5 完整版SMO算法

完整版Platt SMO算法是通过一个外循环来选择违反KKT条件的一个乘子,并且其选择过程会在这两种方式之间进行交替:

  • 在所有数据集上进行单遍扫描
  • 在非边界α中实现单遍扫描

非边界α指的就是那些不等于边界0或C的α值,并且跳过那些已知的不会改变的α值。所以我们要先建立这些α的列表,用于才能出α的更新状态。

在选择第一个α值后,算法会通过”启发选择方式”选择第二个α值。

6.5 使用sklearn构建SVM分类器

在第一篇文章中,我们使用了kNN进行手写数字识别。它的缺点是存储空间大,因为要保留所有的训练样本,如果你的老板让你节约这个内存空间,并达到相同的识别效果,甚至更好。那这个时候,我们就要可以使用SVM了,因为它只需要保留支持向量即可,而且能获得可比的效果。

6.5.1 Sklearn.svm.SVC

官方手册

sklearn.svm模块提供了很多模型,其中svm.SVC是基于libsvm实现的。

class sklearn.svm.SVC(C=1.0, kernel=’rbf’, degree=3, gamma=’auto’, coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape=’ovr’, random_state=None)

参数说明如下:

  • C:惩罚项,float类型,可选参数,默认为1.0,C越大,即对分错样本的惩罚程度越大,因此在训练样本中准确率越高,但是泛化能力降低,也就是对测试数据的分类准确率降低。相反,减小C的话,容许训练样本中有一些误分类错误样本,泛化能力强。对于训练样本带有噪声的情况,一般采用后者,把训练样本集中错误分类的样本作为噪声。
  • kernel:核函数类型,str类型,默认为’rbf’。可选参数为:

    • ’linear’:线性核函数
    • ‘poly’:多项式核函数
    • ‘rbf’:径像核函数/高斯核
    • ‘sigmod’:sigmod核函数
    • ‘precomputed’:核矩阵
    • precomputed表示自己提前计算好核函数矩阵,这时候算法内部就不再用核函数去计算核矩阵,而是直接用你给的核矩阵,核矩阵需要为n*n的。
  • degree:多项式核函数的阶数,int类型,可选参数,默认为3。这个参数只对多项式核函数有用,是指多项式核函数的阶数n,如果给的核函数参数是其他核函数,则会自动忽略该参数。

  • gamma:核函数系数,float类型,可选参数,默认为auto。只对’rbf’ ,’poly’ ,’sigmod’有效。如果gamma为auto,代表其值为样本特征数的倒数,即1/n_features。
  • coef0:核函数中的独立项,float类型,可选参数,默认为0.0。只有对’poly’ 和,’sigmod’核函数有用,是指其中的参数c。
  • probability:是否启用概率估计,bool类型,可选参数,默认为False,这必须在调用fit()之前启用,并且会fit()方法速度变慢。
  • shrinking:是否采用启发式收缩方式,bool类型,可选参数,默认为True。
  • tol:svm停止训练的误差精度,float类型,可选参数,默认为1e^-3。
  • cache_size:内存大小,float类型,可选参数,默认为200。指定训练所需要的内存,以MB为单位,默认为200MB。
  • class_weight:类别权重,dict类型或str类型,可选参数,默认为None。给每个类别分别设置不同的惩罚参数C,如果没有给,则会给所有类别都给C=1,即前面参数指出的参数C。如果给定参数’balance’,则使用y的值自动调整与输入数据中的类频率成反比的权重。
  • verbose:是否启用详细输出,bool类型,默认为False,此设置利用libsvm中的每个进程运行时设置,如果启用,可能无法在多线程上下文中正常工作。一般情况都设为False,不用管它。
  • max_iter:最大迭代次数,int类型,默认为-1,表示不限制。
  • decision_function_shape:决策函数类型,可选参数’ovo’和’ovr’,默认为’ovr’。’ovo’表示one vs one,’ovr’表示one vs rest。
  • random_state:数据洗牌时的种子值,int类型,可选参数,默认为None。伪随机数发生器的种子,在混洗数据时用于概率估计。

6.5.2 编写代码

import numpy as np
import operator
from os import listdir
from sklearn.svm import SVCdef img2Vector(filename):"""将32*32的二进制图像转换为1*1024的向量:param filename: 文件名:return: 返回的二进制图像的1*1024向量"""# 创建1*1024零向量returnVect = np.zeros((1, 1024))# 打开文件fr = open(filename)# 按行读取for i in range(32):# 读取一行数据lineStr = fr.readline()# 每一行的前32个元素依次添加到returnVect中for j in range(32):returnVect[0, 32 * i + j] = int(lineStr[j])# 返回转换后的1*1024向量return returnVectdef handwritingClassTest():"""手写数字分类测试:return: 无"""# 测试集的LabelshwLabels = []# 返回trainingDigits目录下的文件名trainingFileList = listdir('E:/python/machine learning in action/My Code/chap 06/trainingDigits')# 返回文件夹下文件的个数m = len(trainingFileList)# 初始化训练的Mat矩阵,测试集trainingMat = np.zeros((m, 1024))# 从文件名中解析出训练的类别for i in range(m):# 获得文件的名字fileNameStr = trainingFileList[i]# 获得分类的数字classNumber = int(fileNameStr.split('_')[0])# 将获得的类别添加到hwlabels中hwLabels.append(classNumber)# 将每个文件的1*1024数据存储到trainingMat矩阵中trainingMat[i, :] = img2Vector('E:/python/machine learning in action/My Code/chap 06/trainingDigits/%s' % (fileNameStr))clf = SVC(C=200, kernel='rbf')clf.fit(trainingMat, hwLabels)# 返回testDigits目录下的文件列表testFileList = listdir('testDigits')# 错误检测技术errorCount = 0.0# 测试数据的数量mTest = len(testFileList)# 从文件中解析出测试集的类别并进行分类测试for i in range(mTest):fileNameStr = testFileList[i]classNumber = int(fileNameStr.split('_')[0])# 获得测试集的1*1024向量,用于训练vectorUnderTest = img2Vector('E:/python/machine learning in action/My Code/chap 06/testDigits/%s' % (fileNameStr))# 获得预测结果classfierResult = clf.predict(vectorUnderTest)print("分类返回结果为 %d \t 真实结果为%d " % (classfierResult, classNumber))if (classfierResult != classNumber):errorCount += 1.0print("总共错了%d个数据 \n 错误率为%f%%" % (errorCount, errorCount / mTest * 100))if __name__ == '__main__':handwritingClassTest()

结果:

6.6 SVM的优缺点

优点:

  • 可用于线性/非线性分类,也可以用于回归,泛化错误率低,也就是说具有良好的学习能力,且学到的结果具有很好的推广性。
  • 可以解决小样本情况下的机器学习问题,可以解决高维问题,可以避免神经网络结构选择和局部极小点问题。
  • SVM是最好的现成的分类器,现成是指不加修改可直接使用。并且能够得到较低的错误率,SVM可以对训练集之外的数据点做很好的分类决策。

缺点:

  • 对参数调节和和函数的选择敏感。

机器学习实战(六)——支持向量机相关推荐

  1. 机器学习实战:支持向量机

    支持向量机(Support Vector Machine, SVM)是一类按监督学习(supervised learning)方式对数据进行二元分类(binary classification)的广义 ...

  2. 机器学习实战之支持向量机

    说实话,这一章算是这本书最难理解的部分了,所以挣扎了好几天这才决定想起来写写(其实这几天都看小说去了,哈哈哈哈) 书上给的案例很详细了,所以这篇文章我打算讲讲其他的部分,让大家觉得不那么枯燥. 支持向 ...

  3. 机器学习实战六步法之训练模型、优化模型、部署模型(七)

    要落地一个机器学习的项目,是有章可循的,通过这六个步骤,小白也能搞定机器学习. 看我闪电六连鞭!

  4. 机器学习笔记(六)支持向量机

    6.支持向量机 6.1间隔与支持向量 对于给定的训练集D={(x1,y1),(x2,y2),-,(xm,ym)},yi∈{-1,+1},分类学习的初衷就是基于训练集在样本空间中找到一个可以有效划分样本 ...

  5. 机器学习实战之支持向量机(SVM)(三)SMO高效优化算法

    转载请注明作者和出处:https://blog.csdn.net/weixin_45814668 知乎:https://www.zhihu.com/people/qiongjian0427 Git:h ...

  6. 《机器学习实战》支持向量机(手稿+代码)

    注释:已经看过有半年了,而且当时只是看了理论和opencv的函数调用,现在忘的九霄云外了,Ng的视频也没看SVM,现在准备系统的再学一遍吧. 之前记录的博客:http://www.cnblogs.co ...

  7. Python3《机器学习实战》学习笔记(八):支持向量机原理篇之手撕线性SVM

    原 Python3<机器学习实战>学习笔记(八):支持向量机原理篇之手撕线性SVM 置顶 2017年09月23日 17:50:18 阅读数:12644 转载请注明作者和出处: https: ...

  8. 机器学习实战(五)支持向量机SVM(Support Vector Machine)

    目录 0. 前言 1. 寻找最大间隔 2. 拉格朗日乘子法和KKT条件 3. 松弛变量 4. 带松弛变量的拉格朗日乘子法和KKT条件 5. 序列最小优化SMO(Sequential Minimal O ...

  9. Python3:《机器学习实战》之支持向量机(2)简化版SMO

    Python3:<机器学习实战>之支持向量机(2)简化版SMO 转载请注明作者和出处:http://blog.csdn.net/u011475210 代码地址:https://github ...

  10. 【白话机器学习】算法理论+实战之支持向量机(SVM)

    1. 写在前面 如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的, 常见的机器学习算法: 监督学习算法:逻辑回归,线性回归,决策树,朴素贝叶斯,K近邻,支持向量机,集成算法A ...

最新文章

  1. React Native 网络层分析
  2. Java 线程同步 synchronized
  3. 针对不同基础学Java编程的人,提出的小建议?
  4. volatile可以保证原子性吗
  5. Linux shell multifile content replace with sed
  6. 计算机知识太多了,计算机基础知识对程序员来说有多重要?
  7. WSGI服务器实践二--实践一个基本功能的WSGI服务器
  8. dbentry mysql_DbEntry.Net(Lephone Framework) Access ORM:安装和简单使用
  9. Atitit.单向sso  单点登录的设计与实现
  10. WARCannon:高速低功耗网络爬虫
  11. Mybatis自动去重
  12. 第二章 软件项目立项与规划
  13. 计算机专业应届毕业生如何找工作(偏软件方向)
  14. 友华 PT926G 超管密码 V3.0
  15. webservice学习wsdl解读(2)
  16. Windows系统与Linux系统下的硬盘分区操作
  17. arm neon介绍
  18. linux系统的超级管理员,系统的超级管理员:root《 Linux 文件与目录权限 》
  19. 电脑上的软件卸载不了怎么办
  20. 【人工智能】2017年中国人工智能技术——智能语音应用报告

热门文章

  1. MongoDB复制选举原理及复制集管理
  2. C#设计模式之十五迭代器模式(Iterator Pattern)【行为型】
  3. java内存泄露问题
  4. IBM Watson物联网平台的两个MQTT工具
  5. MongoDB 聚合
  6. 给定任意字符串,计算一共能组合成多少个单词bing
  7. 根据定制的 XML 文件进行随机抽取节
  8. unit 10计算机英语教程,计算机英语实用教程Unit 10.doc
  9. scala java maven项目_IntelliJ IDEA下Maven创建Scala项目的方法步骤
  10. ups计算软件_ups不间断电源系统分类及作用