机器学习系列 10:支持向量机 02 - SMO(序列最小化)


针对 “支持向量机” 将分为以下几个部分进行介绍:

  1. 支持向量机 01 - 线性可分支持向量机和线性支持向量机
  2. 支持向量机 02 - SMO(序列最小化)(本篇)
  3. 支持向量机 03 - 非线性支持向量机
  4. 支持向量机 04 - SVR(支持向量回归)


  本内容将介绍 SMO(序列最小化)算法,包含详细公式推导以及 Python 代码实现。

  在学习本内容前,需要对支持向量机基本理论知识有一定了解,如果您还不了解,可以参阅 支持向量机 01 - 线性可分支持向量机和线性支持向量机

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

  SMO 算法是一种启发式算法,其基本思路是:如果所有变量的解都满足此最优化问题的 KKT 条件,那么这个最优化问题的解就得到了。因为 KKT 条件是该最优化问题的充要条件。否则,选择两个变量,固定其他变量,针对这两个变量构建一个二次规划问题。这个二次规划问题关于这两个变量的解应该更接近原始二次规划问题的解,因为这会使得原始二次规划问题的目标函数值变得更小。重要的是,这时子问题可以通过解析方法求解,这样就可以大大提高整个算法的计算速度。子问题有两个变量,一个是违反 KKT 条件最严重的那一个,另一个由约束条件自动确定。如此,SMO 算法将原问题不断分解为子问题并对子问题求解,进而达到求解原问题的目的。

一、SMO 算法描述

  整个 SMO 算法包括两个部分:求解两个变量二次规划的解析方法和选择变量的启发式方法。

1.1 两个变量二次规划的求解方法
1.1.1 问题转化

  在 支持向量机 01 - 线性可分支持向量机和线性支持向量机 中讲解了 SVM 的基本原理,了解到 SMO 算法要解如下凸二次规划的对偶问题

(1) max ⁡ α ( ∑ i = 1 m α i − 1 2 ∑ i = 1 m ∑ j = 1 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) ) s . t . ∑ i = 1 m α i y ( i ) = 0 α i ≥ 0 , i = 1 , 2 , ⋯   , m \begin{aligned} & \max_\alpha \left( \sum_{i=1}^{m}\alpha_i - \frac{1}{2} \sum_{i=1}^{m} \sum_{j=1}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} \right) \\\\ & \begin{aligned} s.t. \quad &\sum_{i=1}^{m}\alpha_i y^{(i)} = 0 \\\\ & \alpha_i \geq 0,\quad i=1,2,\cdots,m \end{aligned} \end{aligned} \tag{1} αmax(i=1mαi21i=1mj=1mαiαjy(i)y(j)x(i)x(j))s.t.i=1mαiy(i)=0αi0,i=1,2,,m(1)

  添加一个负号,将最大值问题转换成最小值问题

(2) min ⁡ α ( 1 2 ∑ i = 1 m ∑ j = 1 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) − ∑ i = 1 m α i ) s . t . ∑ i = 1 m α i y ( i ) = 0 α i ≥ 0 , i = 1 , 2 , ⋯   , m \begin{aligned} & \min_\alpha \left( \frac{1}{2} \sum_{i=1}^{m} \sum_{j=1}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} - \sum_{i=1}^{m}\alpha_i \right) \\\\ & \begin{aligned} s.t. \quad & \sum_{i=1}^{m}\alpha_i y^{(i)} = 0 \\\\ & \alpha_i \geq 0,\quad i=1,2,\cdots,m \end{aligned} \end{aligned} \tag{2} αmin(21i=1mj=1mαiαjy(i)y(j)x(i)x(j)i=1mαi)s.t.i=1mαiy(i)=0αi0,i=1,2,,m(2)

1.1.2 转换为二元函数

  假设选择的两个变量为 α 1 \alpha_1 α1 α 2 \alpha_2 α2,其他变量 α i ( i = 3 , 4 , ⋯   , m ) \alpha_i(i=3,4, \cdots ,m) αi(i=3,4,,m) 是固定的,式(2)可写为

(3) W ( α 1 , α 2 ) = 1 2 ∑ i = 1 m ∑ j = 1 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) − ∑ i = 1 m α i = 1 2 ∑ i = 1 m ( ∑ j = 1 2 α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) + ∑ j = 3 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) ) − α i − α 2 − ∑ i = 3 m α i = 1 2 ∑ i = 1 2 ( ∑ j = 1 2 α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) + ∑ j = 3 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) ) + 1 2 ∑ i = 3 m ( ∑ j = 1 2 α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) + ∑ j = 3 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) ) − α i − α 2 − ∑ i = 3 m α i = 1 2 ∑ i = 1 2 ∑ j = 1 2 α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) + ∑ i = 1 2 ∑ j = 3 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) + 1 2 ∑ i = 3 m ∑ j = 3 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) − α 1 − α 2 − ∑ i = 3 m α i = 1 2 α 1 2 x ( 1 ) ⋅ x ( 1 ) + 1 2 α 2 2 x ( 2 ) x ( 2 ) + y ( 1 ) y ( 2 ) α 1 α 2 x ( 1 ) ⋅ x ( 2 ) + y ( 1 ) α 1 ∑ j = 3 m α j y ( j ) x ( 1 ) ⋅ x ( j ) + y ( 2 ) α 2 ∑ j = 3 m α j y ( j ) x ( 2 ) ⋅ x ( j ) − α 1 − α 2 + 1 2 ∑ i = 3 m ∑ j = 3 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) − ∑ i = 3 m α i \begin{aligned} W(\alpha_1, \alpha_2) & = \frac{1}{2} \sum_{i=1}^{m} \sum_{j=1}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} - \sum_{i=1}^{m}\alpha_i \\\\ & = \frac{1}{2} \sum_{i=1}^{m} \left( \sum_{j=1}^{2} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} + \sum_{j=3}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} \right) - \alpha_i - \alpha_2 - \sum_{i=3}^{m} \alpha_i \\\\ & = \frac{1}{2} \sum_{i=1}^{2} \left( \sum_{j=1}^{2} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} + \sum_{j=3}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} \right) \\ & \quad\quad + \frac{1}{2} \sum_{i=3}^{m} \left( \sum_{j=1}^{2} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} + \sum_{j=3}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} \right) - \alpha_i - \alpha_2 - \sum_{i=3}^{m} \alpha_i \\\\ & = \frac{1}{2}\sum_{i=1}^{2}\sum_{j=1}^{2} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} + \sum_{i=1}^{2}\sum_{j=3}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} \\ & \quad\quad +\frac{1}{2}\sum_{i=3}^{m}\sum_{j=3}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} - \alpha_1 - \alpha_2 - \sum_{i=3}^m \alpha_i \\\\ & = \frac{1}{2} \alpha_1^2 \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} + \frac{1}{2} \alpha_2^2 \mathbf{x}^{(2)} \mathbf{x}^{(2)} + y^{(1)} y^{(2)} \alpha_1 \alpha_2 \mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)} + y^{(1)} \alpha_1 \sum_{j=3}^m \alpha_j y^{(j)} \mathbf{x}^{(1)} \cdot \mathbf{x}^{(j)} \\ & \quad\quad + y^{(2)} \alpha_2 \sum_{j=3}^m \alpha_j y^{(j)} \mathbf{x}^{(2)} \cdot \mathbf{x}^{(j)} - \alpha_1 - \alpha_2 + \frac{1}{2}\sum_{i=3}^{m}\sum_{j=3}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} - \sum_{i=3}^m \alpha_i \end{aligned} \tag{3} W(α1,α2)=21i=1mj=1mαiαjy(i)y(j)x(i)x(j)i=1mαi=21i=1m(j=12αiαjy(i)y(j)x(i)x(j)+j=3mαiαjy(i)y(j)x(i)x(j))αiα2i=3mαi=21i=12(j=12αiαjy(i)y(j)x(i)x(j)+j=3mαiαjy(i)y(j)x(i)x(j))+21i=3m(j=12αiαjy(i)y(j)x(i)x(j)+j=3mαiαjy(i)y(j)x(i)x(j))αiα2i=3mαi=21i=12j=12αiαjy(i)y(j)x(i)x(j)+i=12j=3mαiαjy(i)y(j)x(i)x(j)+21i=3mj=3mαiαjy(i)y(j)x(i)x(j)α1α2i=3mαi=21α12x(1)x(1)+21α22x(2)x(2)+y(1)y(2)α1α2x(1)x(2)+y(1)α1j=3mαjy(j)x(1)x(j)+y(2)α2j=3mαjy(j)x(2)x(j)α1α2+21i=3mj=3mαiαjy(i)y(j)x(i)x(j)i=3mαi(3)

  为了描述方便,我们定义如下符号:

