支持向量机
- 1. 基于最大间隔分隔数据
- 2. 寻找最大间隔
- 2.1分类器求解的优化问题:
- 2.2 拉格朗日乘子法
- 3. SMO 高效优化算法
- 3.1 Platt 的 SMO 算法
- 3.2 求解步骤
- 3.3 应用简化版 SMO 算法处理小规模数据集
- 4. 利用完整 Platt SMO 算法加速优化
- 4.1 完整版 Platt SMO 的支持函数
- 4.2 完整 Platt SMO 算法中的优化例程
- 5. 在复杂数据上应用核函数
- 5.1 利用核函数将数据映射到高维空间
- 5.2 径向基核函数
- 5.3 在测试中使用核函数
- Support Vector Machines
- 本文使用最流行的一种实现方法完成 SVM,该算法为序列最小优化(Sequential Minimal Optimization, SMO)算法
- 优点:泛化错误率低,计算开销不大,结果易解释
- 缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适用于处理二类问题
- 适用数据类型:数值型和标称型数据
1. 基于最大间隔分隔数据
- 超平面(hyperplane):对于二维数据,需要用一条直线来分隔,三维数据用一个平面来分隔,以此类推,N 维数据需要 N-1 维的对象来分隔,该对象为超平面
- 间隔(margin):间隔指离分隔超平面最近的点与分隔面的距离,该距离越大,分类器越好,或者说越健壮
- 支持向量(support vector):即离分隔超平面最近的那些点
2. 寻找最大间隔
- 假设分隔超平面为 w T x + b = 0 {\bf {w}}^T{\bf {x}} + b = 0 wTx+b=0,则点 A {\bf {A}} A 到该超平面的距离为 ∣ w T A + b ∣ ∣ ∣ w ∣ ∣ \frac{|{\bf {w}}^T{\bf {A}} + b|}{||{\bf {w}}||} ∣∣w∣∣∣wTA+b∣
2.1分类器求解的优化问题:
- 超平面首先要满足能够正确分类,即:
{ w T x i + b > 0 y i = 1 w T x i + b < 0 y i = − 1 \left\{ \begin{array}{l} {\bf {w}}^T{\bf {x_i}} + b>0\qquad y_i=1\\ {\bf {w}}^T{\bf {x_i}} + b<0\qquad y_i=-1 \end{array} \right. {wTxi+b>0yi=1wTxi+b<0yi=−1
- 其次,我们要找的超平面,应该是间隔区域的中轴线,假设支持向量与超平面的距离为 d,则:
{ w T x i + b ∣ ∣ w ∣ ∣ ≥ d ∀ y i = 1 w T x i + b ∣ ∣ w ∣ ∣ ≤ − d ∀ y i = − 1 \left\{ \begin{array}{l} \frac{{\bf {w}}^T{\bf {x_i}} + b}{||{\bf {w}}||} \geq d \qquad \forall y_i=1\\[2ex] \frac{{\bf {w}}^T{\bf {x_i}} + b}{||{\bf {w}}||} \leq -d \qquad \forall y_i=-1 \end{array} \right. ⎩⎨⎧∣∣w∣∣wTxi+b≥d∀yi=1∣∣w∣∣wTxi+b≤−d∀yi=−1
两边除以 d,得到:
{ w d T x i + b d ≥ 1 ∀ y i = 1 w d T x i + b d ≤ − 1 ∀ y i = − 1 \left\{ \begin{array}{l} {\bf {w}}_d^T{\bf {x_i}} + b_d \geq 1 \qquad \forall y_i=1\\[2ex] {\bf {w}}_d^T{\bf {x_i}} + b_d \leq -1 \qquad \forall y_i=-1 \end{array} \right. {wdTxi+bd≥1∀yi=1wdTxi+bd≤−1∀yi=−1
其中 w d T = w T ∣ ∣ w ∣ ∣ d , b d = b ∣ ∣ w ∣ ∣ d {\bf {w}}_d^T = \frac{{\bf {w}}^T}{||{\bf {w}}||d},\quad b_d = \frac{b}{||{\bf {w}}||d} wdT=∣∣w∣∣dwT,bd=∣∣w∣∣db
其实是在原 w {\bf {w}} w 和 b b b 的基础上除以了一个标量,因此 w d T x i + b d = 0 {\bf {w}}_d^T{\bf {x_i}} + b_d=0 wdTxi+bd=0 和 w T x i + b = 0 {\bf {w}}^T{\bf {x_i}} + b=0 wTxi+b=0 为同一个平面,因此可以直接表示为:
{ w T x i + b ≥ 1 ∀ y i = 1 w T x i + b ≤ − 1 ∀ y i = − 1 \left\{ \begin{array}{l} {\bf {w}}^T{\bf {x_i}} + b \geq 1 \qquad \forall y_i=1\\[2ex] {\bf {w}}^T{\bf {x_i}} + b \leq -1 \qquad \forall y_i=-1 \end{array} \right. {wTxi+b≥1∀yi=1wTxi+b≤−1∀yi=−1
- 假设我们的输出类别分别为 -1 和 +1,则上式可以表示为:
y i ( w T x i + b ) ≥ 1 ∀ x i y_i({\bf {w}}^T{\bf {x_i}} + b)\geq 1 \qquad \forall {\bf {x_i}} yi(wTxi+b)≥1∀xi
- 对于我们的支持向量(离超平面最近的点),有: ∣ w T x i + b ∣ = 1 |{\bf {w}}^T{\bf {x_i}} + b| = 1 ∣wTxi+b∣=1
所以我们要最大化的距离: d = 1 ∣ ∣ w ∣ ∣ d = \frac{1}{||{\bf {w}}||} d=∣∣w∣∣1
即求 ∣ ∣ w ∣ ∣ ||{\bf {w}}|| ∣∣w∣∣ 的最小值,为了方便求导,可等效于求
m i n 1 2 ∣ ∣ w ∣ ∣ 2 s . t y i ( w T x i + b ) ≥ 1 i = 1 , 2 , . . , n \begin{array}{l}min \frac{1}{2}||{\bf {w}}||^2\\[2ex] s.t\quad y_i({\bf {w}}^T{\bf {x_i}} + b)\geq 1 \quad i=1,2,..,n \end{array} min21∣∣w∣∣2s.tyi(wTxi+b)≥1i=1,2,..,n
以上即为支持向量机的基本数学模型
2.2 拉格朗日乘子法
- 上述问题为具有约束条件的最小化问题,且约束条件为不等式,对于这种问题,一般采用拉格朗日乘子法求解,其基本思想为构造一个函数,使得该函数在可行解区域内与原目标函数完全一致,而在可行解区域外的数值非常大,甚至是无穷大,这样的话就将其转换为与原问题等价的无约束条件最小化问题。构造的函数如下:
L ( x , α ) = f ( x ) + α g ( x ) L(x, \alpha) =f(x) + \alpha g(x) L(x,α)=f(x)+αg(x)
其中 f ( x ) f(x) f(x) 为原函数, g ( x ) g(x) g(x) 为满足约束条件的函数,约束条件为 g ( x ) ≤ 0 g(x)\leq 0 g(x)≤0,对于支持向量机的基本数学模型,构造函数如下:
L ( w , α , b ) = 1 2 ∣ ∣ w ∣ ∣ 2 − ∑ i = 1 n α i ( y i ( w T x i + b ) − 1 ) L({\bf {w}}, \alpha, b) =\frac{1}{2}||{\bf {w}}||^2 - \sum_{i=1}^n\alpha_i(y_i({\bf {w}}^T{\bf {x_i}} + b)-1) L(w,α,b)=21∣∣w∣∣2−i=1∑nαi(yi(wTxi+b)−1)
其中 α i ≥ 0 \alpha_i \geq 0 αi≥0。设:
θ ( w ) = m a x α i ≥ 0 L ( w , α , b ) \theta({\bf {w}}) = \mathop{max}\limits_{\alpha_i \geq 0} L({\bf {w}}, \alpha, b) θ(w)=αi≥0maxL(w,α,b)
当 y i ( w T x i + b ) ≥ 1 y_i({\bf {w}}^T{\bf {x_i}} + b)\geq 1 yi(wTxi+b)≥1 时,显然该函数等于原函数,当 y i ( w T x i + b ) ≤ 1 y_i({\bf {w}}^T{\bf {x_i}} + b)\leq 1 yi(wTxi+b)≤1 时,我们将 α \alpha α 设置为无穷大,那么该函数值也为无穷大,于是就满足了上面提到的转换为无约束最小化问题的条件。于是我们的问题转换为求解:
m i n w , b m a x α i ≥ 0 L ( w , α , b ) = p ∗ \mathop{min} \limits_{{\bf {w}},b}\mathop{max}\limits_{\alpha_i \geq 0} L({\bf {w}}, \alpha, b) = p* w,bminαi≥0maxL(w,α,b)=p∗
- 上式求解起来比较困难,因为首先要面对关于 w , b {\bf {w}},b w,b 的方程,而 α i \alpha_i αi 又是不等式约束。于是我们利用拉格朗日函数的对偶性,将最小和最大的位置交换一下:
m a x α i ≥ 0 m i n w , b L ( w , α , b ) = d ∗ \mathop{max}\limits_{\alpha_i \geq 0}\mathop{min} \limits_{{\bf {w}},b} L({\bf {w}}, \alpha, b) = d* αi≥0maxw,bminL(w,α,b)=d∗
满足 p ∗ = d ∗ p*=d* p∗=d∗ 的条件一是求取最小值的函数是凸函数(这一点很明显满足),二是满足 KKT 条件,全称是 Karush-Kuhn-Tucker 条件,它表示我们的最优值必须满足:
- 经过拉格朗日函数处理之后的新目标函数 L ( w , b , α ) L({\bf {w}},b,α) L(w,b,α) 对 w , b {\bf {w}},b w,b 求导为零;
- h ( x ) = 0 h(x) = 0 h(x)=0( h ( x ) h(x) h(x) 为等式约束,在我们的问题中显然满足该条件);
- α ∗ g ( x ) = 0 α*g(x) = 0 α∗g(x)=0;
针对第一个条件,对 w , b {\bf {w}},b w,b 分别求偏导:
∂ L ∂ w = 0 ⟺ w = ∑ i = 1 n α i y i x i \frac{\partial L}{\partial {\bf {w}}} = 0\iff{\bf {w}}=\sum_{i=1}^n\alpha_iy_i{\bf {x}}_i ∂w∂L=0⟺w=i=1∑nαiyixi
∂ L ∂ b = 0 ⟺ ∑ i = 1 n α i y i = 0 \frac{\partial L}{\partial b} = 0\iff\sum_{i=1}^n\alpha_iy_i=0 ∂b∂L=0⟺i=1∑nαiyi=0
代回原式得到:
L ( w , b , α ) = ∑ i = 1 n α i − 1 2 ∑ i , j = 1 n ( α i α j y i y j x i T x j ) L({\bf {w}},b,α) = \sum_{i=1}^n\alpha_i - \frac{1}{2}\sum_{i,j=1}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j) L(w,b,α)=i=1∑nαi−21i,j=1∑n(αiαjyiyjxiTxj)
于是上述问题转换为:
m a x α [ ∑ i = 1 n α i − 1 2 ∑ i , j = 1 n ( α i α j y i y j x i T x j ) ] \mathop{max}\limits_{\alpha}\left[\sum_{i=1}^n\alpha_i - \frac{1}{2}\sum_{i,j=1}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)\right] αmax[i=1∑nαi−21i,j=1∑n(αiαjyiyjxiTxj)] s . t . α i ≥ 0 , i = 1 , 2 , 3 , . . . , n s.t.\qquad \alpha_i\geq 0, i=1,2,3,...,n s.t.αi≥0,i=1,2,3,...,n ∑ i = 1 n α i ⋅ y i = 0 \sum_{i=1}^n\alpha_i\cdot y_i = 0 i=1∑nαi⋅yi=0
3. SMO 高效优化算法
3.1 Platt 的 SMO 算法
- 将大优化问题分解为多个小优化问题来求解小优化问题往往很容易求解,并且对它们进行顺序求解的结果与将它们作为整体来求解的结果完全一致的。在结果完全相同的同时,SMO算法的求解时间短很多。
- SMO算法的目标是求出一系列 α {\alpha} α 和 b,一旦求出了这些 α {\alpha} α,就很容易计算出权重向量 w {\bf {w}} w 并得到分隔超平面。
- SMO算法的工作原理是:每次循环中选择两个 α {\alpha} α 进行优化处理。一旦找到了一对合适的 α {\alpha} α ,那么就增大其中一个同时减小另一个。这里所谓的“合适”就是指两个 α {\alpha} α 必须符合以下两个条件,条件之一就是两个 α {\alpha} α 必须要在间隔边界之外,而且第二个条件则是这两个 α {\alpha} α 还没有进行过区间化处理或者不在边界上。
- 令 u = w T x + b = ∑ i = 1 n α i y i x i T x + b u={\bf {w}}^T{\bf {x}} + b = \sum_{i=1}^n\alpha_i y_i{\bf {x}}_i^T{\bf {x}}+b u=wTx+b=i=1∑nαiyixiTx+b
我们的目标函数:
m a x α [ ∑ i = 1 n α i − 1 2 ∑ i , j = 1 n ( α i α j y i y j x i T x j ) ] \mathop{max}\limits_{\alpha}\left[\sum_{i=1}^n\alpha_i - \frac{1}{2}\sum_{i,j=1}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)\right] αmax[i=1∑nαi−21i,j=1∑n(αiαjyiyjxiTxj)] s . t . α i ≥ 0 , i = 1 , 2 , 3 , . . . , n s.t.\qquad \alpha_i\geq 0, i=1,2,3,...,n s.t.αi≥0,i=1,2,3,...,n ∑ i = 1 n α i ⋅ y i = 0 \sum_{i=1}^n\alpha_i\cdot y_i = 0 i=1∑nαi⋅yi=0
变换为求最小值:
m i n α [ 1 2 ∑ i = 1 n ∑ j = 1 n ( α i α j y i y j x i T x j ) − ∑ i = 1 n α i ] \mathop{min}\limits_{\alpha}\left[\frac{1}{2}\sum_{i=1}^n\sum_{j=1}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j) - \sum_{i=1}^n\alpha_i\right] αmin[21i=1∑nj=1∑n(αiαjyiyjxiTxj)−i=1∑nαi] s . t . α i ≥ 0 , i = 1 , 2 , 3 , . . . , n s.t.\qquad \alpha_i\geq 0, i=1,2,3,...,n s.t.αi≥0,i=1,2,3,...,n ∑ i = 1 n α i ⋅ y i = 0 \sum_{i=1}^n\alpha_i\cdot y_i = 0 i=1∑nαi⋅yi=0
实际上我们的数据并非完全线性可分,所以可以引入一个松弛变量(slack variable),来允许有些数据点可以处于分隔面的错误一侧,此时的约束条件变为:
s . t . C ≥ α i ≥ 0 , i = 1 , 2 , 3 , . . . , n s.t.\qquad C \geq\alpha_i\geq 0, i=1,2,3,...,n s.t.C≥αi≥0,i=1,2,3,...,n ∑ i = 1 n α i ⋅ y i = 0 \sum_{i=1}^n\alpha_i\cdot y_i = 0 i=1∑nαi⋅yi=0
根据 KKT条件以及松弛变量定义(具体可参考知乎,尤其是第三个理解起来比较困难,个人任务该问答比较有帮助)可以得出其中 α i {\alpha}_i αi 取值的意义为:
α i = 0 ⟹ y i u i ≥ 1 ⟺ 该 点 在 边 界 内 部 或 边 界 上 \alpha_i = 0\implies y_iu_i\geq1 \iff 该点在边界内部或边界上 αi=0⟹yiui≥1⟺该点在边界内部或边界上 0 < α i < C ⟹ y i u i = 1 ⟺ 该 点 在 边 界 上 0< \alpha_i < C\implies y_iu_i = 1 \iff 该点在边界上 0<αi<C⟹yiui=1⟺该点在边界上 α i = C ⟹ y i u i ≤ 1 ⟺ 该 点 在 边 界 外 或 边 界 上 ( 允 许 错 分 的 样 本 点 ) \alpha_i = C\implies y_iu_i \leq 1 \iff 该点在边界外或边界上(允许错分的样本点) αi=C⟹yiui≤1⟺该点在边界外或边界上(允许错分的样本点)
当 α i {\alpha}_i αi 不满足上面的条件时,需要更新 α i {\alpha}_i αi,与此同时它还应满足第二个约束条件 ∑ i = 1 n α i ⋅ y i = 0 \sum_{i=1}^n\alpha_i\cdot y_i = 0 ∑i=1nαi⋅yi=0,因此我们同时更新两个 α {\alpha} α 值,因为只有成对更新,才能保证更新之后的值仍然满足和为 0 的约束,假设我们选择的两个乘子为 α 1 {\alpha}_1 α1 和 α 2 {\alpha}_2 α2:
α 1 n e w y 1 + α 2 n e w y 2 = α 1 o l d y 1 + α 2 o l d y 2 = ζ \alpha_1^{new}y_1 + \alpha_2^{new}y_2 = \alpha_1^{old}y_1 + \alpha_2^{old}y_2 = \zeta α1newy1+α2newy2=α1oldy1+α2oldy2=ζ
其中 ζ \zeta ζ 为常数,假设: L ≤ α 2 n e w ≤ H L\leq \alpha_2^{new}\leq H L≤α2new≤H
由: C ≥ α i ≥ 0 , i = 1 , 2 , 3 , . . . , n C \geq\alpha_i\geq 0, i=1,2,3,...,n C≥αi≥0,i=1,2,3,...,n α 1 n e w y 1 + α 2 n e w y 2 = α 1 o l d y 1 + α 2 o l d y 2 = ζ \alpha_1^{new}y_1 + \alpha_2^{new}y_2 = \alpha_1^{old}y_1 + \alpha_2^{old}y_2 = \zeta α1newy1+α2newy2=α1oldy1+α2oldy2=ζ
当 y 1 y_1 y1 和 y 2 y_2 y2 不同时: α 1 n e w − α 2 n e w = α 1 o l d − α 2 o l d = ζ \alpha_1^{new} - \alpha_2^{new} = \alpha_1^{old} - \alpha_2^{old} = \zeta α1new−α2new=α1old−α2old=ζ
当 y 1 y_1 y1 和 y 2 y_2 y2 相同时: α 1 n e w + α 2 n e w = α 1 o l d + α 2 o l d = ζ \alpha_1^{new} + \alpha_2^{new} = \alpha_1^{old} + \alpha_2^{old} = \zeta α1new+α2new=α1old+α2old=ζ
得到 { H = m a x ( 0 , − ζ ) , L = m i n ( C , C − ζ ) y 1 ≠ y 2 H = m a x ( 0 , ζ − C ) , L = m i n ( C , ζ ) y 1 = y 2 \left\{\begin{array}{l}H = max(0, -\zeta), \; L = min(C, C-\zeta)\qquad y_1\neq y_2\\H = max(0, \zeta - C), \; L = min(C, \zeta)\qquad y_1= y_2 \end{array}\right. {H=max(0,−ζ),L=min(C,C−ζ)y1=y2H=max(0,ζ−C),L=min(C,ζ)y1=y2
这个界限就是编程的时候需要用到的。已经确定了边界,接下来,就是推导迭代式,用于更新 α值。
对于目标函数,将 α 1 {\alpha}_1 α1 和 α 2 {\alpha}_2 α2 提取出来:
W ( α 2 ) = ∑ i = 1 n α i − 1 2 ∑ i = 1 n ∑ j = 1 n ( α i α j y i y j x i T x j ) W({\alpha}_2) = \sum_{i=1}^n\alpha_i - \frac{1}{2}\sum_{i=1}^n\sum_{j=1}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j) W(α2)=i=1∑nαi−21i=1∑nj=1∑n(αiαjyiyjxiTxj) = α 1 + α 2 + ∑ i = 3 n α i − 1 2 ∑ i = 1 n [ ∑ j = 1 2 ( α i α j y i y j x i T x j ) + ∑ j = 3 n ( α i α j y i y j x i T x j ) ] = \alpha_1+\alpha_2+\sum_{i=3}^n\alpha_i - \frac{1}{2}\sum_{i=1}^n\left[\sum_{j=1}^2(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)+\sum_{j=3}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)\right] =α1+α2+i=3∑nαi−21i=1∑n[j=1∑2(αiαjyiyjxiTxj)+j=3∑n(αiαjyiyjxiTxj)] = α 1 + α 2 + ∑ i = 3 n α i − 1 2 ∑ i = 1 2 [ ∑ j = 1 2 ( α i α j y i y j x i T x j ) + ∑ j = 3 n ( α i α j y i y j x i T x j ) ] =\alpha_1+\alpha_2+\sum_{i=3}^n\alpha_i - \frac{1}{2}\sum_{i=1}^2\left[\sum_{j=1}^2(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)+\sum_{j=3}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)\right] =α1+α2+i=3∑nαi−21i=1∑2[j=1∑2(αiαjyiyjxiTxj)+j=3∑n(αiαjyiyjxiTxj)] − 1 2 ∑ i = 3 n [ ∑ j = 1 2 ( α i α j y i y j x i T x j ) + ∑ j = 3 n ( α i α j y i y j x i T x j ) ] - \frac{1}{2}\sum_{i=3}^n\left[\sum_{j=1}^2(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)+\sum_{j=3}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)\right] −21i=3∑n[j=1∑2(αiαjyiyjxiTxj)+j=3∑n(αiαjyiyjxiTxj)] = α 1 + α 2 + ∑ i = 3 n α i − 1 2 ∑ i = 1 2 ∑ j = 1 2 ( α i α j y i y j x i T x j ) − ∑ i = 1 2 ∑ j = 3 n ( α i α j y i y j x i T x j ) =\alpha_1+\alpha_2+\sum_{i=3}^n\alpha_i - \frac{1}{2}\sum_{i=1}^2\sum_{j=1}^2(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j)-\sum_{i=1}^2\sum_{j=3}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j) =α1+α2+i=3∑nαi−21i=1∑2j=1∑2(αiαjyiyjxiTxj)−i=1∑2j=3∑n(αiαjyiyjxiTxj) − 1 2 ∑ i = 3 n ∑ j = 3 n ( α i α j y i y j x i T x j ) - \frac{1}{2}\sum_{i=3}^n\sum_{j=3}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j) −21i=3∑nj=3∑n(αiαjyiyjxiTxj) = α 1 + α 2 − 1 2 ( α 1 2 x 1 T x 1 ) − 1 2 ( α 2 2 x 2 T x 2 ) − α 1 α 2 y 1 y 2 x 1 T x 2 − α 1 y 1 ∑ j = 3 n ( α j y j x 1 T x j ) =\alpha_1+\alpha_2 - \frac{1}{2}(\alpha_1^2{\bf {x}}_1^T{\bf {x}}_1) - \frac{1}{2}(\alpha_2^2{\bf {x}}_2^T{\bf {x}}_2) - \alpha_1\alpha_2y_1y_2{\bf {x}}_1^T{\bf {x}}_2 - \alpha_1y_1\sum_{j=3}^n(\alpha_j y_j {\bf {x}}_1^T{\bf {x}}_j) =α1+α2−21(α12x1Tx1)−21(α22x2Tx2)−α1α2y1y2x1Tx2−α1y1j=3∑n(αjyjx1Txj) − α 2 y 2 ∑ j = 3 n ( α j y j x 2 T x j ) − ∑ i = 3 n α i − 1 2 ∑ i = 3 n ∑ j = 3 n ( α i α j y i y j x i T x j ) - \alpha_2y_2\sum_{j=3}^n(\alpha_j y_j {\bf {x}}_2^T{\bf {x}}_j) - \sum_{i=3}^n\alpha_i - \frac{1}{2}\sum_{i=3}^n\sum_{j=3}^n(\alpha_i \alpha_j y_i y_j {\bf {x}}_i^T{\bf {x}}_j) −α2y2j=3∑n(αjyjx2Txj)−i=3∑nαi−21i=3∑nj=3∑n(αiαjyiyjxiTxj)
定义:
f ( x i ) = ∑ j = 1 n α j y j x i T x j + b f({\bf {x}}_i) = \sum_{j=1}^n\alpha_jy_j{\bf {x}}_i^T{\bf {x}}_j+b f(xi)=j=1∑nαjyjxiTxj+b v i = ∑ j = 3 n α j y j x i T x j = f ( x i ) − ∑ j = 1 2 α j y j x i T x j − b v_i = \sum_{j=3}^n\alpha_jy_j{\bf {x}}_i^T{\bf {x}}_j=f({\bf {x}}_i) - \sum_{j=1}^2\alpha_jy_j{\bf {x}}_i^T{\bf {x}}_j-b vi=j=3∑nαjyjxiTxj=f(xi)−j=1∑2αjyjxiTxj−b
则目标函数为:
W ( α 2 ) = α 1 + α 2 − 1 2 ( α 1 2 x 1 T x 1 ) − 1 2 ( α 2 2 x 2 T x 2 ) − α 1 α 2 y 1 y 2 x 1 T x 2 − α 1 y 1 v 1 − α 2 y 2 v 2 + c o n s t a n t W({\alpha}_2) = \alpha_1+\alpha_2 - \frac{1}{2}(\alpha_1^2{\bf {x}}_1^T{\bf {x}}_1) - \frac{1}{2}(\alpha_2^2{\bf {x}}_2^T{\bf {x}}_2) - \alpha_1\alpha_2y_1y_2{\bf {x}}_1^T{\bf {x}}_2 - \alpha_1y_1v_1- \alpha_2y_2v_2 +constant W(α2)=α1+α2−21(α12x1Tx1)−21(α22x2Tx2)−α1α2y1y2x1Tx2−α1y1v1−α2y2v2+constant
由于 ∑ i = 1 n α i ⋅ y i = 0 \sum_{i=1}^n\alpha_i\cdot y_i = 0 i=1∑nαi⋅yi=0
所以 α 1 y 1 + α 2 y 2 = − ∑ i = 3 n α i y i = B \alpha_1y_1+\alpha_2y_2 = -\sum_{i=3}^n\alpha_iy_i = B α1y1+α2y2=−i=3∑nαiyi=B同时乘以 y 1 y_1 y1:
α 1 = B y 1 − y 1 y 2 α 2 = γ − s α 2 \alpha_1 = By_1-y_1y_2\alpha_2=\gamma-s\alpha_2 α1=By1−y1y2α2=γ−sα2
代入目标函数:
W ( α 2 ) = γ − s α 2 + α 2 − 1 2 ( γ − s α 2 ) 2 x 1 T x 1 − 1 2 ( α 2 2 x 2 T x 2 ) − ( γ − s α 2 ) α 2 s x 1 T x 2 W({\alpha}_2) = \gamma-s\alpha_2+\alpha_2 - \frac{1}{2}(\gamma-s\alpha_2)^2{\bf {x}}_1^T{\bf {x}}_1 - \frac{1}{2}(\alpha_2^2{\bf {x}}_2^T{\bf {x}}_2)- (\gamma-s\alpha_2)\alpha_2s{\bf {x}}_1^T{\bf {x}}_2 W(α2)=γ−sα2+α2−21(γ−sα2)2x1Tx1−21(α22x2Tx2)−(γ−sα2)α2sx1Tx2 − ( γ − s α 2 ) y 1 v 1 − α 2 y 2 v 2 + c o n s t a n t - (\gamma-s\alpha_2)y_1v_1- \alpha_2y_2v_2 +constant −(γ−sα2)y1v1−α2y2v2+constant
对 α 2 \alpha_2 α2求导:
∂ W ( α 2 ) α 2 = − s + 1 + γ s x 1 T x 1 − α 2 x 1 T x 1 − α 2 x 2 T x 2 − γ s x 1 T x 2 + 2 α 2 x 1 T x 2 + y 2 v 1 − y 2 v 2 = 0 \frac{\partial W(\alpha_2)}{\alpha_2} = -s+1+\gamma s{\bf {x}}_1^T{\bf {x}}_1-\alpha_2{\bf {x}}_1^T{\bf {x}}_1- \alpha_2{\bf {x}}_2^T{\bf {x}}_2 -\gamma s {\bf {x}}_1^T{\bf {x}}_2+2\alpha_2{\bf {x}}_1^T{\bf {x}}_2+y_2v_1-y_2v_2=0 α2∂W(α2)=−s+1+γsx1Tx1−α2x1Tx1−α2x2Tx2−γsx1Tx2+2α2x1Tx2+y2v1−y2v2=0
将 s = y 1 y 2 s=y_1y_2 s=y1y2 代入上式,得到:
α 2 n e w = y 2 [ y 2 − y 1 + γ y 1 ( x 1 T x 1 − x 1 T x 2 ) + v 1 − v 2 ] x 1 T x 1 + x 2 T x 2 − 2 x 1 T x 2 \alpha_2^{new} = \frac{y_2[y_2-y_1+\gamma y_1({\bf {x}}_1^T{\bf {x}}_1-{\bf {x}}_1^T{\bf {x}}_2)+v_1-v_2]}{{\bf {x}}_1^T{\bf {x}}_1+{\bf {x}}_2^T{\bf {x}}_2-2{\bf {x}}_1^T{\bf {x}}_2} α2new=x1Tx1+x2Tx2−2x1Tx2y2[y2−y1+γy1(x1Tx1−x1Tx2)+v1−v2]
令:
E i = f ( x i ) − y i E_i = f(x_i) - y_i Ei=f(xi)−yi η = x 1 T x 1 + x 2 T x 2 − 2 x 1 T x 2 \eta = {\bf {x}}_1^T{\bf {x}}_1+{\bf {x}}_2^T{\bf {x}}_2-2{\bf {x}}_1^T{\bf {x}}_2 η=x1Tx1+x2Tx2−2x1Tx2
E i E_i Ei 为误差项, η \eta η 为学习速率
已知:
γ = α 1 o l d + s α 2 o l d \gamma = \alpha_1^{old} + s\alpha_2^{old} γ=α1old+sα2old v i = ∑ j = 3 n α j y j x i T x j = f ( x i ) − ∑ j = 1 2 α j y j x i T x j − b v_i = \sum_{j=3}^n\alpha_jy_j{\bf {x}}_i^T{\bf {x}}_j=f({\bf {x}}_i) - \sum_{j=1}^2\alpha_jy_j{\bf {x}}_i^T{\bf {x}}_j-b vi=j=3∑nαjyjxiTxj=f(xi)−j=1∑2αjyjxiTxj−b
代入化简最终得到:
α 2 n e w = α 2 o l d + y 2 ( E 1 − E 2 ) η \alpha_2^{new} = \alpha_2^{old}+\frac{y_2(E_1-E_2)}{\eta} α2new=α2old+ηy2(E1−E2)
根据之前推导的 α 2 \alpha_2 α2 的范围,得到修剪后的 α 2 \alpha_2 α2:
α 2 n e w , c l i p p e d = { H , α 2 n e w > H α 2 n e w , L ≤ α 2 n e w ≤ H L , α 2 n e w < L \alpha_2^{new,clipped} = \left\{\begin{array}{l} H,\;\alpha_2^{new} > H\\[2ex] \alpha_2^{new},\;L\leq\alpha_2^{new} \leq H\\[2ex] L,\;\alpha_2^{new} α2new,clipped=⎩⎪⎪⎪⎨⎪⎪⎪⎧H,α2new>Hα2new,L≤α2new≤HL,α2new<L
对于 α 1 \alpha_1 α1,由 α 1 \alpha_1 α1 和 α 2 \alpha_2 α2 的关系,可以得到:
α 1 n e w = α 1 o l d + y 1 y 2 ( α 2 o l d − α 2 n e w , c l i p p e d ) \alpha_1^{new} = \alpha_1^{old} + y_1y_2(\alpha_2^{old} - \alpha_2^{new,clipped}) α1new=α1old+y1y2(α2old−α2new,clipped)
由此我们得到了 α \alpha α 的求解公式,接下来看常数 b,我们要根据 α \alpha α 的取值范围,去更正 b 的值,使间隔最大化。当 α 1 n e w \alpha_1^{new} α1new 在 0 和 C 之间的时候,根据 KKT 条件可知,这个点是支持向量上的点。因此,满足下列公式:
y 1 ( w T x 1 + b ) = 1 y_1({\bf {w}}^T{\bf {x}}_1+b)=1 y1(wTx1+b)=1
公式两边同时乘以y1得(y1y1=1):
w T x 1 + b = y 1 {\bf {w}}^T{\bf {x}}_1+b=y_1 wTx1+b=y1即 ∑ i = 1 n α i y i x i T x 1 + b = y 1 \sum_{i=1}^{n}\alpha_iy_i{\bf {x}}_i^T{\bf {x}}_1 + b = y_1 i=1∑nαiyixiTx1+b=y1 ⟹ b 1 n e w = y 1 − α 1 n e w y 1 x 1 T x 1 − α 2 n e w y 2 x 2 T x 1 − ∑ i = 3 n α i y i x i T x 1 \implies b_1^{new}=y_1 - \alpha_1^{new}y_1{\bf {x}}_1^T{\bf {x}}_1-\alpha_2^{new}y_2{\bf {x}}_2^T{\bf {x}}_1-\sum_{i=3}^{n}\alpha_iy_i{\bf {x}}_i^T{\bf {x}}_1 ⟹b1new=y1−α1newy1x1Tx1−α2newy2x2Tx1−i=3∑nαiyixiTx1 = − E 1 + α 1 o l d y 1 x 1 T x 1 + α 2 o l d y 1 x 2 T x 1 + b o l d − α 1 n e w y 1 x 1 T x 1 − α 2 n e w y 2 x 2 T x 1 =-E_1+ \alpha_1^{old}y_1{\bf {x}}_1^T{\bf {x}}_1+ \alpha_2^{old}y_1{\bf {x}}_2^T{\bf {x}}_1 +b^{old}- \alpha_1^{new}y_1{\bf {x}}_1^T{\bf {x}}_1-\alpha_2^{new}y_2{\bf {x}}_2^T{\bf {x}}_1 =−E1+α1oldy1x1Tx1+α2oldy1x2Tx1+bold−α1newy1x1Tx1−α2newy2x2Tx1 = b o l d − E 1 + y 1 x 1 T x 1 ( α 1 o l d − α 1 n e w ) + y 2 x 2 T x 1 ( α 2 o l d − α 2 n e w ) =b^{old}-E_1+y_1{\bf {x}}_1^T{\bf {x}}_1(\alpha_1^{old}-\alpha_1^{new})+y_2{\bf {x}}_2^T{\bf {x}}_1(\alpha_2^{old}-\alpha_2^{new}) =bold−E1+y1x1Tx1(α1old−α1new)+y2x2Tx1(α2old−α2new)
同理可得:
b 2 n e w = b o l d − E 2 + y 1 x 1 T x 2 ( α 1 o l d − α 1 n e w ) + y 2 x 2 T x 2 ( α 2 o l d − α 2 n e w ) b_2^{new}=b^{old}-E_2+y_1{\bf {x}}_1^T{\bf {x}}_2(\alpha_1^{old}-\alpha_1^{new})+y_2{\bf {x}}_2^T{\bf {x}}_2(\alpha_2^{old}-\alpha_2^{new}) b2new=bold−E2+y1x1Tx2(α1old−α1new)+y2x2Tx2(α2old−α2new)
当 b 1 b_1 b1 和 b 2 b_2 b2 都有效的时候,它们是相等的,即:
b n e w = b 1 n e w = b 2 n e w b^{new} = b_1^{new} = b_2^{new} bnew=b1new=b2new
当两个乘子都在边界上,则 b b b 阈值和 KKT 条件一致。当不满足的时候,SMO 算法选择他们的中点作为新的阈值:
b = { b 1 , 0 < α 1 n e w < C b 2 , 0 < α 2 n e w < C b 1 + b 2 2 , o t h e r w i s e b=\left\{\begin{array}{l} b_1,\; 0<\alpha_1^{new}b=⎩⎪⎪⎪⎨⎪⎪⎪⎧b1,0<α1new<Cb2,0<α2new<C2b1+b2,otherwise
3.2 求解步骤
- 计算误差: E i = f ( x i ) − y i = ∑ j = 1 n α j y j x i T x j + b − y i E_i =f({\bf {x}}_i)-y_i = \sum_{j=1}^n\alpha_jy_j{\bf {x}}_i^T{\bf {x}}_j+b - y_i Ei=f(xi)−yi=j=1∑nαjyjxiTxj+b−yi
- 计算上下界 L L L 和 H H H: { H = m a x ( 0 , α j o l d − α i o l d ) , L = m i n ( C , C + α j o l d − α i o l d ) y 1 ≠ y 2 H = m a x ( 0 , α j o l d + α i o l d − C ) , L = m i n ( C , α j o l d + α i o l d ) y 1 = y 2 \left\{\begin{array}{l}H = max(0, \alpha_j^{old}-\alpha_i^{old}), \; L = min(C, C+\alpha_j^{old}-\alpha_i^{old})\qquad y_1\neq y_2\\H = max(0, \alpha_j^{old}+\alpha_i^{old} - C), \; L = min(C, \alpha_j^{old}+\alpha_i^{old})\qquad y_1= y_2 \end{array}\right. {H=max(0,αjold−αiold),L=min(C,C+αjold−αiold)y1=y2H=max(0,αjold+αiold−C),L=min(C,αjold+αiold)y1=y2
- 计算 η \eta η: η = x i T x i + x j T x j − 2 x i T x j \eta = {\bf {x}}_i^T{\bf {x}}_i+{\bf {x}}_j^T{\bf {x}}_j-2{\bf {x}}_i^T{\bf {x}}_j η=xiTxi+xjTxj−2xiTxj
- 更新 α j \alpha_j αj: α j n e w , c l i p p e d = { H , α j n e w > H α j o l d + y j ( E i − E j ) η , L ≤ α j n e w ≤ H L , α j n e w < L \alpha_j^{new,clipped} = \left\{\begin{array}{l} H,\;\alpha_j^{new} > H\\[2ex] \alpha_j^{old}+\frac{y_j(E_i-E_j)}{\eta},\;L\leq\alpha_j^{new} \leq H\\[2ex] L,\;\alpha_j^{new} αjnew,clipped=⎩⎪⎪⎪⎨⎪⎪⎪⎧H,αjnew>Hαjold+ηyj(Ei−Ej),L≤αjnew≤HL,αjnew<L
- 更新 α i \alpha_i αi: α i n e w = α i o l d + y i y j ( α j o l d − α j n e w , c l i p p e d ) \alpha_i^{new} = \alpha_i^{old} + y_iy_j(\alpha_j^{old} - \alpha_j^{new,clipped}) αinew=αiold+yiyj(αjold−αjnew,clipped)
- 更新 b 1 b_1 b1 和 b 2 b_2 b2: b 1 n e w = b o l d − E i + y i x i T x i ( α i o l d − α i n e w ) + y j x j T x i ( α j o l d − α j n e w ) b_1^{new}=b^{old}-E_i+y_i{\bf {x}}_i^T{\bf {x}}_i(\alpha_i^{old}-\alpha_i^{new})+y_j{\bf {x}}_j^T{\bf {x}}_i(\alpha_j^{old}-\alpha_j^{new}) b1new=bold−Ei+yixiTxi(αiold−αinew)+yjxjTxi(αjold−αjnew) b 2 n e w = b o l d − E j + y i x i T x j ( α i o l d − α i n e w ) + y j x j T x j ( α j o l d − α j n e w ) b_2^{new}=b^{old}-E_j+y_i{\bf {x}}_i^T{\bf {x}}_j(\alpha_i^{old}-\alpha_i^{new})+y_j{\bf {x}}_j^T{\bf {x}}_j(\alpha_j^{old}-\alpha_j^{new}) b2new=bold−Ej+yixiTxj(αiold−αinew)+yjxjTxj(αjold−αjnew)
- 根据 b 1 b_1 b1 和 b 2 b_2 b2 更新 b b b: b = { b 1 , 0 < α 1 n e w < C b 2 , 0 < α 2 n e w < C b 1 + b 2 2 , o t h e r w i s e b=\left\{\begin{array}{l} b_1,\; 0<\alpha_1^{new}b=⎩⎪⎪⎪⎨⎪⎪⎪⎧b1,0<α1new<Cb2,0<α2new<C2b1+b2,otherwise
以上推导过程转载自大神的文章:Python3《机器学习实战》学习笔记(八):支持向量机原理篇之手撕线性SVM
3.3 应用简化版 SMO 算法处理小规模数据集
'''SMO 算法中的辅助函数'''
import numpy as np
def loadDataSet(filename):
dataMat = []
labelMat = []
with open(filename) as f:
for line in f.readlines():
lineArr = line.strip().split('\t')
dataMat.append([float(lineArr[0]), float(lineArr[1])])
labelMat.append(float(lineArr[2]))
return dataMat, labelMat
def selectJrand(i, m):
'''
i: 第一个 alpha 的下标
m: 所有 alpha 的数目
'''
j = i
while (j==i):
j = int(np.random.uniform(0, m))
return j
def clipAlpha(aj, H, L):
'''
用于调整 alpha 值,使其小于 H,且大于 L
'''
if aj > H:
aj = H
if L > aj:
aj = L
return aj
'''简化 SMO 算法'''
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
'''
dataMatIn: 数据集
classLabels: 类别标签
C: 常数
toler: 松弛变量
maxIter: 最大循环次数
'''
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).T
b = 0
m, n = np.shape(dataMatrix)
alphas = np.mat(np.zeros((m, 1)))
iternum = 0
while (iternum < maxIter):
alphaPairsChanged = 0
for i in range(m):
y_i = float(np.multiply(alphas, labelMat).T * dataMatrix * dataMatrix[i, :].T) + b
e_i = y_i - float(labelMat[i])
if ((labelMat[i]*e_i < -toler) and (alphas[i] < C)) or ((labelMat[i]*e_i > toler) and (alphas[i] > 0)):
j = selectJrand(i, m)
y_j = float(np.multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[j, :].T)) + b
e_j = y_j - float(labelMat[j])
alpha_i_old = alphas[i].copy()
alpha_j_old = alphas[j].copy()
if (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
eta = 2.0 * dataMatrix[i,:] * dataMatrix[j,:].T - \
dataMatrix[i,:] * dataMatrix[i,:].T - dataMatrix[j,:] * dataMatrix[j,:].T
if eta >= 0:
print('eta>=0')
continue
alphas[j] -= labelMat[j] * (e_i - e_j) / eta
alphas[j] = clipAlpha(alphas[j], H, L)
if (abs(alphas[j] - alpha_j_old) < 0.00001):
print('j not moving enough')
continue
alphas[i] += labelMat[i] * labelMat[j] * (alpha_j_old - alphas[j])
b1 = b - e_i - labelMat[i] * (alphas[i] - alpha_i_old) * dataMatrix[i,:] * dataMatrix[i,:].T -\
labelMat[j] * (alphas[j] - alpha_j_old) * dataMatrix[i,:] * dataMatrix[j,:].T
b2 = b - e_j - labelMat[i] * (alphas[i] - alpha_i_old) * dataMatrix[i,:] * dataMatrix[j,:].T -\
labelMat[j] * (alphas[j] - alpha_j_old) * dataMatrix[j,:] * dataMatrix[j,:].T
if (0 < alphas[i]) and (C > alphas[i]):
b = b1
elif (0 < alphas[j]) and (C > alphas[j]):
b = b2
else:
b = (b1 + b2) / 2.0
alphaPairsChanged += 1
print('iter: %d i: %d, pairs changed %d' % (iternum, i, alphaPairsChanged))
if (alphaPairsChanged == 0):
iternum += 1
else:
iternum = 0
print('iteration number: %d' % iternum)
return b, alphas
[In]: dataMat, labelMat = loadDataSet('Ch06/testSet.txt')
[In]: b, alphas = smoSimple(dataMat, labelMat, 0.6, 0.001, 40)
[In]: b
[Out]: matrix([[-3.73041522]])
[In]: alphas[alphas>0]
[Out]: matrix([[0.12876353, 0.23408636, 0.36284989]])
'''计算 w'''
def get_w(dataMat, labelMat, alphas):
dataMat, labelMat = np.array(dataMat), np.array(labelMat)
w = np.multiply(alphas,(labelMat.reshape(100,1))).T * dataMat
return w
[In]: w = get_w(dataMat, labelMat, alphas)
[Out]: matrix([[ 0.7972711 , -0.28038632]])
划分结果如下图所示:
4. 利用完整 Platt SMO 算法加速优化
- 完整的 Platt SMO 算法在速度上会优于简化版
- 两者唯一的不同点在于 alpha 的选择方式
- Platt SMO 算法是通过一个外循环来选择第一个 alpha 值的,并且其选择过程会在两种方式之间进行交替:一种方式是在所有数据集上进行单遍扫描,另一种方式则是在非边界 alpha (不等于 0 或 C)中实现单遍扫描。
- 在选择第一个 alpha 值后,算法会通过一个内循环来选择第二个 alpha 值。在优化过程中,会通过最大化步长的方式来获得第二个 alpha 值。根据 alpha 的更新公式,我们选择使得 Ei-Ej 最大的 alpha 值。
4.1 完整版 Platt SMO 的支持函数
- 构建一个仅包含 init 方法的 optStruct 类,将其作为一个数据结构来使用,方便我们对于重要数据的维护
class optStruct:
def __init__(self, dataMatIn, classLabels, C, toler):
self.X = dataMatIn
self.labelMat = classLabels
self.C = C
self.tol = toler
self.m = np.shape(dataMatIn)[0]
self.alphas = np.mat(np.zeros((self.m, 1)))
self.b = 0
self.eCache = np.mat(np.zeros((self.m, 2)))
- 对于给定的 alpha 值,计算 E 值并返回
'''给定 alpha,计算 E'''
def calcEk(oS, k):
yk = float(np.multiply(oS.alphas, oS.labelMat).T * oS.X * oS.X[k, :].T) + oS.b
Ek = yk - float(oS.labelMat[k])
return Ek
- 使用启发式方法选择第二个 alpha
'''使用启发式方法选择第二个 alpha'''
def selectJ(i, oS, Ei):
maxK = -1
maxDeltaE = 0
Ej = 0
os.eCache[i] = [1, Ei]
validEcacheList = np.nonzero(oS.eCache[:,0].A)[0]
if len(validEcacheList) > 1:
for k in validEcacheList:
if k == i:
continue
Ek = calcEk(oS, k)
deltaE = abs(Ei - Ek)
if (deltaE > maxDeltaE):
maxK = k
maxDeltaE = deltaE
Ej = Ek
return maxK, Ej
else:
j = selectJrand(i, oS.m)
Ej = calcEk(oS, j)
return j, Ej
- 计算误差值并存入缓存,在对 alpha 进行优化后会用到该值
'''计算误差值并存入缓存'''
def updateEk(oS, k):
Ek = calcEk(oS, k)
oS.eCache[k] = [1, Ek]
4.2 完整 Platt SMO 算法中的优化例程
- 内循环
def innerL(i, oS):
Ei = calcEk(oS, i)
if ((oS.labelMat[i] * Ei < -oS.tol) and (oS.alphas[i] < oS.C)) or ((oS.labelMat[i]*Ei > oS.tol) and (oS.alphas[i] > 0)):
j, Ej = selectJ(i, oS, Ei)
alpha_i_old = oS.alphas[i].copy()
alpha_j_old = oS.alphas[j].copy()
if (oS.labelMat[i] != oS.labelMat[j]):
L = max(0, oS.alphas[j] - oS.alphas[i])
H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
else:
L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)
H = min(oS.C, oS.alphas[j] + oS.alphas[i])
if L == H:
print('L==H')
return 0
eta = 2.0 * oS.X[i,:] * oS.X[j,:].T - \
oS.X[i,:] * oS.X[i,:].T - oS.X[j,:] * oS.X[j,:].T
if eta >= 0:
print('eta>=0')
return 0
oS.alphas[j] -= oS.labelMat[j] * (Ei - Ej) / eta
oS.alphas[j] = clipAlpha(oS.alphas[j], H, L)
updateEk(oS, j)
if (abs(oS.alphas[j] - alpha_j_old) < 0.00001):
print('j not moving enough')
return 0
oS.alphas[i] += oS.labelMat[i] * oS.labelMat[j] * (alpha_j_old - oS.alphas[j])
updateEk(oS, i)
b1 = oS.b - Ei - oS.labelMat[i] * (oS.alphas[i] - alpha_i_old) * oS.X[i,:] * oS.X[i,:].T -\
oS.labelMat[j] * (oS.alphas[j] - alpha_j_old) * oS.X[i,:] * oS.X[j,:].T
b2 = oS.b - Ej - oS.labelMat[i] * (oS.alphas[i] - alpha_i_old) * oS.X[i,:] * oS.X[j,:].T -\
oS.labelMat[j] * (oS.alphas[j] - alpha_j_old) * oS.X[j,:] * oS.X[j,:].T
if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]):
oS.b = b1
elif (0 < oS.alphas[j]) and (oS.C > oS.alphas[j]):
oS.b = b2
else:
oS.b = (b1 + b2) / 2.0
return 1
else:
return 0
- 外循环
'''完整版 Platt SMO 的外循环代码'''
def smoP(dataMatIn, classLabels, C, toler, maxIter, kTup=('lin', 0)):
oS = optStruct(np.mat(dataMatIn), np.mat(classLabels).T, C, toler)
iter_num = 0
entireSet = True
alphaPairsChanged = 0
while (iter_num < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
alphaPairsChanged = 0
if entireSet:
for i in range(oS.m):
alphaPairsChanged += innerL(i, oS)
print('fullSet, iter: %d i: %d, pairs changed %d' % (iter_num, i, alphaPairsChanged))
iter_num += 1
else:
nonBoundIs = np.nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
for i in nonBoundIs:
alphaPairsChanged += innerL(i, oS)
print('non-bound, iter: %d i: %d, pairs changed %d' % (iter_num, i, alphaPairsChanged))
iter_num += 1
if entireSet:
entireSet = False
elif (alphaPairsChanged == 0):
entireSet = True
print('iteration number: %d' % iter_num)
return oS.b, oS.alphas
[In]: dataArr, labelArr = loadDataSet('Ch06/testSet.txt')
[In]: b, alphas = smoP(dataArr, labelArr, 0.6, 0.001, 40)
[In]: b
[Out]: matrix([[-2.89901748]])
[In]: alphas[alphas > 0]
[Out]: matrix([[0.06961952, 0.0169055 , 0.0169055 , 0.0272699 , 0.04522972,
0.0272699 , 0.0243898 , 0.06140181, 0.06140181]])
两种方法对比图如下,左图为完整版,右图为简化版
5. 在复杂数据上应用核函数
- 前面使用的数据都是线性可分的情况。对于某些非线性可分,但是存在某种可识别的方式,比如分类边界为一个圆,圆内部为一类,圆外部为另一类,对于人眼来说这种情况很容易分辨,但对于分类器,就无法直接识别了。
- 这种情况下,我们需要使用一种称为核函数的工具将数据转换成易于分类器理解的形式。
5.1 利用核函数将数据映射到高维空间
- 上述问题的解决方法为将数据从一个特征空间映射到另一个特征空间。一般情况下是从低维特征空间映射到高维空间,该过程通过核函数完成,这里核函数可以看做是一个包装器或者是接口
- SVM 中的所有运算都可以写成内积,我们可以把内积运算替换成核函数,而不必做简化处理,该方式称为核技巧或者核变电。其含义具体就是:对于核函数 k k k,对所有 x i , x j ∈ X x_i,x_j \in X xi,xj∈X,满足 k ( x i , x j ) = ⟨ ϕ ( x i ) , ϕ ( x j ) ⟩ k(x_i,x_j)=\langle \phi(x_i),\phi(x_j)\rangle k(xi,xj)=⟨ϕ(xi),ϕ(xj)⟩,其中 ϕ ( x ) \phi(x) ϕ(x) 为映射函数,这样避免了先计算映射值,再计算内积这种复杂运算。
5.2 径向基核函数
- 对于简单的例子,我们可以手动构造对应映射的核函数,如果对于任意一个映射,要构造出对应的核函数就很困难了。因此,通常,人们会从一些常用的核函数中进行选择,根据问题和数据的不同,选择不同的参数,得到不同的核函数。其中一个非常流行的核函数就是径向基核函数。径向基函数的高斯版本如下:
k ( x , y ) = e x p ( − ∣ ∣ x − y ∣ ∣ 2 2 σ 2 ) k(x, y) = exp\left(\frac{-||x-y||^2}{2\sigma^2}\right) k(x,y)=exp(2σ2−∣∣x−y∣∣2)其中, σ \sigma σ 是用户自定义的用于确定到达率(reach)或者说函数值跌落到0的速度参数。
- 核转换函数:该函数定义为计算单条数据的核函数值,然后在数据结构类中添加核函数值属性,并且在该类中计算每条数据的核函数值。最终的核函数值矩阵大小为(m, m)
'''核转换函数'''
import numpy as np
def kernelTrans(X, A, kTup):
'''
X: 输入矩阵
A: 单个数据的向量
kTup: 核函数的信息,为二元元组,第一个元素为核函数类型,第二个元素为核函数的可选参数
Return 转换结果 K
'''
m, n = np.shape(X)
K = np.mat(np.zeros((m, 1)))
if kTup[0] == 'lin':
K = X * A.T
elif kTup[0] == 'rbf':
for j in range(m):
deltaRow = X[j,:] - A
K[j] = deltaRow * deltaRow.T
K = np.exp(K / (-1*kTup[1]**2))
else:
print('raise NameError(''Houston We Have a Problem -- That Kernel is not recognized'')')
return K
class optStruct:
def __init__(self, dataMatIn, classLabels, C, toler, kTup):
self.X = dataMatIn
self.labelMat = classLabels
self.C = C
self.tol = toler
self.m = np.shape(dataMatIn)[0]
self.alphas = np.mat(np.zeros((self.m, 1)))
self.b = 0
self.eCache = np.mat(np.zeros((self.m, 2)))
self.K = np.mat(np.zeros((self.m, self.m)))
for i in range(self.m):
self.K[:, i] = kernelTrans(self.X, self.X[i, :], kTup)
- 使用了核转换函数后,之前有针对 X 的内积运算都需要修改,包括两个函数,内循环函数和 Ek 计算函数:
'''使用核函数的 innerL() 和 calcEk()'''
def innerL(i, oS):
Ei = calcEk(oS, i)
if ((oS.labelMat[i] * Ei < -oS.tol) and (oS.alphas[i] < oS.C)) or ((oS.labelMat[i]*Ei > oS.tol) and (oS.alphas[i] > 0)):
j, Ej = selectJ(i, oS, Ei)
alpha_i_old = oS.alphas[i].copy()
alpha_j_old = oS.alphas[j].copy()
if (oS.labelMat[i] != oS.labelMat[j]):
L = max(0, oS.alphas[j] - oS.alphas[i])
H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
else:
L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)
H = min(oS.C, oS.alphas[j] + oS.alphas[i])
if L == H:
print('L==H')
return 0
eta = 2.0 * oS.K[i,j] - oS.K[i,i] - oS.K[j,j]
if eta >= 0:
print('eta>=0')
return 0
oS.alphas[j] -= oS.labelMat[j] * (Ei - Ej) / eta
oS.alphas[j] = clipAlpha(oS.alphas[j], H, L)
updateEk(oS, j)
if (abs(oS.alphas[j] - alpha_j_old) < 0.00001):
print('j not moving enough')
return 0
oS.alphas[i] += oS.labelMat[i] * oS.labelMat[j] * (alpha_j_old - oS.alphas[j])
updateEk(oS, i)
b1 = oS.b - Ei - oS.labelMat[i] * (oS.alphas[i] - alpha_i_old) * oS.K[i,i] -\
oS.labelMat[j] * (oS.alphas[j] - alpha_j_old) * oS.K[i,j]
b2 = oS.b - Ej - oS.labelMat[i] * (oS.alphas[i] - alpha_i_old) * oS.K[i,j] -\
oS.labelMat[j] * (oS.alphas[j] - alpha_j_old) * oS.K[j,j]
if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]):
oS.b = b1
elif (0 < oS.alphas[j]) and (oS.C > oS.alphas[j]):
oS.b = b2
else:
oS.b = (b1 + b2) / 2.0
return 1
else:
return 0
def calcEk(oS, k):
yk = float(np.multiply(oS.alphas, oS.labelMat).T * oS.K[:, k]) + oS.b
Ek = yk - float(oS.labelMat[k])
return Ek
5.3 在测试中使用核函数
'''利用核函数进行分类的径向基测试函数'''
def testRbf(k1=1.3):
dataArr, labelArr = loadDataSet('Ch06/testSetRBF.txt')
b, alphas = smoP(dataArr, labelArr, 200, 0.0001, 10000, ('rbf', k1))
dataMat = np.mat(dataArr)
labelMat = np.mat(labelArr).T
svInd = np.nonzero(alphas.A > 0)[0]
sVs = dataMat[svInd]
labelSV = labelMat[svInd]
print('there are %d Support Vectors' % np.shape(sVs)[0])
m, n = np.shape(dataMat)
errorCount = 0
for i in range(m):
kernelEval = kernelTrans(sVs, dataMat[i,:], ('rbf', k1))
predict = kernelEval.T * np.multiply(labelSV, alphas[svInd]) + b
if np.sign(predict) != np.sign(labelArr[i]):
errorCount += 1
print('the training error rate is :%f' % (float(errorCount) / m))
dataArr, labelArr = loadDataSet('Ch06/testSetRBF2.txt')
errorCount = 0
dataMat = np.mat(dataArr)
labelMat = np.mat(labelArr).T
m, n = np.shape(dataMat)
for i in range(m):
kernelEval = kernelTrans(sVs, dataMat[i,:], ('rbf', k1))
predict = kernelEval.T * np.multiply(labelSV, alphas[svInd]) + b
if np.sign(predict) != np.sign(labelArr[i]):
errorCount += 1
print('the test error rate is :%f' % (float(errorCount) / m))
[In]: testRbf()
[Out]: there are 26 Support Vectors
[Out]: the training error rate is :0.090000
[Out]: the test error rate is :0.180000
- 改变 k1 的值,可以发现 k1 越小,支持向量越多,训练错误率越低,但测试错误率升高,即出现过拟合。k1 存在一个最优值,对于不同类型的数据集,该值可能差异很大
参考资料:
- [1] 机器学习实战第六章
- [2] Python3《机器学习实战》学习笔记(八):支持向量机原理篇之手撕线性SVM
- [3] Python3《机器学习实战》学习笔记(九):支持向量机实战篇之再撕非线性SVM