第6章 支持向量机

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第6章 支持向量机相关的知识,希望对你有一定的参考价值。

参考技术A 支持向量机 是一类按监督学习方式对数据进行 二元分类 的广义线性分类器,它的目的是寻找一个 超平面 来对样本进行分割,分割的原则是 间隔最大化 ,最终转化为一个 凸二次规划 问题来求解。

优点:

1.有严格的数学理论支持,可解释性强

2.能找出对任务至关重要的关键样本(即支持向量)

3.采用核技巧后,可以处理非线性分类/回归任务

4.最终决策函数只由少数的支持向量所确定,计算的复杂度取决于支持向量的数目,而不是样本空间的维数,在某种意义上避免了“维数灾难”

缺点:

1.训练时间长

2.当采用核技巧时,如果需要存储核矩阵,空间复杂度大

3.模型预测时,支持向量数目较大,预测计算复杂度高

本文重点对基于硬间隔的线性可分支持向量机、基于核函数的非线性支持向量机、基于软间隔的线性支持向量机这三类进行介绍。

给定训练样本集D=( , ),( , ),...,( , ), -1,+1,分类学习基于训练集D在样本空间中找到一个划分超平面将不同类别的样本分开,但能将训练样本分开的划分超平面有很多,而我们要努力找到位于两类训练样本“ 正中间 ”的划分超平面(如图中的粗线),它对训练样本局部扰动的“容忍”性最好,即它产生的分类效果是 最鲁棒 的,对未见示例的 泛化能力最强 。

在样本空间中,划分超平面可通过线性方程来描述:

样本空间任意点x到超平面(w,b)的距离为

假设超平面(w,b)能将训练样本正确分类,则 约束 条件为:

使式子等号成立的训练样本点被称为“ 支持向量 ”(如图带圈圈的标记)。

两个异类支持向量到超平面的距离之和( 间隔 )为:

“ 最大间隔 ”的划分超平面条件:满足式(6.3)中对参数w和b,使得 最大,即:

可改写为(支持向量机 SVM的基本型 ):

对 凸二次规划 问题使用 拉格朗日乘子法 可得到对偶问题,具体是对每条约束 添加拉格朗日乘子 0, 从而得出拉格朗日函数后,令对w和b的偏导为零,将得出的式子带入拉格朗日函数后可得到原式对应的 对偶问题 ,用 SMO算法 对对偶问题求解后,即可得到最大间隔划分超平面所对应的模型(上述过程需满足 KKT条件 )。

在线性可分的假设下,希望得到的最大间隔划分超平面所对应的 模型 为:

由KKT条件,对任意训练样本( , ),总有 = 0  或 = 0 。

若 = 0,则该样本将不会在式(6.12)的求和中出现,也就不会对模型有任何影响;

若 > 0 ,则必有 = 0,所对应的样本点位于最大间隔边界上,是一个支持向量。

这显示出支持向量机的一个重要性质: 训练完成后,大部分的训练样本都不需保留,最终模型仅与支持向量有关。

在现实任务中,原始样本空间内也许并不存在一个能正确划分为两类样本的超平面。这时,可 将样本从原始空间映射到一个更高维的特征空间 ,使得样本 在这个特征空间内线性可分 。

那么,在特征空间中划分超平面所对应的 模型 表示为:

类似式(6.6),有原始 目标函数 :

用拉格朗日乘子法得到其对偶问题为:

为避开计算困难,可以通过设想一个 核函数 :

核函数的作用 :核函数可以用 原始样本空间上的点内积 的方式,经过运算转化为高维空间点内积,而不必完全由高维空间上的点进行内积计算,这样达到了降低运算复杂度的作用。即从先升维度再算内积变成了 先算内积再升维度 。

在低纬空间(原始样本空间)中对于内积的运算则被定义为“ 核函数 ”, 在原始样本空间经过核函数计算的内积会等于高维空间的内积。

由此,原始目标函数经过改写求解出特征空间中划分超平面所对应的模型:

几种常用的核函数:

核函数的引入一方面减少了计算量,另一方面减少了存储数据的内存使用量。

在现实任务中往往难确定合适的核函数使得训练样本在特征空间中线性可分,即使恰好找到了也很难断定这个 貌似线性可分 的结果不是由于过拟合所造成的。为缓解这一问题是 允许支持向量机在一些样本上出错 。

软间隔 :数据样本不是实际的线性可分,而是 近似线性可分 ,即 允许某些样本不满足约束 :

由此,原始目标函数中增加了一个 损失函数 可写为:

三种常用的替代损失函数:

若采用hinge损失,则目标函数变成:

为度量这个间隔软到何种程度,引入“松弛变量” (即用以表示该样本不满足约束的程度),将上式改写得到“ 软间隔支持向量机 ”:

通过拉格朗日乘子法得到目标函数的拉格朗日函数并得到其对偶问题过程如下:

上述过程需满足 KKT条件 要求:

对于任意训练样本( , ),总有 或 .

若 ,则样本不会对模型有任何影响;

若 ,   则必有 , 即该样本是支持向量:

由式(6.39)可知, 

    若 ,则 ,进而有 ,即该样本恰在最大间隔边界上,

    若 ,则 ,此时若 ,则该样本落在最大间隔内部,

                                                        若 ,则该样本被错误分类。

由此可看出: 软间隔支持向量机的最终模型仅与支持向量有关。

把目标函数中的0/1损失函数换成别的替代损失函数可得到其他学习模型,这些模型具有一个 共性 :优化目标中的第一项用来描述划分超平面的间隔大小( 结构风险 ),另一项用来表述训练集上的误差( 经验风险 ),所以加了损失函数的线性支持向量机优化目标的 一般形式 为:

正则化 :对不希望得到的结果施以惩罚,从而使得优化过程趋向于希望目标。

上式是“正则化”问题, 称为正则化项,C称为正则化常数, 范数是常用的正则化项。

SVR与SVM的区别:

 SVM是要使到超平面 最近 的样本点的“距离” 最大 ;

 SVR则是要使到超平面 最远 的样本点的“距离” 最小 。

传统回归模型与支持向量回归计算损失的区别 :

传统回归模型直接基于模型输出 与真实输出 之间的差别来计算损失,当且仅当 与 完全相同时,损失才为零。

支持向量回归假设 能容忍 与 之间最多有 的偏差 ,仅当 与 之间的差别绝对值 大于 时 才计算损失,这相当于以 为中心,构建了一个 宽度为 的间隔带 ,若训练样本落入此间隔带,则以为是被预测正确的。

于是, SVR问题的目标函数 为:

加入 松弛变量 和 ,改写为:

再用拉格朗日乘子法得到 SVR的对偶问题 :

求解后得到 SVR模型 :

能使式(6.53)中的 的样本即为 SVR的支持向量 ,它们 必落在间隔带之外 。

上述过程需满足 KKT条件 :

若考虑 特征映射形 式, SVR模型 为:

核函数定理 :令 为输入空间, 是定义在 上的 对称函数 ,则 是核函数当且仅当对于任意数据D= ,“ 核矩阵 ”总是 半正定 的:

表示定理 :令H为核函数 对应的再生核希尔伯特空间, 表示H空间中关于h的范数,对于任意单调递增函数 和任意非负损失函数 ,优化问题

表示定理对损失函数 没有限制 ,对正则化项 仅要求 单调递增 ,即对于一般的损失函数和正则化项,优化问题的最优解 都可以表示为核函数 的线性组合。

引入核函数能将线性学习器扩展为非线性学习器。

这里我们使用sklearn的 乳腺癌数据 对以下5种模型的准确度进行预测,重点放在SVC上。

SVC主要调节的参数有: C (正则化参数)、 kernel (核函数)、 degree (多项式维度)、 gamma (核函数参数)、 coef0 (核函数的常数项)。

第一次我用SVC的默认参数,此时的核函数是 高斯核函数(kernel=‘rbf’) ,结果测试集的准确度为62.9%,太低了!说明存在严重的 过拟合 情况。

第二次我选择 改变核函数

用维度为2的 多项式核函数(kernel=‘poly’degree=2) 试试,测试集准确度变为95.1%,感觉比高斯核函数好多了!

线性核函数(kernel=‘linear’) 也来试试,多项式核函数当维度为1时 (kernel=‘poly’,degree=1) 退化为线性核。咦,测试集的准确度提升到了95.8%,但是测试集和训练集的准确度太过于接近,可能会有 欠拟合 的情况。

sigmoid核函数(kernel=‘sigmoid’) 也来试试,真的是太太太低了吧,算了果断抛弃。

第三次,对于常用的 高斯核函数 ,就这么被PK下去了感觉不太好,我决定试试 改变正则化参数 C 看看能不能挽救它,默认下的是C=1.0. 乳腺癌数据集的特征具有完全不同的数量级,这对SVC模型影响比较大,所以先进行 归一化处理 ,对每 个特征进行缩放 ,使其缩放到 0 和 1 之间 。归一化处理后,默认参数下的SVC模型测试集的准确率已经高达96.5%了。

改变C值 试试,当C值为1000时,测试集准确度又提高了,达到了97.4%,说明 增大C值可以优化模型。

第一次我先用了决策树里面默认的参数,其中 max_depth=None ,即树的深度是无穷的,此时出现了训练集的准确度为100%,说明出现了 过拟合 情况。

对于上述过拟合情况我采取的是 限制树的深度 。限制树的深度可以 减少过拟合 。这会 降低训练集的精度,但可以提高测试集的精度。

从 max_depth=3 开始,发现训练集的准确率下降了,但是测试集的准确度从93%提高到了94.4%,明显泛化性能提高了。

再用 max_depth=4 试试,测试集准确度为95.1%,泛化性能又提高了。可!

复现经典:《统计学习方法》第 7 章 支持向量机

本文是李航老师的《统计学习方法》[1]一书的代码复现。

备注:代码都可以在github[3]中下载。

代码目录

  • 第 1 章 统计学习方法概论
  • 第 2 章 感知机
  • 第 3 章 k 近邻法
  • 第 4 章 朴素贝叶斯
  • 第 5 章 决策树
  • 第 6 章 逻辑斯谛回归
  • 第 7 章 支持向量机
  • 第 8 章 提升方法
  • 第 9 章 EM 算法及其推广
  • 第 10 章 隐马尔可夫模型
  • 第 11 章 条件随机场
  • 第 12 章 监督学习方法总结

代码参考:wzyonggege[4],WenDesi[5],火烫火烫的[6]

第 7 章 支持向量机

1.支持向量机最简单的情况是线性可分支持向量机,或硬间隔支持向量机。构建它的条件是训练数据线性可分。其学习策略是最大间隔法。可以表示为凸二次规划问题,其原始最优化问题为

求得最优化问题的解为 ,得到线性可分支持向量机,分离超平面是

分类决策函数是

最大间隔法中,函数间隔与几何间隔是重要的概念。

线性可分支持向量机的最优解存在且唯一。位于间隔边界上的实例点为支持向量。最优分离超平面由支持向量完全决定。二次规划问题的对偶问题是

通常,通过求解对偶问题学习线性可分支持向量机,即首先求解对偶问题的最优值

,然后求最优值 ,得出分离超平面和分类决策函数。

2.现实中训练数据是线性可分的情形较少,训练数据往往是近似线性可分的,这时使用线性支持向量机,或软间隔支持向量机。线性支持向量机是最基本的支持向量机。

对于噪声或例外,通过引入松弛变量 ,使其“可分”,得到线性支持向量机学习的凸二次规划问题,其原始最优化问题是

求解原始最优化问题的解 ,得到线性支持向量机,其分离超平面为

分类决策函数为

线性可分支持向量机的解 唯一但 不唯一。对偶问题是

线性支持向量机的对偶学习算法,首先求解对偶问题得到最优解 ,然后求原始问题最优解 ,得出分离超平面和分类决策函数。

对偶问题的解 中满 的实例点 称为支持向量。支持向量可在间隔边界上,也可在间隔边界与分离超平面之间,或者在分离超平面误分一侧。最优分离超平面由支持向量完全决定。

线性支持向量机学习等价于最小化二阶范数正则化的合页函数

3.非线性支持向量机

对于输入空间中的非线性分类问题,可以通过非线性变换将它转化为某个高维特征空间中的线性分类问题,在高维特征空间中学习线性支持向量机。由于在线性支持向量机学习的对偶问题里,目标函数和分类决策函数都只涉及实例与实例之间的内积,所以不需要显式地指定非线性变换,而是用核函数来替换当中的内积。核函数表示,通过一个非线性转换后的两个实例间的内积。具体地, 是一个核函数,或正定核,意味着存在一个从输入空间 x 到特征空间的映射 ,对任意 ,有

对称函数

,任意正整数 ,对称函数 对应的 Gram 矩阵是半正定的。


所以,在线性支持向量机学习的对偶问题中,用核函数 替代内积,求解得到的就是非线性支持向量机

4.SMO 算法

SMO 算法是支持向量机学习的一种快速算法,其特点是不断地将原二次规划问题分解为只有两个变量的二次规划子问题,并对子问题进行解析求解,直到所有变量满足 KKT 条件为止。这样通过启发式的方法得到原二次规划问题的最优解。因为子问题有解析解,所以每次计算子问题都很快,虽然计算子问题次数很多,但在总体上还是高效的。


分离超平面:

点到直线距离:

为 2-范数:

直线为超平面,样本可表示为:

margin:

函数间隔:

几何间隔: ,当数据被正确分类时,几何间隔就是点到超平面的距离