(4) f ( x ( i ) ) = ∑ j = 1 m α j y ( j ) x ( j ) ⋅ x ( i ) + b = ∑ j = 1 m α j y ( j ) x ( i ) ⋅ x ( j ) + b f(\mathbf{x}^{(i)}) = \sum_{j=1}^{m} \alpha_j y^{(j)} \mathbf{x}^{(j)} \cdot \mathbf{x}^{(i)} + b = \sum_{j=1}^{m} \alpha_j y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} + b \tag{4} f(x(i))=j=1mαjy(j)x(j)x(i)+b=j=1mαjy(j)x(i)x(j)+b(4)

(5) v i = ∑ j = 3 m α j y ( j ) x ( i ) ⋅ x ( j ) = f ( x ( i ) ) − ∑ j = 1 2 α j y ( j ) x ( i ) ⋅ x ( j ) − b v_i = \sum_{j=3}^m \alpha_j y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} = f(\mathbf{x}^{(i)}) - \sum_{j=1}^2 \alpha_j y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)}- b \tag{5} vi=j=3mαjy(j)x(i)x(j)=f(x(i))j=12αjy(j)x(i)x(j)b(5)

  根据式(4)和(5),目标函数式(3)可写为

(6) W ( α 1 , α 2 ) = 1 2 α 1 2 x ( 1 ) ⋅ x ( 1 ) + 1 2 α 2 2 x ( 2 ) ⋅ x ( 2 ) + y ( 1 ) y ( 2 ) α 1 α 2 x ( 1 ) ⋅ x ( 2 ) + y ( 1 ) α 1 v 1 + y ( 2 ) α 2 v 2 − α 1 − α 2 + c o n s t a n t \begin{aligned} W(\alpha_1,\alpha_2) & = \frac{1}{2} \alpha_1^2 \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} + \frac{1}{2} \alpha_2^2 \mathbf{x}^{(2)} \cdot \mathbf{x}^{(2)} + y^{(1)} y^{(2)} \alpha_1 \alpha_2 \mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)} \\ & + y^{(1)} \alpha_1 v_1 + y^{(2)} \alpha_2 v_2 - \alpha_1 - \alpha_2 + constant \end{aligned} \tag{6} W(α1,α2)=21α12x(1)x(1)+21α22x(2)x(2)+y(1)y(2)α1α2x(1)x(2)+y(1)α1v1+y(2)α2v2α1α2+constant(6)

其中

(7) c o n s t a n t = 1 2 ∑ i = 3 m ∑ j = 3 m α i α j y ( i ) y ( j ) x ( i ) ⋅ x ( j ) − ∑ i = 3 m α i constant = \frac{1}{2}\sum_{i=3}^{m}\sum_{j=3}^{m} \alpha_i \alpha_j y^{(i)} y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} - \sum_{i=3}^m \alpha_i \tag{7} constant=21i=3mj=3mαiαjy(i)y(j)x(i)x(j)i=3mαi(7)

  因为 c o n s t a n t constant constant 部分对 α 1 \alpha_1 α1 α 2 ​ \alpha_2​ α2 来说,属于常数项,在求导的时候,直接变为 0,所以我们不需要关心这部分的内容。

1.1.3 转换为一元函数

  因为有 ∑ i = 1 m α i y ( i ) = 0 \sum_{i=1}^{m}\alpha_i y^{(i)} = 0 i=1mαiy(i)=0,可得

(8) α 1 y ( 1 ) + α 2 y ( 2 ) = − ∑ i = 3 m α i y ( i ) \alpha_1 y^{(1)} + \alpha_2 y^{(2)} = -\sum_{i=3}^m \alpha_i y^{(i)} \tag{8} α1y(1)+α2y(2)=i=3mαiy(i)(8)

  对变量 α 1 \alpha_1 α1 α 2 \alpha_2 α2 来说, α i \alpha_i αi y ( i ) y^{(i)} y(i) i = 3 , 4 , ⋯   , m i = 3,4,\cdots,m i=3,4,,m )可看作常数项,因此有

(9) α 1 y ( 1 ) + α 2 y ( 2 ) = B \alpha_1 y^{(1)} + \alpha_2 y^{(2)} = B \tag{9} α1y(1)+α2y(2)=B(9)

其中, B B B 为一个常数。将等式(9)两边同时乘以 y ( 1 ) y^{(1)} y(1),得

(10) α 1 = B y ( 1 ) − α 2 y ( 1 ) y ( 2 ) = ( B − α 2 y ( 2 ) ) y ( 1 ) \alpha_1 = By^{(1)} - \alpha_2 y^{(1)} y^{(2)} = \left(B - \alpha_2 y^{(2)}\right)y^{(1)} \tag{10} α1=By(1)α2y(1)y(2)=(Bα2y(2))y(1)(10)

将其带入式(6),得到只含变量 α 2 \alpha_2 α2 的目标函数

(11) W ( α 2 ) = 1 2 ( B − α 2 y ( 2 ) ) 2 x ( 1 ) ⋅ x ( 1 ) + 1 2 α 2 2 x ( 2 ) ⋅ x ( 2 ) + y ( 2 ) ( B − α 2 y ( 2 ) ) α 2 x ( 1 ) ⋅ x ( 2 ) + ( B − α 2 y ( 2 ) ) v 1 + y ( 2 ) α 2 v 2 − ( B − α 2 y ( 2 ) ) y ( 1 ) − α 2 + c o n s t a n t \begin{aligned} W(\alpha_2) & = \frac{1}{2} (B - \alpha_2 y^{(2)})^{2} \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} + \frac{1}{2} \alpha_2^{2} \mathbf{x}^{(2)} \cdot \mathbf{x}^{(2)} + y^{(2)} (B - \alpha_2 y^{(2)}) \alpha_2 \mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)} \\ & \quad\quad + (B - \alpha_2 y^{(2)}) v_1 + y^{(2)} \alpha_2 v_2 - (B - \alpha_2 y^{(2)})y^{(1)} - \alpha_2 + constant \end{aligned} \tag{11} W(α2)=21(Bα2y(2))2x(1)x(1)+21α22x(2)x(2)+y(2)(Bα2y(2))α2x(1)x(2)+(Bα2y(2))v1+y(2)α2v2(Bα2y(2))y(1)α2+constant(11)

