吴恩达机器学习笔记(10)—支持向量机
目录
- 一、支持向量机
- 1、优化目标
- 2、大间隔思想
- 3、几何角度理解大间隔
- 二、实际使用
- 1、软间隔
- 2、高斯核函数
- 三、代码实现
本节将继续介绍监督学习中的一个强力的分类器——支持向量机(Support Vector Machine,SVM),其通过「间隔最大化」的思想来学习最优的决策边界,比逻辑回归算法更为鲁棒。
一、支持向量机
首先来直观地理解一下决策边界,考虑一个线性可分的二分类问题:
图中有三条直线分别代表三个分类器的决策边界,可以直观地感受到 H3H_3H3 应该会好于 H1H_1H1 和 H2H_2H2 。显然,H1H_1H1 甚至不能把类别正确分开,肯定不能考虑;H2H_2H2 虽然可以,但是边界距离最近的数据点只有很小的间隔,如果测试数据有一些噪声的话,很可能会被 H2H_2H2 错误地分到另一类——即对噪声敏感、泛化能力弱。
而 H3H_3H3 似乎距离两侧都有较大的间隔,可以容忍一些微小的噪声。如何能学习到这样一个分类器呢?
1、优化目标
回忆逻辑回归的优化目标:
minθ1m[∑i=1my(i)(−lnhθ(x(i)))+(1−y(i))(−ln(1−hθ(x(i))))]+λ2m∑j=1nθj2\min_{\theta} \frac{1}{m} \left[ \sum_{i=1}^{m} y^{(i)} \left( -\ln h_{\theta} \left( x^{(i)} \right) \right) + \left( 1 - y^{(i)} \right) \left( -\ln \left( 1 - h_{\theta} \left( x^{(i)} \right) \right) \right) \right] + \frac{\lambda}{2m} \sum_{j=1}^{n} \theta_{j}^{2}θminm1[i=1∑my(i)(−lnhθ(x(i)))+(1−y(i))(−ln(1−hθ(x(i))))]+2mλj=1∑nθj2
其中的 hθ(x(i))h_{\theta}\left(x^{(i)}\right)hθ(x(i)) 使用 sigmoid 函数,因此对于一个 y=1y = 1y=1 的样本,我们希望 hθ(x)h_{\theta}(x)hθ(x) 也趋近于 1,也就是 θTx\theta^{T}xθTx 远大于 0,反之同理。进一步地,我们只考虑优化目标中的代价函数:
Cost(hθ(x),y)={−ln(hθ(x))y=1−ln(1−hθ(x))y=0\text{Cost}\left(h_{\theta}(x), y\right)= \begin{cases} -\ln \left(h_{\theta}(x)\right) & y = 1 \\ -\ln \left(1 - h_{\theta}(x)\right) & y = 0 \end{cases}Cost(hθ(x),y)={−ln(hθ(x))−ln(1−hθ(x))y=1y=0
画出两种情况下的代价函数关于 θTx\theta^{T}xθTx 的曲线:
记上图中左图为 cost1(θTx)\text{cost}_1(\theta^T x)cost1(θTx),即分类为 1 时采用的代价;右图为 cost0(θTx)\text{cost}_0(\theta^T x)cost0(θTx),即分类为 0 时采用的代价。在支持向量机中,我们不再采用 sigmoid 函数,而是将其修改为一条非常接近的折线:
其中,折线的拐点位于 z=±1z = \pm 1z=±1 的位置,先不考虑斜率的大小,则代价函数可以表示为:
cost1(z)={0z⩾1k1(z−1)z<1cost0(z)={0z⩽−1k0(z+1)z>−1\text{cost}_1(z) = \begin{cases} 0 & z \geqslant 1 \\ k_1(z - 1) & z < 1 \end{cases} \quad \text{cost}_0(z) = \begin{cases} 0 & z \leqslant -1 \\ k_0(z + 1) & z > -1 \end{cases}cost1(z)={0k1(z−1)z⩾1z<1cost0(z)={0k0(z+1)z⩽−1z>−1
此代价函数也被称为折页损失(Hinge Loss),非常形象,常用于凸优化任务中,也被写作:
L(y)=max(0,1−y^⋅y)L(y) = \max(0, 1 - \hat{y} \cdot y)L(y)=max(0,1−y^⋅y)
其中 y=±1y = \pm 1y=±1 为正确的标签,y^\hat{y}y^ 为预测输出,通常是软结果,即取 [−1,1][-1, 1][−1,1] 区间中的值。
另外,SVM 中习惯不除以样本大小 mmm,这不会影响最终的优化目标。并将正则化参数放在第一项而非第二项,即支持向量机的优化目标为:
minθC∑i=1m[y(i)cost1(θTx(i))+(1−y(i))cost0(θTx(i))]+12∑j=1nθj2\min_{\theta} C \sum_{i=1}^{m} \left[ y^{(i)} \text{cost}_1 \left( \theta^T x^{(i)} \right) + \left( 1 - y^{(i)} \right) \text{cost}_0 \left( \theta^T x^{(i)} \right) \right] + \frac{1}{2} \sum_{j=1}^{n} \theta_j^2θminCi=1∑m[y(i)cost1(θTx(i))+(1−y(i))cost0(θTx(i))]+21j=1∑nθj2
其中,CCC 就是正则化参数,类比逻辑回归中 λ\lambdaλ 的作用。并且当 CCC 取到 1λ\frac{1}{\lambda}λ1 时两者的优化目标一致。
2、大间隔思想
显然,新的代价函数会在计算速度上有一定的优势,但它是如何最大化间隔(Margin) 的呢?我们首先观察第一项代价 y1(θTx)+(1−y)0(θTx)y_1(\theta^T x) + (1 - y)_0(\theta^T x)y1(θTx)+(1−y)0(θTx),欲使之最小,最理想的情况就是对于每个样本,都有 costy(θTx)=0\text{cost}_y(\theta^T x) = 0costy(θTx)=0,对应着:
{θTx⩾1if y=1θTx⩽−1if y=0\begin{cases} \theta^T x \geqslant 1 & \text{if } y = 1 \\ \theta^T x \leqslant -1 & \text{if } y = 0 \end{cases}{θTx⩾1θTx⩽−1if y=1if y=0
而在逻辑回归中,我们只需要使误分类的样本最少,也就是:
{θTx⩾0if y=1θTx⩽0if y=0\begin{cases} \theta^T x \geqslant 0 & \text{if } y = 1 \\ \theta^T x \leqslant 0 & \text{if } y = 0 \end{cases}{θTx⩾0θTx⩽0if y=1if y=0
因此 SVM 相当于将判别条件变得更苛刻,以文章开篇提到的三条决策边界为例,直线 H2H_2H2 和 H3H_3H3 以及它俩的任意线性组合,都能完美分开两类,也就是能有无穷多个解满足逻辑回归的优化目标。但对于支持向量机而言,可选的解会相对少很多。而这也使 SVM 能相对更具有鲁棒性,因为我们留出了一个「安全边界」来应对噪声样本。
接下来假设一种极端的情况,我们将正则化参数 CCC 取一个非常大的值,此时模型会倾向于将第一项收敛为 0。那么模型的优化目标可以改写为:
minθ12∑j=1nθj2s.t {θTx(i)⩾1if y(i)=1θTx(i)⩽−1if y(i)=0\min_{\theta} \frac{1}{2} \sum_{j=1}^{n} \theta_j^2 \text{ s.t } \begin{cases} \theta^T x^{(i)} \geqslant 1 & \text{if } y^{(i)} = 1 \\ \theta^T x^{(i)} \leqslant -1 & \text{if } y^{(i)} = 0 \end{cases}θmin21j=1∑nθj2 s.t {θTx(i)⩾1θTx(i)⩽−1if y(i)=1if y(i)=0
上式也被称为硬间隔(Hard Margin) 优化目标,用于在线性可分的数据集中找到唯一的最优边界。但在现实情况中,数据样本通常更加复杂,即使真的线性可分,也可能会存在部分离群点,而模型为了得到完美的边界,会学习到偏差很大的边界。因此在实际中通常不将 CCC 设置得过大,采用软间隔(Soft Margin)。
回顾我们前面说到的,可以将 CCC 类比为 1λ\frac{1}{\lambda}λ1,因此:
- CCC 较大时,相当于 λ\lambdaλ 较小,可能会导致过拟合、高方差,出现如上图红线那样的情形。
- CCC 较小时,相当于 λ\lambdaλ 较大,可能会导致欠拟合、高偏差,甚至无法分类。
3、几何角度理解大间隔
在前文改写的优化目标的基础上,我们注意到 θTx\theta^T xθTx 其实是参数 θ\thetaθ 和 xxx 两个向量的点积(内积),可以视作 xxx 向 θ\thetaθ 的投影长度乘上 θ\thetaθ 的长度 ∥θ∥\|\theta\|∥θ∥。
为了更好理解,我们忽略掉截距,令 θ0=0\theta_0 = 0θ0=0,将特征数 nnn 置为 2,于是仅有两个特征 {x1,x2}\{x_1, x_2\}{x1,x2} 和两个参数 {θ1,θ2}\{\theta_1, \theta_2\}{θ1,θ2}。则此时有:
12∑j=1nθj2=12(θ12+θ22)=12(θ12+θ22)2=12∥θ∥2\frac{1}{2} \sum_{j=1}^{n} \theta_j^2 = \frac{1}{2} \left( \theta_1^2 + \theta_2^2 \right) = \frac{1}{2} \left( \sqrt{\theta_1^2 + \theta_2^2} \right)^2 = \frac{1}{2} \|\theta\|^221j=1∑nθj2=21(θ12+θ22)=21(θ12+θ22)2=21∥θ∥2
从几何角度理解,参数 θ\thetaθ 其实是决策边界的法向量,因此会和边界本身呈现垂直关系。而特征 xxx 就是样本点在(特征空间)坐标系上的向量,起点为原点。绘制出如下样例:
其中 p(i)p^{(i)}p(i) 为投影轴的长度(可正可负),于是可以得到:
θTx(i)=θ1x1(i)+θ2x2(i)=p(i)∥θ∥\theta^T x^{(i)} = \theta_1 x_1^{(i)} + \theta_2 x_2^{(i)} = p^{(i)} \|\theta\|θTx(i)=θ1x1(i)+θ2x2(i)=p(i)∥θ∥
因此优化目标可以从几何角度再次改写为:
minθ12∥θ∥2s. t {p(i)∥θ∥⩾1if y(i)=1p(i)∥θ∥⩽−1if y(i)=0\min_{\theta} \frac{1}{2} \|\theta\|^2 \text{ s. t } \begin{cases} p^{(i)} \|\theta\| \geqslant 1 & \text{if } y^{(i)} = 1 \\ p^{(i)} \|\theta\| \leqslant -1 & \text{if } y^{(i)} = 0 \end{cases}θmin21∥θ∥2 s. t {p(i)∥θ∥⩾1p(i)∥θ∥⩽−1if y(i)=1if y(i)=0
显然,为了让法向量的范数 ∥θ∥2\|\theta\|^2∥θ∥2 最小化,我们必须增大样本在法向量上的投影 p(i)p^{(i)}p(i) 长度,如下图所示:
以上就是为什么支持向量机最终会找到最大间隔的原因。当然,在推导的过程中我们使用了简化的假设,例如 θ0=0\theta_0 = 0θ0=0,这是为了让决策平面通过原点。
二、实际使用
1、软间隔
当训练数据不能线性可分但是可以近似线性可分时,可以允许 SVM 在少量样本上出错,即将之前的硬间隔最大化条件放宽一点,为此引入软间隔(Soft Margin)的概念。
此时,我们就不再不将正则化参数 CCC 设置得过大,则优化目标就变成:
minθ,θ012∥θ∥2+C∑i=1nmax(0,1−y(i)(θTx(i)+θ0))\min_{\theta, \theta_0} \frac{1}{2} \|\theta\|^2 + C \sum_{i=1}^{n} \max \left( 0, 1 - y^{(i)} \left( \theta^T x^{(i)} + \theta_0 \right) \right)θ,θ0min21∥θ∥2+Ci=1∑nmax(0,1−y(i)(θTx(i)+θ0))
2、高斯核函数
对于非线性可分的数据,在之前的文章中我们知道可以引入高阶特征来进行多项式回归,而问题就是如何选择这些高阶特征?除了通过组合原有特征,在这里我们引入核函数 ϕ:x↦ϕ(x)\phi: x \mapsto \phi(x)ϕ:x↦ϕ(x),它将原来的 nnn 维向量映射为更高维的向量,使得这些向量在该更高维空间中线性可分。
在之前的例子中,可以认为是使用了线性核 ϕ(x)=x\phi(x) = xϕ(x)=x,即不做任何改变;而接下来我们将介绍一种最常用的核函数——高斯核(Gaussian Kernel):
ϕi(x)=Sim(x,l(i))=exp{−∥x−l(i)∥22σ2}\phi_{i}(x) = \text{Sim} \left( x, l^{(i)} \right) = \exp \left\{ -\frac{\left\| x - l^{(i)} \right\|^2}{2\sigma^2} \right\}ϕi(x)=Sim(x,l(i))=exp{−2σ2x−l(i)2}
其中 σ\sigmaσ 是可调节参数,l(i)l^{(i)}l(i) 被称为地标(Landmarks),是原始空间中选取的某些点,具有和样本特征 xxx 相同的维度。∥x−l(i)∥\left\| x - l^{(i)} \right\|x−l(i) 即为样本到地标的欧氏距离。
可以看出,高斯核与正态分布的形式有些许类似,绘制出函数图像:
显然,xxx 距离 l(i)l^{(i)}l(i) 越远时,分子越大,函数值越接近 000;距离越近时,分子越接近 000,函数值越接近 111;当 x=l(i)x = l^{(i)}x=l(i) 时,函数值取到最大值。因此,我们自然而然地想到可以取训练集中的样本点作为地标,有 mmm 个样本就取 mmm 个地标。这里不需要区分正负样本点,因为在核函数 ϕ(i)(x)\phi^{(i)}(x)ϕ(i)(x) 前面还有一个可学习的 θ(i)\theta^{(i)}θ(i),对于不同样本会自动区分。注意到 σ\sigmaσ 较大时,变化也较为平缓,模型可能会欠拟合,造成高偏差;反之则可能会造成过拟合。
假设我们原来用 x(i)={1,x1(i),x2(i),⋯,xn(i)}∈Rn+1x^{(i)} = \left\{ 1, x_1^{(i)}, x_2^{(i)}, \cdots, x_n^{(i)} \right\} \in \mathbb{R}^{n + 1}x(i)={1,x1(i),x2(i),⋯,xn(i)}∈Rn+1 来描述第 jjj 个样本的特征向量,则现在就是使用:
f(x(i))=[1ϕ1(x(i))ϕ2(x(i))⋮ϕi(x(i))⋮ϕm(x(i))]=[1Sim(x(i),l(1))Sim(x(i),l(2))⋮Sim(x(i),l(i))=1⋮Sim(x(i),l(m))]∈Rm+1f \left( x^{(i)} \right) = \begin{bmatrix} 1 \\ \phi_1 \left( x^{(i)} \right) \\ \phi_2 \left( x^{(i)} \right) \\ \vdots \\ \phi_i \left( x^{(i)} \right) \\ \vdots \\ \phi_m \left( x^{(i)} \right) \end{bmatrix} = \begin{bmatrix} 1 \\ \text{Sim} \left( x^{(i)}, l^{(1)} \right) \\ \text{Sim} \left( x^{(i)}, l^{(2)} \right) \\ \vdots \\ \text{Sim} \left( x^{(i)}, l^{(i)} \right) = 1 \\ \vdots \\ \text{Sim} \left( x^{(i)}, l^{(m)} \right) \end{bmatrix} \in \mathbb{R}^{m + 1}f(x(i))=1ϕ1(x(i))ϕ2(x(i))⋮ϕi(x(i))⋮ϕm(x(i))=1Sim(x(i),l(1))Sim(x(i),l(2))⋮Sim(x(i),l(i))=1⋮Sim(x(i),l(m))∈Rm+1
三、代码实现
在实际运用 SVM 时,我们通常会调用现成的机器学习第三方库,比较经典的就有 Python 中的 Scikit-learn,其调用支持向量分类器的函数如下:
sklearn.svm.SVC(C=1.0, kernel='rbf', gamma='auto', probability=False)
该函数的实现基于 libsvm,其中二次规划问题的解决算法是 SMO,通常需要调节的参数如下:
C
:正则化参数,取值越大,对松弛变量(误分类的代价函数)的惩罚越大,适用于训练集几乎可分的情况,此时训练集上的准确率较高,但泛化能力弱(过拟合)。kernel
:默认取rbf
径向基函数(高斯核),可选linear
线性核、poly
多项式核、sigmod
等。gamma
:核函数系数,默认为auto
,取样本特征数的倒数。probability
:是否启用概率估计,即最后的输出会包含属于每个类的概率值。
下面以 Coursera 上的数据集 ex6data1.mat
为例,这是一个线性可分的数据集:
使用如下代码:
import numpy as np
from scipy.io import loadmat
from sklearn import svm
import matplotlib.pyplot as plt# load data
data = loadmat('ex6data1.mat')
X = data['X'] # (51, 2)
y = data['y'].flatten() # (51, )# classifier define
clf = svm.SVC(C=1, kernel='linear')
clf.fit(X, y)# dicision boundary
x1, x2 = np.meshgrid(np.linspace(0, 4.5, 100), np.linspace(1.3, 4.5, 100))
f = np.concatenate((x1.reshape(10000, 1), x2.reshape(10000, 1)), axis = 1)
res = clf.decision_function(f).reshape((100,100))# draw
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('C=1')
plt.plot(X[y==1][:, 0], X[y==1][:, 1], '+', color='black')
plt.plot(X[y==0][:, 0], X[y==0][:, 1], 'o', color='gold')
plt.contour(x1, x2, res, [0])
plt.show()
训练结果如下:
此外还有一个非线性可分的数据集 ex6data2.mat
:
使用如下代码:
import numpy as np
from scipy.io import loadmat
from sklearn import svm
import matplotlib.pyplot as plt# load data
data = loadmat('ex6data2.mat')
X = data['X'] # (863, 2)
y = data['y'].flatten() # (863, )# classifier define
clf = svm.SVC(C=100, kernel='rbf', gamma=10, probability=True)
clf.fit(X, y)
print(clf.score(X, y))# dicision boundary
x1, x2 = np.meshgrid(np.linspace(0, 1, 100), np.linspace(0.38, 1, 100))
f = np.concatenate((x1.reshape(10000, 1), x2.reshape(10000, 1)), axis = 1)
res = clf.decision_function(f).reshape((100,100))# draw
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('C=100, gamma=10')
plt.plot(X[y==1][:, 0], X[y==1][:, 1], '+', color='black')
plt.plot(X[y==0][:, 0], X[y==0][:, 1], 'o', color='gold')
plt.contour(x1, x2, res, [0])
plt.show()
训练结果如下: