经典机器学习方法(5)——线性支持向量机
- 首发链接:经典机器学习方法(5)——线性支持向量机
- 参考:
- 《统计学习方法(第二版)》第 7 章
- 白板机器学习
支持向量机器(support vector machines, SVM)可以看作之前介绍过的 经典机器学习方法(4)—— 感知机 的升级版,它们都是用于处理二分类问题的判别模型,SVM 进一步解决了感知机只能处理线性可分样本数据、结果不唯一、只能给出线性分类面等问题。SVM 方法包含从简单到复杂的一系列模型,其中简单模型是复杂模型的特殊情况;复杂模型是简单模型的推广,和感知机对比如下模型 适用问题 数据要求 求解策略 得到分类超平面 感知机 线性二分类问题 线性可分 误分类最小化 线性,不唯一 线性可分支持向量机器 线性二分类问题 线性可分 硬间隔最大化 线性,唯一 线性支持向量机器 线性二分类问题 近似线性可分 软间隔最大化 线性,不唯一 非线性支持向量机器 非线性二分类问题 无 核计巧 + 软间隔最大化 非线性,唯一 - 需要注意的一点是,无论感知机还是 SVM 本质都是线性分类器,它们都是先把输入空间(欧式空间或离散集合)中的样本特征映射到特征空间(欧式空间或希尔伯特空间)中,使之成为特征空间里的一个线性二分类问题进行求解
- 受篇幅限制,本文仅讨论 “线性可分支持向量机” 和 “线性支持向量机”,它们分别要求数据线性可分和近似线性可分,这时特征空间和输入空间相同
1. 线性可分 SVM 与硬间隔最大化
- 给定一个特征空间上的线性可分训练数据集
其中 。学习目标是求解线性的分离超平面
以及相应的分类决策函数
- 可见线性可分支持向量机的输入数据和感知机是完全相同的,这时可以有无限多个超平面将两类数据正确划分,为了能够得到唯一的最优决策函数,SVM 增加了一个要求,即 “间隔最大化”。从直观上看,对训练数据集找到间隔最大的超平面意味着以充分大的置信度对训练数据进行分类,也就是不仅要将正负实例点分开,而且要对最难分的实例点(距离超平面最近的点)也有足够大的确信度将它们分开。这样的超平面应该对未知的新实例有很好的分类预测能力
1.1 函数间隔和几何间隔
-
超平面关于某个样本点 的
几何间隔就是几何意义上直接用点到直线距离公式 计算出的距离,在二分类视角下定义为对于被正确分类的样本这个间隔是正数,而误分类点的间隔是负数
-
从感知机模型引出函数间隔,经典机器学习方法(4)—— 感知机 里定义的距离是上述几何间隔的相反数,这样误分类点的距离就是正数,损失就可以自然地设定为 ,其中 是误分类样本集合。因为数据集是线性可分的,所以这个损失一定可以优化到零,这样就可以把系数 去掉,最终损失为
其中被求和的部分就是超平面关于某个样本点 的
函数间隔了对于被正确分类的样本这个间隔是正数,而误分类点的间隔是负数
-
接下来可以定义超平面关于训练数据集 的
函数间隔和几何间隔为线性可分 SVM 的优化目标就是要在正确划分训练集的情况下最大化超平面关于训练数据集 的间隔,那么该使用函数间隔还是几何间隔呢 ?
-
观察公式可见函数间隔 和几何间隔 间的关系就是差一个系数 ,即
这个系数对于感知机无关紧要,但对 SVM 模型来说很重要,因为
- 感知机只考虑 “最小化所有误分类点到超平面的错误间隔之和”,这在线性可分数据集下一定可以优化到零,我们只要在优化过程中定性地检查损失是否为 0 集合,系数 并不影响这个定性指标的计算
- SVM要考虑 “最大化训练数据集到超平面的最小间隔”,这时我们就必须定量地将间隔大小计算出来。超平面 和 是同一个平面的两种表示方法,但在没有系数 的情况下前者的函数间隔是后者的 倍,这会干扰计算
综上所述,SVM 模型定义优化问题时有两个选择
- 使用几何间隔
这是形式化 SVM 优化问题的出发点
- 使用函数间隔 ,并增加 等于某常数的约束
基于几何间隔的优化问题经过简化后,得到的最终优化问题是这个形式
1.2 硬间隔最大化
- SVM 学习的基本想法是求解
能够正确划分数据集且几何间隔最大的分离超平面,其中第二个要求保证得到的解唯一 - 对于线性可分的训练数据集,所有样本点到最终的分离超平面的间隔一定都是正数,因此称其为
硬间隔最大化
1.2.1 问题建模
- 这里我们展开 1.1 节最后的分析,首先按 SVM 学习的原始想法,把硬间隔最大化建模为如下使用几何间隔的约束优化问题
考虑几何间隔和函数间隔的关系,可将上述问题改写为以下使用函数间隔的形式
假设 变成之前的 倍,即 ,则由函数间隔定义 都变成之前的 倍,进而 也变成之前的 倍,这时有
可见常数都可以消去,约束问题没有变化,这说明函数间隔的取值不影响优化问题的解,不妨直接将其设为 1 代入,又注意到 ,最终的优化问题为
注意到每一个样本点对应一个不等式约束
理论上讲,这个优化问题属于凸二次规划问题
- 若约束优化问题的目标函数和不等式约束都是 上连续可微的凸函数;等式约束都是 上的仿射函数,则其是
凸优化问题 - 若凸优化问题的目标函数是二次函数且不等式约束都是仿射函数,则其是
凸二次规划问题,注意它是一种特殊的凸优化问题
凸优化问题具有优良性质:其局部最优解就是全局最优解。另外,凸优化问题的研究较为成熟,如果一个具体问题能被归为一个凸优化问题,基本可以确定该问题是可被求解的
- 若约束优化问题的目标函数和不等式约束都是 上连续可微的凸函数;等式约束都是 上的仿射函数,则其是
1.2.2 线性可分 SVM 最大间隔算法
-
直接解上述约束优化得到分离超平面和相应的分类决策函数,就是线性可分支持向量机的
最大间隔算法
可以证明这样得到的线性可分数据集的最大硬间隔分离超平面是存在且唯一的
具体证明过程见 《统计学习方法(第二版)》P.117
1.2.3 支持向量和间隔边界
-
仔细考虑我们的优化目标 “最大化训练数据集到超平面的最小间隔”,其中 “数据集到超平面的最小间隔” 是距离分类平面最近的样本点到平面的间隔,因此事实上最后优化得到的分离超平面只由距离分离超平面最近的样本点决定,这些样本实例称为
支持向量support vector。从公式角度考虑,支持向量是使优化问题中不等式约束 等号成立的点,具体而言- 正例点 ,位于超平面
- 负例点 ,位于超平面
间隔边界平行且没有样本点落于其中,它们关于分离超平面对称,间隔边界之间的距离称为间隔margin,它依赖于分离超平面的 。在一个简单二分类任务中将上述概念全部图示出来,如下
- 只有支持向量决定了分离超平面的位置,间隔边界以外实例点的存在与否没有任何影响,上图是一个十分巧合的情况,两个间隔边界上都有两个支持向量,只要一个向量和间隔边界稍稍不共面,它的存在也没有意义了。支持向量机由很少的 “重要的” 训练样本(支持向量)确定,这也是其名称的由来
1.3 学习的对偶算法
- 上面 1.2.1 节建模了原始优化问题,这种约束优化问题直接求解有时很困难,我们通常会将其转换为拉格朗日对偶问题求解,这样做有两个好处
- 拉格朗日对偶问题一定是凸优化问题,而且往往能减少约束条件,更容易解
- 可以自然地引入核函数,从而推广到非线性分类问题
- 关于转换对偶问题的方法和原理请参考 一文看懂拉格朗日乘子法、KKT条件和对偶问题
1.3.1 原始问题转换为对偶问题
- 根据以下步骤转换对偶问题
- 先把原始问题变形为约束优化问题的标准形式
- 每个不等式约束引入拉格朗日乘子 和拉格朗日乘子向量 ,定义广义拉格朗日函数为
这时原始问题等价于
对偶问题是
注意到这里我们已经解除了关于 和 的约束,如果不懂怎么推过来的请参考上面文章 3. 进一步处理对偶问题,令偏导为零处理内层的
将上面得到两个式子带回到广义拉格朗日函数得到
最后对目标取相反数,把外层的 变成符合习惯的 ,最终得到对偶问题为
- 先把原始问题变形为约束优化问题的标准形式
- 考虑原始优化问题,优化目标 是凸函数,不等式约束 是仿射函数,因此线性可分 SVM 满足弱 slater 条件,强对偶性成立,对偶问题一定和原问题同解,进而强对偶的必要条件 KKT 条件也成立
1.3.2 线性可分 SVM 对偶算法
- 考察对偶问题的最优解 ,注意到 ,因为
- 从直观上看, 意味着原问题中所有不等式约束都是松弛的,而我们知道支持向量对应的不等式约束条件一定是紧致的,因此出现矛盾
- 从公式上看, 意味着无论数据如何,最优分离超平面都有 ,这显然不合理
- 假设对偶问题的最优解 中至少有一个 ,可以原问题的最优解如下得到
进而得到分类决策函数
- 将上述过程整理为线性可分 SVM 的对偶算法
2. 线性 SVM 与软间隔最大化
- 线性可分支持向量机要求训练数据必须线性可分,因为线性不可分数据集不可能令 1.2.1 节原始问题中的不等式约束全部成立,这在很大程度上限制了 SVM 的实用性。数据收集过程中存在噪声,可能数据的真实分布其实是线性可分的,但由于噪声出现了少量
特异点outlier,这种数据集性质称为近似线性可分,本节我们考虑如何将 SVM 推广应用到这种情况中
2.1 松弛约束
- 一个直观的想法是,允许某些样本违反 的约束,因此我们可以对每个样本点引入一个
松弛变量,使得 ,这有两种等价的理解方式- 把每个样本移动一段后 后使之满足函数间隔大于 1 的要求,即
- 对每个样本灵活调整间隔边界位置,使其函数间隔满足
- 注意,在 的极端情况下,任意分离超平面都能使所有样本满足约束,这就没有意义了,所以我们不想对模型过于宽容,每一个松弛变量都要成比例地支付代价 ,将之体现在目标函数中
这里 是个超参数,称为
惩罚系数。注意正确分类点的,因此惩罚全部来自误分类点, 本质是在 “函数间隔尽量大” 和 “误分类点个数尽量少” 直接进行权衡
2.2 软间隔最大化
2.2.1 问题建模
- 将上述讨论形式化,得到线性支持向量机优化的原始问题
这也是一个凸二次规划问题,因而一定有解。可以证明的最优解是唯一的, 的最优解则存在于一个区间
2.2.2 支持向量和间隔边界
-
由于引入了松弛变量,线性 SVM 的支持向量于线性可分 SVM 稍有不同,先看一个简单二分类任务,画出分离超平面 及间隔边界
上图固定了间隔边界,支持向量可以理解为 “函数间隔本身恰好为 1” 以及 “移动一段后 后才能满足函数间隔 1” 要求的样本。上图中标有箭头的都是支持向量,会影响优化结果。具体而言,这时支持向量可以分成三类
- 位于正确分类一侧的间隔边界上(和线性可分 SVM 一样),
- 位于正确分类一侧的间隔边界和分离超平面之间,
- 位于分离超平面误分类一侧,
2.3 学习的对偶算法
- 我们必须意识到,线性 SVM 只是线性可分 SVM 的简单扩展,从数学形式上也可以看出二者并没有本质区别,因此和第 1 节类似,将原始优化问题转为对偶问题可以使求解更简便,而且便于后面扩展到非线性 SVM 的情况
2.3.1 原始问题转换为对偶问题
- 根据以下步骤转换对偶问题
- 先把原始问题变形为约束优化问题的标准形式
- 每个不等式约束引入拉格朗日乘子 和拉格朗日乘子向量 ,定义广义拉格朗日函数为
这时原始问题等价于
对偶问题是
注意到这里我们已经解除了关于 的约束 3. 进一步处理对偶问题,令偏导为零处理内层的
将上面得到两个式子带回到广义拉格朗日函数得到
最后对目标取相反数,把外层的 变成符合习惯的 ,整理中间得到的新约束,如下
把利用等式约束再化简一下,得到对偶问题的最终形式
- 先把原始问题变形为约束优化问题的标准形式
- 容易上述验证线性 SVM 优化问题也具有强对偶性
2.3.2 线性 SVM 对偶算法
- 类似 1.3.2 节分析,不可能所有 和 都等于 0,因此一定存在 的一个分量 满足 ,则原始问题的解可以按下式求得
进而得到分类决策函数
注意这里 随着选出的 不同而不同,这是线性 SVM 优化结果不唯一的原因
- 将上述过程整理为线性 SVM 的对偶算法
2.4 线性 SVM 的等价解释:优化带 正则项的合页损失
-
回到 2.1 节线性 SVM 的基础想法上,我们的目标是想要所有样本都满足函数间隔(确信度)大于等于 1 的约束 ,如果不满足就设置成比例的惩罚,这个思路可以形式化为对每个样本设置如下损失
其中 称为
合页损失函数(hinge loss function),定义为重要的是,这个损失的定义和样本的松弛变量 完全一致
- 如果本身函数间隔就大于等于 1,则不需要松弛,
- 否则将样本按分离超平面法线方向移动直到函间隔为 1,
-
将上述损失应用到整个训练集,并用 的 范数作为正则化项避免过拟合得到损失函数
回顾线性 SVM 的原问题
可见它和基于合页损失的优化问题 等价
-
最后,以横轴为函数,纵轴为损失值,把合页损失的函数曲线图画出来看一下
- 红色线为 0-1 损失,可以认为它是一切二分类问题真正的损失函数,但它不可导,无法直接使用
- 黑色虚线为感知机损失函数,正确分类时(函数间隔大于0)损失值为0,否则为函数间隔大小
- 蓝色线为感知机使用的合页损失函数,可见
- 合页损失是 0-1 损失的上界,可以认为线性 SVM 是在优化由 0-1 损失上界构成的目标函数
- 合页损失比感知机损失更严格,不仅要求分类正确,还要确信度足够高(函数间隔足够大)损失值才是0,合页损失对学习有更高的要求
3. 代码实现
-
使用 Python CVXOPT 凸优化库实现上述算法,为简便考虑,本节仅实现线性可分 SVM 的原始算法和对偶算法。关于该模块的使用方法可以参考 《Python进阶系列》二十三:解决线性规划和二次型规划问题的CVXOPT模块
-
使用
sklearn.datasets库生成数据,关于该库的使用可以参考【sklearn】dataset模块(2)—— 生成数据集,生成数据后,将其处理为 CVXOPT 库解二次规划问题的标准形式
然后直接调库求解,进而得到原问题最优解即可
3.1 线性可分 SVM 的原始算法
-
完整代码如下,可以直接复制进 vscode 运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48import numpy as np
from cvxopt import matrix, solvers
from sklearn import datasets
from matplotlib import pyplot as plt
SAMPLENUM = 20
COLORS = ['r', 'b']
def drawPlane(w1,w2,b):
x1 = np.array([-4.0, 4.0])
x2 = (-w1*x1-b)/w2
plt.plot(x1, x2)
# 生成数据
my_datas = datasets.make_blobs(n_samples=SAMPLENUM,
n_features=2,
centers=np.array([[-3,-3], [3,3]]),
center_box = (-10,10),
cluster_std=[1.0,1.0])
features, labels = my_datas
labels[labels==0] = -1
labels = labels.reshape(labels.size,1)
# 用矩阵表示原二次规划问题
P = 2*matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0]])
q = matrix([0.0, 0.0, 0.0])
h = matrix(-1*np.ones(labels.size))
G = np.hstack((features, np.ones((labels.size,1))))
G = np.multiply(G, -labels)
G = matrix(G)
# 使用 cvxopt 求解
sol=solvers.qp(P, q, G, h)
print(sol['x'])
w1,w2,b = sol['x']
# 绘制样本,分离超平面和间隔边界
plt.scatter(features[:, 0], features[:, 1], c=labels, s=30)
drawPlane(w1,w2,b)
drawPlane(w1,w2,b+1)
drawPlane(w1,w2,b-1)
for i in range(SAMPLENUM):
if abs(w1*features[i][0] + w2*features[i][1] + b - 1) < 1e-3:
plt.scatter(features[i,0], features[i,1], c='r', s=50, marker='x')
if abs(w1*features[i][0] + w2*features[i][1] + b + 1) < 1e-3:
plt.scatter(features[i,0], features[i,1], c='r', s=50, marker='x')
3.2 线性可分 SVM 的对偶算法
-
完整代码如下,可以直接复制进 vscode 运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59import numpy as np
from cvxopt import matrix, solvers
from sklearn import datasets
from matplotlib import pyplot as plt
SAMPLENUM = 20
COLORS = ['r', 'b']
def drawPlane(w1,w2,b):
x1 = np.array([-4.0, 4.0])
x2 = (-w1*x1-b)/w2
plt.plot(x1, x2)
# 生成数据
my_datas = datasets.make_blobs(n_samples=SAMPLENUM,
n_features=2,
centers=np.array([[-3,-3], [3,3]]),
center_box = (-10,10),
cluster_std=[1.0,1.0])
features, labels = my_datas
labels[labels==0] = -1
labels = labels.reshape(labels.size,1)
# 用矩阵表示对偶二次规划问题
t = np.multiply(features, labels)
P = np.dot(t,t.T)
P = matrix(P)
q = matrix(-1.0*np.ones((labels.size,1)))
G = matrix(-1.0*np.eye(labels.size))
h = matrix(np.zeros((labels.size,1)))
b = matrix(0.0)
A = matrix(labels.astype(np.double), (1,labels.size))
# 使用 cvxopt 求解
sol=solvers.qp(P, q, G, h, A, b)
alphas = np.array(sol['x'])
w = np.multiply(features, alphas)
w = np.multiply(w, labels)
# 还原原问题的解
alpha_j = np.max(alphas)
label_j = labels[np.argmax(alphas)]
feature_j = features[np.argmax(alphas)]
b = label_j.item() - np.sum(np.dot(w, feature_j.T), axis=0)
w = np.sum(w, axis=0)
w1,w2 = w[0],w[1]
# 绘制样本,分离超平面和间隔边界
plt.scatter(features[:, 0], features[:, 1], c=labels, s=30)
drawPlane(w1,w2,b)
drawPlane(w1,w2,b+1)
drawPlane(w1,w2,b-1)
for i in range(SAMPLENUM):
if abs(w1*features[i][0] + w2*features[i][1] + b - 1) < 1e-3:
plt.scatter(features[i,0], features[i,1], c='r', s=50, marker='x')
if abs(w1*features[i][0] + w2*features[i][1] + b + 1) < 1e-3:
plt.scatter(features[i,0], features[i,1], c='r', s=50, marker='x')
考虑到 SVM 的原始问题也是一个简单的凸二次规划,这里其实看不太出来转化对偶形式的优势,但是注意到对偶形式中计算了很多向量内积,这对于引入核技巧推广到非线性 SVM 非常重要,下一篇文章进行详细介绍