1.1.4 求解 α 2 n e w , u n c l i p p e d \alpha_2^{new,unclipped} α2new,unclipped

  将式(11)对 α 2 \alpha_2 α2 求导,得

(12) ∂ W ( α 2 ) ∂ α 2 = x ( 1 ) ⋅ x ( 1 ) α 2 + x ( 2 ) ⋅ x ( 2 ) α 2 − 2 x ( 1 ) ⋅ x ( 2 ) α 2 − B x ( 1 ) ⋅ x ( 1 ) y ( 2 ) + B x ( 1 ) ⋅ x ( 2 ) y ( 2 ) + y ( 1 ) y ( 2 ) − v 1 y ( 2 ) + v 2 y ( 2 ) − 1 \begin{aligned} \frac{\partial W(\alpha_2)}{\partial \alpha_2} &= \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} \alpha_2 + \mathbf{x}^{(2)} \cdot \mathbf{x}^{(2)} \alpha_2 - 2 \mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)} \alpha_2 \\ &\quad\quad - B \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} y^{(2)} + B \mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)} y^{(2)} + y^{(1)} y^{(2)} - v_1 y^{(2)} + v_2 y^{(2)} - 1 \end{aligned} \tag{12} α2W(α2)=x(1)x(1)α2+x(2)x(2)α22x(1)x(2)α2Bx(1)x(1)y(2)+Bx(1)x(2)y(2)+y(1)y(2)v1y(2)+v2y(2)1(12)

令其为 0 ​ 0​ 0,得

(13) α 2 n e w = y ( 2 ) ( y ( 2 ) − y ( 1 ) + B ( x ( 1 ) ⋅ x ( 1 ) − x ( 1 ) ⋅ x ( 2 ) ) + v 1 − v 2 ) x ( 1 ) ⋅ x ( 1 ) + x ( 2 ) ⋅ x ( 2 ) − 2 x ( 1 ) ⋅ x ( 2 ) \alpha_2^{new} = \frac {y^{(2)}\left(y^{(2)} - y^{(1)} + B(\mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} - \mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)}) + v_1 - v_2\right)} {\mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} + \mathbf{x}^{(2)} \cdot \mathbf{x}^{(2)} - 2\mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)}} \tag{13} α2new=x(1)x(1)+x(2)x(2)2x(1)x(2)y(2)(y(2)y(1)+B(x(1)x(1)x(1)x(2))+v1v2)(13)

  定义如下符号

(14) E i = f ( x ( i ) ) − y ( i ) E_i = f(\mathbf{x}^{(i)}) - y^{(i)} \tag{14} Ei=f(x(i))y(i)(14)

(15) η = x ( 1 ) ⋅ x ( 1 ) + x ( 2 ) ⋅ x ( 2 ) − 2 x ( 1 ) ⋅ x ( 2 ) \eta = \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} + \mathbf{x}^{(2)} \cdot \mathbf{x}^{(2)} - 2\mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)} \tag{15} η=x(1)x(1)+x(2)x(2)2x(1)x(2)(15)

其中 E i E_i Ei 为误差项, η \eta η 为学习速率。

  由于已知

(16) B = α 1 o l d y ( 1 ) + α 2 o l d y ( 2 ) B = \alpha_1^{old} y^{(1)} + \alpha_2^{old} y^{(2)} \tag{16} B=α1oldy(1)+α2oldy(2)(16)

(17) v i = f ( x ( i ) ) − ∑ j = 1 2 α j y ( j ) x ( i ) ⋅ x ( j ) − b v_i = f\left(\mathbf{x}^{(i)}\right) - \sum_{j=1}^2 \alpha_j y^{(j)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(j)} - b \tag{17} vi=f(x(i))j=12αjy(j)x(i)x(j)b(17)

将式(16)和(17)带入式(13), 将 α 2 n e w , u n c l i p p e d ​ \alpha_2^{new,unclipped}​ α2new,unclipped 化简后得

(18) α 2 n e w , u n c l i p p e d = α 2 o l d + y ( 2 ) ( E 1 − E 2 ) η \alpha_2^{new,unclipped} = \alpha_2^{old} + \frac{y^{(2)}(E_1-E_2)}{\eta} \tag{18} α2new,unclipped=α2old+ηy(2)(E1E2)(18)

1.1.5 求解 α 2 n e w \alpha_2^{new} α2new(对原始 α 2 n e w , u n c l i p p e d \alpha_2^{new,unclipped} α2new,unclipped 解进行修剪)

  上面求出的 α 2 n e w , u n c l i p p e d \alpha_2^{new, unclipped} α2new,unclipped 解是没有经过修剪的。我们知道 α i \alpha_i αi 存在以下约束条件