为了求几何间隔最大,SVM 基本问题可以转化为求解:( 为几何间隔,( 为函数间隔)

分类点几何间隔最大,同时被正确分类。但这个方程并非凸函数求解,所以要先 ① 将方程转化为凸函数,② 用拉格朗日乘子法和 KKT 条件求解对偶问题。

① 转化为凸函数:

先令 ,方便计算(参照衡量,不影响评价结果)

再将 转化成 求解凸函数,1/2 是为了求导之后方便计算。

② 用拉格朗日乘子法和 KKT 条件求解最优值:

整合成:

推导:

根据 KKT 条件:

代入

再把 max 问题转成 min 问题:

以上为 SVM 对偶问题的对偶形式


kernel

在低维空间计算获得高维空间的计算结果,也就是说计算结果满足高维(满足高维,才能说明高维下线性可分)。

soft margin & slack variable

引入松弛变量 ,对应数据点允许偏离的 functional margin 的量。

目标函数:

对偶问题:


Sequential Minimal Optimization

首先定义特征到结果的输出函数: .

因为


参考资料:

[1] :Lagrange Multiplier and KKT[7]

[2] :推导 SVM[8]

[3] :机器学习算法实践-支持向量机(SVM)算法原理[9]

[4] :Python 实现 SVM[10]

import numpy as npimport pandas as pdfrom sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_splitimport matplotlib.pyplot as plt%matplotlib inline
# datadef create_data(): iris = load_iris() df = pd.DataFrame(iris.data, columns=iris.feature_names) df['label'] = iris.target df.columns = [ 'sepal length', 'sepal width', 'petal length', 'petal width', 'label' ] data = np.array(df.iloc[:100, [0, 1, -1]]) for i in range(len(data)): if data[i, -1] == 0: data[i, -1] = -1 # print(data) return data[:, :2], data[:, -1]
X, y = create_data()X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
plt.scatter(X[:50,0],X[:50,1], label='0')plt.scatter(X[50:,0],X[50:,1], label='1')plt.legend()

  
    
    
  


class SVM: def __init__(self, max_iter=100, kernel='linear'): self.max_iter = max_iter self._kernel = kernel
def init_args(self, features, labels): self.m, self.n = features.shape self.X = features self.Y = labels self.b = 0.0
# 将Ei保存在一个列表里 self.alpha = np.ones(self.m) self.E = [self._E(i) for i in range(self.m)] # 松弛变量 self.C = 1.0
def _KKT(self, i): y_g = self._g(i) * self.Y[i] if self.alpha[i] == 0: return y_g >= 1 elif 0 < self.alpha[i] < self.C: return y_g == 1 else: return y_g <= 1
# g(x)预测值,输入xi(X[i]) def _g(self, i): r = self.b for j in range(self.m): r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j]) return r
# 核函数 def kernel(self, x1, x2): if self._kernel == 'linear': return sum([x1[k] * x2[k] for k in range(self.n)]) elif self._kernel == 'poly': return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1)**2
return 0
# E(x)为g(x)对输入x的预测值和y的差 def _E(self, i): return self._g(i) - self.Y[i]
def _init_alpha(self): # 外层循环首先遍历所有满足0<a<C的样本点,检验是否满足KKT index_list = [i for i in range(self.m) if 0 < self.alpha[i] < self.C] # 否则遍历整个训练集 non_satisfy_list = [i for i in range(self.m) if i not in index_list] index_list.extend(non_satisfy_list)
for i in index_list: if self._KKT(i): continue
E1 = self.E[i] # 如果E2是+,选择最小的;如果E2是负的,选择最大的 if E1 >= 0: j = min(range(self.m), key=lambda x: self.E[x]) else: j = max(range(self.m), key=lambda x: self.E[x]) return i, j
def _compare(self, _alpha, L, H): if _alpha > H: return H elif _alpha < L: return L else: return _alpha
def fit(self, features, labels): self.init_args(features, labels)
for t in range(self.max_iter): # train i1, i2 = self._init_alpha()
# 边界 if self.Y[i1] == self.Y[i2]: L = max(0, self.alpha[i1] + self.alpha[i2] - self.C) H = min(self.C, self.alpha[i1] + self.alpha[i2]) else: L = max(0, self.alpha[i2] - self.alpha[i1]) H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])
E1 = self.E[i1] E2 = self.E[i2] # eta=K11+K22-2K12 eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel( self.X[i2], self.X[i2]) - 2 * self.kernel(self.X[i1], self.X[i2]) if eta <= 0: # print('eta <= 0') continue
alpha2_new_unc = self.alpha[i2] + self.Y[i2] * ( E1 - E2) / eta #此处有修改,根据书上应该是E1 - E2,书上130-131页 alpha2_new = self._compare(alpha2_new_unc, L, H)
alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * ( self.alpha[i2] - alpha2_new)
b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * ( alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel( self.X[i2], self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.b b2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * ( alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel( self.X[i2], self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.b
if 0 < alpha1_new < self.C: b_new = b1_new elif 0 < alpha2_new < self.C: b_new = b2_new else: # 选择中点 b_new = (b1_new + b2_new) / 2
# 更新参数 self.alpha[i1] = alpha1_new self.alpha[i2] = alpha2_new self.b = b_new
self.E[i1] = self._E(i1) self.E[i2] = self._E(i2) return 'train done!'
def predict(self, data): r = self.b for i in range(self.m): r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])
return 1 if r > 0 else -1
def score(self, X_test, y_test): right_count = 0 for i in range(len(X_test)): result = self.predict(X_test[i]) if result == y_test[i]: right_count += 1 return right_count / len(X_test)
def _weight(self): # linear model yx = self.Y.reshape(-1, 1) * self.X self.w = np.dot(yx.T, self.alpha) return self.w
svm = SVM(max_iter=200)
svm.fit(X_train, y_train)
'train done!'
svm.score(X_test, y_test)
0.92

scikit-learn 实例

from sklearn.svm import SVCclf = SVC()clf.fit(X_train, y_train)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
clf.score(X_test, y_test)
0.96

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=None,random_state=None)

参数:

  • C:C-SVC 的惩罚参数 C?默认值是 1.0

C 越大,相当于惩罚松弛变量,希望松弛变量接近 0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样对训练集测试时准确率很高,但泛化能力弱。C 值小,对误分类的惩罚减小,允许容错,将他们当成噪声点,泛化能力较强。

  • kernel :核函数,默认是 rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’

    – 线性:u'v

    – 多项式:(gamma*u'*v + coef0)^degree

    – RBF 函数:exp(-gamma|u-v|^2)

    – sigmoid:tanh(gamma*u'*v + coef0)

  • degree :多项式 poly 函数的维度,默认是 3,选择其他核函数时会被忽略。
  • gamma :‘rbf’,‘poly’ 和‘sigmoid’的核函数参数。默认是’auto’,则会选择 1/n_features
  • coef0 :核函数的常数项。对于‘poly’和 ‘sigmoid’有用。
  • probability :是否采用概率估计?.默认为 False
  • shrinking :是否采用 shrinking heuristic 方法,默认为 true
  • tol :停止训练的误差值大小,默认为 1e-3
  • cache_size :核函数 cache 缓存大小,默认为 200
  • class_weight :类别的权重,字典形式传递。设置第几类的参数 C 为 weight*C(C-SVC 中的 C)
  • verbose :允许冗余输出?
  • max_iter :最大迭代次数。-1 为无限制。
  • decision_function_shape :‘ovo’, ‘ovr’ or None, default=None3
  • random_state :数据洗牌时的种子值,int 值

主要调节的参数有:C、kernel、degree、gamma、coef0。

参考资料

[1] 《统计学习方法》: https://baike.baidu.com/item/统计学习方法/10430179
[2] 黄海广: https://github.com/fengdu78
[3] github: https://github.com/fengdu78/lihang-code
[4] wzyonggege: https://github.com/wzyonggege/statistical-learning-method
[5] WenDesi: https://github.com/WenDesi/lihang_book_algorithm
[6] 火烫火烫的: https://blog.csdn.net/tudaodiaozhale

[7]:[Lagrange Multiplier and KKT: http://blog.csdn.net/xianlingmao/article/details/7919597
[8]:[推导SVM: https://my.oschina.net/dfsj66011/blog/517766
[9]:[机器学习算法实践-支持向量机(SVM)算法原理: http://pytlab.org/2017/08/15/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AE%97%E6%B3%95%E5%AE%9E%E8%B7%B5-%E6%94%AF%E6%8C%81%E5%90%91%E9%87%8F%E6%9C%BA-SVM-%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86/
[10] :[Python实现SVM: http://blog.csdn.net/wds2006sdo/article/details/53156589

复现经典:《统计学习方法》第 7 章 支持向量机





往期精彩回顾




备注:加入本站微信群或者qq群,请回复“加群

加入知识星球(4300+用户,ID:92416895),请回复知识星球

以上是关于第6章 支持向量机的主要内容,如果未能解决你的问题,请参考以下文章

支持向量机

6.支持向量机(SVM)什么是SVM支持向量机基本原理与思想基本原理课程中关于SVM介绍

支持向量机篇(连载中 6)

数据挖掘十大算法之SVM支持向量机分类算法

《机器学习实战:基于Scikit-LearnKeras和TensorFlow第2版》-学习笔记:支持向量机

[机器学习与scikit-learn-34]:算法-分类-支持向量机SVM的基本简介与基本原理-线性分类