(19) { 0 &lt; α i &lt; C α 1 y ( 1 ) + α 2 y ( 2 ) = B \left \{ \begin{array}{cc} \begin{aligned} &amp; 0 &lt; \alpha_i &lt; C \\\\ &amp; \alpha_1 y^{(1)} + \alpha_2 y^{(2)} = B \end{aligned} \end{array} \right. \tag{19} 0<αi<Cα1y(1)+α2y(2)=B(19)

可知 α 2 n e w ​ \alpha_2^{new}​ α2new 的取值需要满足一定条件,假设为

(20) L ≤ α 2 n e w ≤ H L \leq \alpha_2^{new} \leq H \tag{20} Lα2newH(20)

  图-1 在二维平面上直观地表达了式(19)中的约束条件,从而可知

  • y ( 1 ) ≠ y ( 2 ) y^{(1)} \neq y^{(2)} y(1)̸=y(2) 时,存在 α 2 o l d − α 1 o l d = k \alpha_2^{old} - \alpha_1^{old}=k α2oldα1old=k,得

(21) L = m a x ( 0 , α 2 o l d − α 1 o l d ) , H = m i n ( C , C + α 2 o l d − α 1 o l d ) L = max(0, \alpha_2^{old} - \alpha_1^{old}),\quad H = min(C, C + \alpha_2^{old} - \alpha_1^{old}) \tag{21} L=max(0,α2oldα1old),H=min(C,C+α2oldα1old)(21)

  • y ( 1 ) = y ( 2 ) y^{(1)} = y^{(2)} y(1)=y(2) 时,存在 α 2 o l d + α 1 o l d = k ​ \alpha_2^{old} + \alpha_1^{old}=k​ α2old+α1old=k,得

(22) L = m a x ( 0 , α 2 o l d + α 1 o l d − C ) , H = m i n ( C , α 2 o l d + α 1 o l d ) L = max(0, \alpha_2^{old} + \alpha_1^{old} - C), \quad H = min(C, \alpha_2^{old} + \alpha_1^{old}) \tag{22} L=max(0,α2old+α1oldC),H=min(C,α2old+α1old)(22)

图-1

  则经过修剪后, α 2 n e w \alpha_2^{new} α2new 的解为

(23) α 2 n e w = { H , α 2 n e w , u n c l i p p e d &gt; H α 2 n e w , u n c l i p p e d , L ≤ α 2 n e w , u n c l i p p e d ≤ H L , α 2 n e w , u n c l i p p e d &lt; L \alpha_2^{new} = \left \{ \begin{array}{cc} \begin{aligned} &amp; H, &amp; \alpha_2^{new,unclipped} &gt; H \\\\ &amp; \alpha_2^{new,unclipped}, &amp; L \leq \alpha_2^{new,unclipped} \leq H \\\\ &amp; L, &amp; \alpha_2^{new,unclipped} &lt; L \end{aligned} \end{array} \right. \tag{23} α2new=H,α2new,unclipped,L,α2new,unclipped>HLα2new,unclippedHα2new,unclipped<L(23)

1.1.6 求解 α 1 n e w \alpha_1^{new} α1new

  因为存在以下等式条件

(24) α 1 o l d y ( 1 ) + α 2 o l d y ( 2 ) = α 1 n e w y ( 1 ) + α 2 n e w y ( 2 ) \alpha_1^{old} y^{(1)} + \alpha_2^{old} y^{(2)} = \alpha_1^{new} y^{(1)} + \alpha_2^{new} y^{(2)} \tag{24} α1oldy(1)+α2oldy(2)=α1newy(1)+α2newy(2)(24)

从而得出

(25) α 1 n e w = α 1 o l d + y ( 1 ) y ( 2 ) ( α 2 o l d − α 2 n e w ) \alpha_1^{new} = \alpha_1^{old} + y^{(1)} y^{(2)} (\alpha_2^{old} - \alpha_2^{new}) \tag{25} α1new=α1old+y(1)y(2)(α2oldα2new)(25)

1.1.7 求解阈值 b n e w b^{new} bnew

  在求得 α 1 n e w \alpha_1^{new} α1new α 2 n e w \alpha_2^{new} α2new 后,需要根据它们更新 b b b

  1. 如果 0 &lt; α 1 n e w &lt; C 0 &lt; \alpha_1^{new} &lt; C 0<α1new<C
      由 KKT 条件可知(在 支持向量机 01 - 线性可分支持向量机和线性支持向量机 中 2.2 节有进行介绍),当 0 &lt; α i &lt; C 0 &lt; \alpha_i &lt; C 0<αi<C 时,这个样本点是支持向量。因此,满足
    (26) y ( 1 ) ( w T x ( 1 ) + b ) = 1 y^{(1)}(\mathbf{w}^T \mathbf{x}^{(1)} + b) = 1 \tag{26} y(1)(wTx(1)+b)=1(26)

    上面公式两边同时乘以 y ( 1 ) ​ y^{(1)}​ y(1)
    (27) w T x ( 1 ) + b = y ( 1 ) \mathbf{w}^T \mathbf{x}^{(1)} + b = y^{(1)} \tag{27} wTx(1)+b=y(1)(27)

    w = ∑ i = 1 m α i y ( i ) x ( i ) ​ \mathbf{w} = \sum_{i = 1}^{m} \alpha_i y^{(i)} \mathbf{x}^{(i)}​ w=i=1mαiy(i)x(i) 代入,得

    (28) ∑ i = 1 m α i y ( i ) x ( i ) ⋅ x ( 1 ) + b = y ( 1 ) \sum_{i=1}^{m} \alpha_i y^{(i)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(1)} + b = y^{(1)} \tag{28} i=1mαiy(i)x(i)x(1)+b=y(1)(28)

      因为我们是根据 α 1 n e w ​ \alpha_1^{new}​ α1new α 2 n e w ​ \alpha_2^{new}​ α2new 的值更新 b ​ b​ b,整理可得:

    (29) b 1 n e w = y ( 1 ) − ∑ i = 3 m α i y ( i ) x ( i ) ⋅ x ( 1 ) − α 1 n e w y ( 1 ) x ( 1 ) ⋅ x ( 1 ) − α 2 n e w y ( 2 ) x ( 2 ) ⋅ x ( 1 ) b_1^{new} =y^{(1)} -\sum_{i=3}^{m} \alpha_i y^{(i)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(1)} -\alpha_1^{new} y^{(1)} \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} -\alpha_2^{new} y^{(2)} \mathbf{x}^{(2)} \cdot \mathbf{x}^{(1)} \tag{29} b1new=y(1)i=3mαiy(i)x(i)x(1)α1newy(1)x(1)x(1)α2newy(2)x(2)x(1)(29)

    其中有

    (30) y ( 1 ) − ∑ i = 3 m α i y ( i ) x ( i ) ⋅ x ( 1 ) = y ( 1 ) − ∑ i = 1 m α i y ( i ) x ( i ) ⋅ x ( 1 ) − b o l d + α 1 o l d y ( 1 ) x ( 1 ) ⋅ x ( 1 ) + α 2 o l d y ( 2 ) x ( 2 ) ⋅ x ( 1 ) + b o l d = y ( 1 ) − f ( x ( 1 ) ) + α 1 o l d y ( 1 ) x ( 1 ) ⋅ x ( 1 ) + α 2 o l d y ( 2 ) x ( 2 ) ⋅ x ( 1 ) + b o l d = − E 1 + α 1 o l d y ( 1 ) x ( 1 ) ⋅ x ( 1 ) + α 2 o l d y ( 2 ) x ( 2 ) ⋅ x ( 1 ) + b o l d \begin{aligned} y^{(1)} - \sum_{i=3}^{m} \alpha_i y^{(i)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(1)} &amp;= y^{(1)} - \sum_{i=1}^{m} \alpha_i y^{(i)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(1)} -b^{old} \\ &amp; \quad\quad +\alpha_1^{old} y^{(1)} \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} +\alpha_2^{old} y^{(2)} \mathbf{x}^{(2)} \cdot \mathbf{x}^{(1)} +b^{old} \\\\ &amp;= y^{(1)} - f\left(\mathbf{x}^{(1)}\right) +\alpha_1^{old} y^{(1)} \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} +\alpha_2^{old} y^{(2)} \mathbf{x}^{(2)} \cdot \mathbf{x}^{(1)} +b^{old} \\\\ &amp;= - E_1 +\alpha_1^{old} y^{(1)} \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} +\alpha_2^{old} y^{(2)} \mathbf{x}^{(2)} \cdot \mathbf{x}^{(1)} +b^{old} \end{aligned} \tag{30} y(1)i=3mαiy(i)x(i)x(1)=y(1)i=1mαiy(i)x(i)x(1)bold+α1oldy(1)x(1)x(1)+α2oldy(2)x(2)x(1)+bold=y(1)f(x(1))+α1oldy(1)x(1)x(1)+α2oldy(2)x(2)x(1)+bold=E1+α1oldy(1)x(1)x(1)+α2oldy(2)x(2)x(1)+bold(30)

    将式(30)代入式(29)得

    (31) b 1 n e w = b o l d − E 1 − y ( 1 ) ( α 1 n e w − α 1 o l d ) x ( 1 ) ⋅ x ( 1 ) − y ( 2 ) ( α 2 n e w − α 2 o l d ) x ( 2 ) x ( 1 ) b_1^{new} = b^{old} - E_1 -y^{(1)}(\alpha_1^{new} - \alpha_1^{old}) \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} -y^{(2)}(\alpha_2^{new} - \alpha_2^{old}) \mathbf{x}^{(2)} \mathbf{x}^{(1)} \tag{31} b1new=boldE1y(1)(α1newα1old)x(1)x(1)y(2)(α2newα2old)x(2)x(1)(31)

  2. 如果 0 &lt; α 2 n e w &lt; C 0 &lt; \alpha_2^{new} &lt; C 0<α2new<C
      按照上面的步骤,同样可求得 b 2 n e w ​ b_2^{new}​ b2new
    (32) b 2 n e w = b o l d − E 2 − y ( 1 ) ( α 1 n e w − α 1 o l d ) x ( 1 ) ⋅ x ( 2 ) − y ( 2 ) ( α 2 n e w − α 2 o l d ) x ( 2 ) x ( 2 ) b_2^{new} = b^{old} - E_2 -y^{(1)}(\alpha_1^{new} - \alpha_1^{old}) \mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)} -y^{(2)}(\alpha_2^{new} - \alpha_2^{old}) \mathbf{x}^{(2)} \mathbf{x}^{(2)} \tag{32} b2new=boldE2y(1)(α1newα1old)x(1)x(2)y(2)(α2newα2old)x(2)x(2)(32)

      如果 α 1 n e w \alpha_1^{new} α1new α 2 n e w \alpha_2^{new} α2new 同时满足条件 0 &lt; α i n e w &lt; C 0 &lt; \alpha_i^{new} &lt; C 0<αinew<C,则 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;如果 α 1 n e w \alpha_1^{new} α1new α 2 n e w \alpha_2^{new} α2new 0 0 0 或者 C C C,那么 b 1 n e w b_1^{new} b1new b 2 n e w b_2^{new} b2new 以及它们之间的数都是符合 KKT 条件的阈值,这是选择它们的重点作为 b n e w b^{new} bnew。则 b n e w b^{new} bnew

(33) b n e w = { b 1 n e w , 0 &lt; α 1 n e w &lt; C b 2 n e w , 0 &lt; α 2 n e w &lt; C ( b 1 n e w + b 2 n e w ) 2 , o t h e r w i s e b^{new} = \left \{ \begin{array}{cc} &amp; b_1^{new}, &amp; 0 &lt; \alpha_1^{new} &lt; C \\\\ &amp; b_2^{new}, &amp; 0 &lt; \alpha_2^{new} &lt; C \\\\ &amp; \frac{(b_1^{new} + b_2^{new})}{2}, &amp; otherwise \end{array} \right. \tag{33} bnew=b1new,b2new,2(b1new+b2new),0<α1new<C0<α2new<Cotherwise(33)

1.1.8 变量更新算法的步骤

  根据上面讲解的内容,我们来整理一下变量更新算法的步骤:

  • 步骤 1:计算误差

E 1 = f ( x ( 1 ) ) − y ( 1 ) = ∑ i = 1 m α i y ( i ) x ( i ) ⋅ x ( 1 ) + b − y ( 1 ) E_1 = f(\mathbf{x}^{(1)}) - y^{(1)} = \sum_{i=1}^{m} \alpha_i y^{(i)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(1)} + b - y^{(1)} E1=f(x(1))y(1)=i=1mαiy(i)x(i)x(1)+by(1)

E 2 = f ( x ( 2 ) ) − y ( 2 ) = ∑ i = 1 m α i y ( i ) x ( i ) ⋅ x ( 2 ) + b − y ( 2 ) E_2 = f(\mathbf{x}^{(2)}) - y^{(2)} = \sum_{i=1}^{m} \alpha_i y^{(i)} \mathbf{x}^{(i)} \cdot \mathbf{x}^{(2)} + b - y^{(2)} E2=f(x(2))y(2)=i=1mαiy(i)x(i)x(2)+by(2)

  • 步骤 2:计算 α 2 n e w \alpha_2^{new} α2new 取值范围

{ i f ( y ( 1 ) ≠ y ( 2 ) ) L = m a x ( 0 , α 2 o l d − α 1 o l d ) , H = m i n ( C , C + α 2 o l d − α 1 o l d ) i f ( y ( 1 ) = y ( 2 ) ) L = m a x ( 0 , α 2 o l d + α 1 o l d − C ) , H = m i n ( C , α 2 o l d + α 1 o l d ) \left \{ \begin{array}{cc} \begin{aligned} &amp; if(y^{(1)} \neq y^{(2)}) \quad L = max(0, \alpha_2^{old} - \alpha_1^{old}),\quad H = min(C, C + \alpha_2^{old} - \alpha_1^{old}) \\\\ &amp; if(y^{(1)} = y^{(2)}) \quad L = max(0, \alpha_2^{old} + \alpha_1^{old} - C), \quad H = min(C, \alpha_2^{old} + \alpha_1^{old}) \end{aligned} \end{array} \right. if(y(1)̸=y(2))L=max(0,α2oldα1old),H=min(C,C+α2oldα1old)if(y(1)=y(2))L=max(0,α2old+α1oldC),H=min(C,α2old+α1old)

  • 步骤 3:计算 η ​ \eta​ η

η = x ( 1 ) ⋅ x ( 1 ) + x ( 2 ) ⋅ x ( 2 ) − 2 x ( 1 ) x ( 2 ) \eta = \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} + \mathbf{x}^{(2)} \cdot \mathbf{x}^{(2)} - 2\mathbf{x}^{(1)} \mathbf{x}^{(2)} η=x(1)x(1)+x(2)x(2)2x(1)x(2)

  • 步骤 4:求解 α 2 n e w , u n c l i p p e d ​ \alpha_2^{new,unclipped}​ α2new,unclipped

α 2 n e w , u n c l i p p e d = α 2 o l d + y ( 2 ) ( E 1 − E 2 ) η \alpha_2^{new,unclipped} = \alpha_2^{old} + \frac{y^{(2)}(E_1-E_2)}{\eta} α2new,unclipped=α2old+ηy(2)(E1E2)

  • 步骤 5:求解 α 2 n e w ​ \alpha_2^{new}​ α2new

α 2 n e w = { H , α 2 n e w , u n c l i p p e d &gt; H α 2 n e w , u n c l i p p e d , L ≤ α 2 n e w , u n c l i p p e d ≤ H L , α 2 n e w , u n c l i p p e d &lt; L \alpha_2^{new} = \left \{ \begin{array}{cc} \begin{aligned} &amp; H, &amp; \alpha_2^{new,unclipped} &gt; H \\\\ &amp; \alpha_2^{new,unclipped}, &amp; L \leq \alpha_2^{new,unclipped} \leq H \\\\ &amp; L, &amp; \alpha_2^{new,unclipped} &lt; L \end{aligned} \end{array} \right. α2new=H,α2new,unclipped,L,α2new,unclipped>HLα2new,unclippedHα2new,unclipped<L

  • 步骤 6:求解 α 1 n e w \alpha_1^{new} α1new

α 1 n e w = α 1 o l d + y ( 1 ) y ( 2 ) ( α 2 o l d − α 2 n e w ) \alpha_1^{new} = \alpha_1^{old} + y^{(1)} y^{(2)} (\alpha_2^{old} - \alpha_2^{new}) α1new=α1old+y(1)y(2)(α2oldα2new)

  • 步骤 7:求解 b 1 n e w b_1^{new} b1new b 2 n e w b_2^{new} b2new

b 1 n e w = b o l d − E 1 − y ( 1 ) ( α 1 n e w − α 1 o l d ) x ( 1 ) ⋅ x ( 1 ) − y ( 2 ) ( α 2 n e w − α 2 o l d ) x ( 2 ) ⋅ x ( 1 ) b_1^{new} = b^{old} - E_1 - y^{(1)}(\alpha_1^{new} - \alpha_1^{old}) \mathbf{x}^{(1)} \cdot \mathbf{x}^{(1)} - y^{(2)}(\alpha_2^{new} - \alpha_2^{old}) \mathbf{x}^{(2)} \cdot \mathbf{x}^{(1)} b1new=boldE1y(1)(α1newα1old)x(1)x(1)y(2)(α2newα2old)x(2)x(1)

b 2 n e w = b o l d − E 2 − y ( 1 ) ( α 1 n e w − α 1 o l d ) x ( 1 ) ⋅ x ( 2 ) − y ( 2 ) ( α 2 n e w − α 2 o l d ) x ( 2 ) ⋅ x ( 2 ) b_2^{new} = b^{old} - E_2 - y^{(1)}(\alpha_1^{new} - \alpha_1^{old}) \mathbf{x}^{(1)} \cdot \mathbf{x}^{(2)} - y^{(2)}(\alpha_2^{new} - \alpha_2^{old}) \mathbf{x}^{(2)} \cdot \mathbf{x}^{(2)} b2new=boldE2y(1)(α1newα1old)x(1)x(2)y(2)(α2newα2old)x(2)x(2)

  • 步骤 8:求解 b n e w ​ b^{new}​ bnew

b n e w = { b 1 n e w , 0 &lt; α 1 n e w &lt; C b 2 n e w , 0 &lt; α 2 n e w &lt; C ( b 1 n e w + b 2 n e w ) 2 , o t h e r w i s e b^{new} = \left \{ \begin{array}{cc} &amp; b_1^{new}, &amp; 0 &lt; \alpha_1^{new} &lt; C \\\\ &amp; b_2^{new}, &amp; 0 &lt; \alpha_2^{new} &lt; C \\\\ &amp; \frac{(b_1^{new} + b_2^{new})}{2}, &amp; otherwise \end{array} \right. bnew=b1new,b2new,2(b1new+b2new),0<α1new<C0<α2new<Cotherwise


  前面进行介绍时,我们有假设选择两个变量 α 1 ​ \alpha_1​ α1 α 2 ​ \alpha_2​ α2,但是在实际实现算法时,我们应该如何选择这两个变量呢?下面就来介绍一下。

1.2 变量 α 1 \alpha_1 α1 α 2 \alpha_2 α2 的选择方法
1.2.1 第 1 个变量的选择

  SMO 称选择第 1 个变量的过程为外层循环。外层循环在训练样本中选取违反 KKT 条件最严重的样本点,并将其对应的变量作为第 1 个变量。具体地,检验训练样本点是否满足 KKT 条件,即(在 支持向量机 01 - 线性可分支持向量机和线性支持向量机 中 2.2 节有进行介绍)

(34) α i = 0 ⇔ y ( i ) f ( x ( i ) ) ≥ 1 0 &lt; α i &lt; C ⇔ y ( i ) f ( x ( i ) ) = 1 α i = C ⇔ y ( i ) f ( x ( i ) ) ≤ 1 \begin{aligned} \alpha_i = 0 \quad \Leftrightarrow \quad y^{(i)} f(\mathbf{x}^{(i)}) \geq 1 \\\\ 0 &lt; \alpha_i &lt; C \quad \Leftrightarrow \quad y^{(i)} f(\mathbf{x}^{(i)}) = 1 \\\\ \alpha_i = C \quad \Leftrightarrow \quad y^{(i)} f(\mathbf{x}^{(i)}) \leq 1 \end{aligned} \tag{34} αi=0y(i)f(x(i))10<αi<Cy(i)f(x(i))=1αi=Cy(i)f(x(i))1(34)

  从而可得知,以下几种情况将会不满足 KKT 条件

(35) y ( i ) f ( x ( i ) ) ≥ 1 a n d α i &gt; 0 y ( i ) f ( x ( i ) ) = 1 a n d ( α i = 0 o r α i = C ) y ( i ) f ( x ( i ) ) ≤ 1 a n d α i &lt; C \begin{aligned} &amp; y^{(i)} f\left(\mathbf{x}^{(i)}\right) \geq 1 \quad and \quad \alpha_i &gt; 0 \\\\ &amp; y^{(i)} f\left(\mathbf{x}^{(i)}\right) = 1 \quad and \quad (\alpha_i = 0 \quad or \quad \alpha_i = C) \\\\ &amp; y^{(i)} f\left(\mathbf{x}^{(i)}\right) \leq 1 \quad and \quad \alpha_i &lt; C \end{aligned} \tag{35} y(i)f(x(i))1andαi>0y(i)f(x(i))=1and(αi=0orαi=C)y(i)f(x(i))1andαi<C(35)

  在检验过程中,外层循环首先遍历所有满足条件 0 &lt; α i &lt; C ​ 0 &lt; \alpha_i &lt; C​ 0<αi<C 的样本点,即在间隔边界上的支持向量点,检验它们是否满足 KKT 条件。如果这些样本点都满足 KKT 条件,那么遍历整个训练集,检验它们是否满足 KKT 条件。

1.2.2 第 2 个变量的选择

  SMO 称选择第 2 个变量的过程为内循环。假设在外层循环中已经找到第 1 个变量 α 1 \alpha_1 α1,现在要在内层循环中找第 2 个变量 α 2 \alpha_2 α2。第 2 个变量选择的标准是希望能使 α 2 \alpha_2 α2 有足够大的变化。

  由式(18)可知, α 2 n e w \alpha_2^{new} α2new 是依赖于 ∣ E 1 − E 2 ∣ |E_1 - E_2| E1E2 的,为了加快计算速度,一种简单的做法是选择 α 2 \alpha_2 α2,使其对应的 ∣ E 1 − E 2 ∣ |E_1 - E_2| E1E2 最大。为了节省计算时间,可以将所有 E i E_i Ei 值保存在一个列表中。

  在特殊情况下,如果内层循环通过以上方法选择的 α 2 \alpha_2 α2 不能使目标函数有足够的下降(等价于 ∣ α 2 n e w − α 2 ∣ |\alpha_2^{new} - \alpha_2| α2newα2 很小),那么可以采用以下启发式规则继续选择 α 2 \alpha_2 α2。遍历在间隔边界上的支持向量点,依次将其对应的变量作为 α 2 \alpha_2 α2 试用,知道目标函数有足够的下降。若仍然找不到合适的 α 2 \alpha_2 α2,那么遍历训练数据集;若还是找不到合适的 α 2 \alpha_2 α2,则放弃第 1 个 α 2 \alpha_2 α2,再通过外层循环寻求另外的 α 1 ​ \alpha_1​ α1

  由 Osuna 定理可知,只需选取的 α 1 \alpha_1 α1 α 2 ​ \alpha_2​ α2 中有一个不满足 KKT 条件,目标函数就会在迭代后减小。

1.3 SMO 算法的步骤

我们总结一下 SMO 算法的步骤:

  • 步骤 1:初始化 α \mathbf{\alpha} α b b b,比如初始化 α \mathbf{\alpha} α 为零向量, b b b 为 0。(注意这里的 α \mathbf{\alpha} α 是一个列向量)
  • 步骤 2:选取优化变量 α 1 \alpha_1 α1 α 2 \alpha_2 α2,然后更新 α 1 \alpha_1 α1 α 2 \alpha_2 α2 b b b
  • 步骤 3:如果满足停止条件(即前面提到的“所有变量的解都满足此最优化问题的 KKT 条件”)则结束;否则,跳到步骤 2。

二、Python 代码实现

  以下是 Python 3 的代码实现:

import numpy as np
import random
import matplotlib.pyplot as plt


class OptStruct:
    """
    数据结构,存储需要操作的数据
    """
    def __init__(self, input_mat, label_mat, c, toler):
        """
        :param input_mat: 样本特征值
        :param label_mat: 样本标签值
        :param c: 惩罚因子
        :param toler: 容错率
        """
        self.x = input_mat
        self.label_mat = label_mat
        self.c = c
        self.toler = toler
        self.m = np.shape(input_mat)[0]
        # 初始化 alphas 为零向量,初始化 b 为 0
        self.alphas = np.mat(np.zeros((self.m, 1)))
        self.b = 0
        # e_cache 用于缓存误差值
        # 第一列给出 e_cache 是否有效的标志位,第二列给出实际的误差值
        self.e_cache = np.mat(np.zeros((self.m, 2)))


class SVM:
    def __init__(self):
        self.alphas = None
        self.b = None
        self.w = None
        pass

    @staticmethod
    def calc_ek(os, k):
        """
        计算误差 E 值
        :param os:
        :param k:
        :return:
        """
        fxk = float(np.multiply(os.alphas, os.label_mat).T *\
                    (os.x * os.x[k, :].T)) + os.b
        ek = fxk - float(os.label_mat[k])
        return ek

    @staticmethod
    def select_j_rand(i, m):
        """
        随机选择第二个 alpha
        :param i: 第一个 alpha 对应的索引值
        :param m: alpha 参数的个数(即所有训练样本的总数)
        :return: 第二个 alpha 对应的索引值
        """
        j = i
        while j == i:
            j = int(random.uniform(0, m))
        return j

    @staticmethod
    def select_j(i, os, ei):
        """
        选择第二个 alpha
        :param i: 第一个 alpha 的索引值
        :param os: 数据结构
        :param ei: 第一个 alpha 对应样本点的误差
        :return: 返回第二个 alpha 的索引值 和 对应的误差值
        """
        max_k = -1
        # 保存最大的 |ei - ej|
        max_delta_e = 0
        ej = 0
        # 将 ei 更新到缓存 e_cache 中
        os.e_cache[i] = [1, ei]
        # 返回 os.e_cache 中 e 为非零的索引值列表
        valid_e_cache_list = np.nonzero(os.e_cache[:, 0].A)[0]
        # 第一次执行这个函数时,会执行 else 语句
        if (len(valid_e_cache_list)) > 1:
            # 通过遍历找到使 |ei - ej| 最大的第二个 alpha
            for k in valid_e_cache_list:
                # 如果是其本身,不进行比较
                if k == i:
                    continue
                ek = SVM.calc_ek(os, k)
                delta_e = abs(ei - ek)
                if delta_e > max_delta_e:
                    max_k = k
                    max_delta_e = delta_e
                    ej = ek
            return max_k, ej
        else:
            # 随机选择第二个 alpha
            j = SVM.select_j_rand(i, os.m)
            ej = SVM.calc_ek(os, j)
        return j, ej

    @staticmethod
    def update_ek(os, k):
        """
        更新误差缓存
        :param os: 数据结构
        :param k: 需要更新误差项对应的索引值
        """
        ek = SVM.calc_ek(os, k)
        os.e_cache[k] = [1, ek]

    @staticmethod
    def clip_alpha(alpha_j, high, low):
        """
        修剪 alpha_j
        :param alpha_j: alpha_j 的未修剪的值
        :param high: alpha_j 的上限
        :param low: alpha_j 的下限
        :return: alpha_j 修剪后的值
        """
        if alpha_j > high:
            alpha_j = high
        elif alpha_j < low:
            alpha_j = low
        return alpha_j

    def inner_l(self, i, os):
        """
        选择第二个 alpha,并更新 alpha 和 b
        :param i: 第一个 alpha 对应样本点的索引值
        :param os: 数据结构
        :return: 是否有更新 alpha 对
        """
        # 步骤 1:计算第一个 alpha 对应的样本点的误差
        ei = SVM.calc_ek(os, i)
        # 如果 (yi * f(xi) < 1 && alpha_i < c) 或者 (yi * f(xi) > 1 && alpha_i > 0)
        # 说明不满足 KKT 条件,可以将其作为第一个 alpha
        if ((os.label_mat[i] * ei < -os.toler) and (os.alphas[i] < os.c)) or \
                ((os.label_mat[i] * ei > os.toler) and (os.alphas[i] > 0)):
            # 步骤 1:选择第二个 alpha,并计算误差
            j, ej = SVM.select_j(i, os, ei)
            alpha_iold = os.alphas[i].copy()
            alpha_jold = os.alphas[j].copy()
            # 步骤 2:求取 alphas_j 取值的上下限
            if os.label_mat[i] != os.label_mat[j]:
                low = max(0, os.alphas[j] - os.alphas[i])
                high = min(os.c, os.c + os.alphas[j] - os.alphas[i])
            else:
                low = max(0, os.alphas[j] + os.alphas[i] - os.c)
                high = min(os.c, os.alphas[j] + os.alphas[i])
            if low == high:
                print('low == high')
                return 0
            # 步骤 3:计算 2*x1*x2 - x1*x2 - x2*x2
            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
            # 步骤 4:求解未修剪的 alpha_2
            os.alphas[j] -= os.label_mat[j] * (ei - ej) / eta
            # 步骤 5:求解修剪后的 alpha_2
            os.alphas[j] = SVM.clip_alpha(os.alphas[j], high, low)
            SVM.update_ek(os, j)
            if abs(os.alphas[j] - alpha_jold) < 0.00001:
                print('j not moving enough')
                return 0
            # 步骤 6:求解 alpha_1
            os.alphas[i] += os.label_mat[j] * os.label_mat[i] * \
                            (alpha_jold - os.alphas[j])
            SVM.update_ek(os, i)
            # 步骤 7:求解 b1 和 b2
            b1 = os.b - ei - os.label_mat[i] * (os.alphas[i] - alpha_iold) * \
                os.x[i, :] * os.x[i, :].T - os.label_mat[j] * \
                 (os.alphas[j] - alpha_jold) * os.x[i, :] * os.x[j, :].T
            b2 = os.b - ej - os.label_mat[i] * (os.alphas[i] - alpha_iold) * \
                os.x[i, :] * os.x[j, :].T - os.label_mat[j] * \
                 (os.alphas[j] - alpha_jold) * os.x[j, :] * os.x[j, :].T
            # 步骤 8:求解 b
            if (os.alphas[i] > 0) and (os.alphas[i] < os.c):
                os.b = b1
            elif (os.alphas[j] > 0) and (os.alphas[j] < os.c):
                os.b = b2
            else:
                os.b = (b1 + b2) / 2.0
            return 1
        else:
            return 0

    def smo_p(self, input_data, label_data, c, toler, max_iter):
        """
        SMO 算法实现
        :param input_data: 样本特征值
        :param label_data: 样本标签值
        :param c: 惩罚因子
        :param toler: 容错率
        :param max_iter: 最大迭代次数
        :return:
        """
        self.b = None
        self.w = None
        # 将需要操作的数据存放到数据结构 OptStruct 中
        os = OptStruct(np.mat(input_data), \
                       np.mat(label_data).transpose(), c, toler)
        iter = 0
        entire_set = True
        # 用于标记是否有更新 alpha 对
        alpha_pairs_changed = 0
        # 当迭代次数超过指定的最大值,
        # 或者遍历整个训练集都未对任意 alpha 对进行更新时,退出循环
        while (iter < max_iter) and ((alpha_pairs_changed > 0) or entire_set):
            alpha_pairs_changed = 0
            if entire_set:
                # 遍历整个训练集
                # 这里的 i 为第一个 alpha 对应的索引值
                for i in range(os.m):
                    alpha_pairs_changed += self.inner_l(i, os)
                print('fullSet, iter: {} i: {},' + \
                      'paris changed {}'.format(iter, i, alpha_pairs_changed))
                iter += 1
            else:
                # 遍历所有 0 < alpha < c 的样本点
                non_bound_i_s = \
                    np.nonzero((os.alphas.A > 0) * (os.alphas.A < os.c))[0]
                # 这里的 i 为第一个 alpha 对应的索引值
                for i in non_bound_i_s:
                    alpha_pairs_changed += self.inner_l(i, os)
                    print('non-bound, iter: {} i: {},' +\
                          'pairs changed {}'.format(iter, i, alpha_pairs_changed))
                iter += 1
            if entire_set:
                entire_set = False
            elif alpha_pairs_changed == 0:
                entire_set = True
            print('iteration number: {}'.format(iter))
        self.b = os.b
        self.alphas = os.alphas
        return os.b, os.alphas

    def calc_ws(self, input_data, label_data):
        """
        计算权值 w
        :param input_data: 样本特征值
        :param label_data: 样本标签值
        :return: 权值w
        """
        x = np.mat(input_data)
        label_mat = np.mat(label_data).transpose()
        m, n = np.shape(x)
        w = np.zeros((n, 1))
        for i in range(m):
            w += np.multiply(self.alphas[i] * label_mat[i], x[i, :].T)
        self.w = w
        # self.alphas = None
        return w

    def predict(self, input_feature):
        """
        进行预测
        :param input_feature: 需要预测的特征值
        :return: 预测标签
        """
        fx = np.mat(input_feature) * self.w + self.b
        output_label = -1
        if fx > 0:
            output_label = 1
        return output_label


def load_data_set(file_name):
    """
    从文件中获取数据集
    :param file_name: 文件名
    :return: 返回从文件中获取的数据集
             input_data 存储特征值,label_data 存储标签值
    """
    input_data, label_data = [], []
    fr = open(file_name)
    for line in fr.readlines():
        cur_line = line.strip().split('\t')
        input_data.append([float(cur_line[0]), float(cur_line[1])])
        label_data.append(float(cur_line[2]))
    return input_data, label_data


def show_plot(input_data, label_data, w, b, alphas):
    # 绘制训练集中的样本点
    data01_x1, data01_x2, data02_x1, data02_x2 = [], [], [], []
    for i in range(len(input_data)):
        if label_data[i] > 0:
            data01_x1.append(input_data[i][0])
            data01_x2.append(input_data[i][1])
        else:
            data02_x1.append(input_data[i][0])
            data02_x2.append(input_data[i][1])
    plt.scatter(data01_x1, data01_x2, s=30)
    plt.scatter(data02_x1, data02_x2, s=30)

    # 绘制分割线
    x_max = max(input_data)[0]
    x_min = min(input_data)[0]
    x = np.arange(x_min, x_max, 0.1)
    y = (-float(b) - float(w[0])*x) / float(w[1])
    plt.plot(x, y)

    # 标记支持向量
    for i, alpha in enumerate(alphas):
        if abs(alpha) > 0:
            x1, x2 = input_data[i]
            plt.scatter([x1], [x2], s=100,\
                        c='none', linewidths=1.5, edgecolors='red')
    plt.show()


def test_svm():
    input_data, label_data = load_data_set('testSet.txt')
    svm = SVM()
    svm.smo_p(input_data, label_data, 0.6, 0.001, 40)
    for i in range(len(svm.alphas)):
        if svm.alphas[i] > 0.0:
            print('support vector: {}, {}; alpha = {}'.\
                  format(input_data[i], label_data[i], svm.alphas[i]))
    print('b = {}'.format(svm.b))
    svm.calc_ws(input_data, label_data)
    print('w = {}'.format(svm.w))
    show_plot(input_data, label_data, svm.w, svm.b, svm.alphas)


if __name__ == "__main__":
    test_svm()

运行以上代码,将打印如下信息(只列出部分打印信息)及绘制如下图形:

support vector: [3.542485, 1.977398], -1.0; alpha = [[0.06961952]]
support vector: [2.114999, -0.004466], -1.0; alpha = [[0.0169055]]
support vector: [8.127113, 1.274372], 1.0; alpha = [[0.0169055]]
support vector: [4.658191, 3.507396], -1.0; alpha = [[0.0272699]]
support vector: [8.197181, 1.545132], 1.0; alpha = [[0.04522972]]
support vector: [7.40786, -0.121961], 1.0; alpha = [[0.0272699]]
support vector: [6.960661, -0.245353], 1.0; alpha = [[0.0243898]]
support vector: [6.080573, 0.418886], 1.0; alpha = [[0.06140181]]
support vector: [3.107511, 0.758367], -1.0; alpha = [[0.06140181]]
b = [[-2.89901748]]
w = [[ 0.65307162]
 [-0.17196128]]
图-2

参考:
[1] 周志华《机器学习》
[2] 李航《统计学习方法》
[3] 《机器学习实战》
[4] https://blog.youkuaiyun.com/luoshixian099/article/details/51227754
[5] https://zhuanlan.zhihu.com/p/29604517
[6] https://zhuanlan.zhihu.com/p/29872905

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值