1 前备知识

在这里简略讲一下使用方法,具体原理和推导公式不展开讲了。

1.1 拉格朗日乘子法

拉格朗日乘子法就是求函数支持向量机 分类 python python支持向量机代码_支持向量机 分类 python在约束条件支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_02下的极值的方法。其主要思想是将约束条件函数与原函数联立,从而求出使原函数取得极值的各个变量的解。

首先看下面的例题:
支持向量机 分类 python python支持向量机代码_极值_03
第一步将每个约束条件都分配一个乘子支持向量机 分类 python python支持向量机代码_极值_04,在将目标函数和所有的约束函数相加,得到函数:
支持向量机 分类 python python支持向量机代码_极值_05
其中每个约束条件支持向量机 分类 python python支持向量机代码_约束条件_06的右边都是0,所以支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_07.
支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_08
第二步对支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_09求偏导:
支持向量机 分类 python python支持向量机代码_约束条件_10
令偏导数等于0,用支持向量机 分类 python python支持向量机代码_极值_04表示支持向量机 分类 python python支持向量机代码_约束函数_12
支持向量机 分类 python python支持向量机代码_约束条件_13
将所得支持向量机 分类 python python支持向量机代码_约束函数_12代入约束条件支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_15中,求得支持向量机 分类 python python支持向量机代码_约束条件_16
支持向量机 分类 python python支持向量机代码_极值_17
得到支持向量机 分类 python python支持向量机代码_约束条件_16的值,代入上式得到支持向量机 分类 python python支持向量机代码_约束函数_12的最优解。

1.2 KKT条件

我们可以发现,1.1讲的拉格朗日乘子法中,它的约束条件都是等式,那么对于约束条件是不等式的应该怎么办呢?

对于一个新的极值问题:
支持向量机 分类 python python支持向量机代码_约束条件_20
为了统一,首先将约束条件都转化为小于号:

支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_21
依旧是分配乘子并求和:
支持向量机 分类 python python支持向量机代码_约束函数_22
其中支持向量机 分类 python python支持向量机代码_约束条件_06是不等式约束条件,支持向量机 分类 python python支持向量机代码_极值_24是等式约束条件。(此例中没有等式)
支持向量机 分类 python python支持向量机代码_约束函数_25
KKT条件就是最优值,KKT条件为:

  1. 支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_26对每个支持向量机 分类 python python支持向量机代码_约束函数_27求偏导等于支持向量机 分类 python python支持向量机代码_约束函数_28
  2. 支持向量机 分类 python python支持向量机代码_约束函数_29
  3. 支持向量机 分类 python python支持向量机代码_极值_30
  4. 支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_31
  5. 支持向量机 分类 python python支持向量机代码_约束函数_32

可以发现,将3、4、5合并就是:
支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_33
对于上例题,接下来的操作就是:
一、支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_34对每个支持向量机 分类 python python支持向量机代码_约束函数_12求偏导等于支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_36求出支持向量机 分类 python python支持向量机代码_约束函数_12的表达式。
二、将支持向量机 分类 python python支持向量机代码_约束函数_12的表达式代入支持向量机 分类 python python支持向量机代码_约束条件_39,求出支持向量机 分类 python python支持向量机代码_约束条件_16
三、将支持向量机 分类 python python支持向量机代码_约束条件_16代回,求出支持向量机 分类 python python支持向量机代码_约束函数_12

2 SVM

2.1 简介

支持向量机(support vector machines, SVM)是一种二分类问题模型。
它的目标是找到一个尽可能正确分类,且“确信度”尽可能高的超平面。
其中“确信度”指的是:正确分类的样本点,距离超平面越远,该样本点的确信度就越高。(我对这个样本点分类正确的信任程度)
换而言之,就是该超平面的鲁棒性要好,泛化能力要强。

对于线性可分支持向量机,分类超平面为:
支持向量机 分类 python python支持向量机代码_约束条件_43
相应的分类决策函数
支持向量机 分类 python python支持向量机代码_约束条件_44
称为线性可分支持向量机。

2.2 函数间隔与几何间隔

函数间隔和几何间隔是用来描述计算“确信度”的。

2.1.1 函数间隔

在超平面支持向量机 分类 python python支持向量机代码_极值_45确定的情况下,支持向量机 分类 python python支持向量机代码_约束函数_46的值可以作为衡量样本点支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_09确信度的一个指标,支持向量机 分类 python python支持向量机代码_约束函数_46就是该样本点的函数间隔。
定义(函数间隔):对于给定的训练数据集支持向量机 分类 python python支持向量机代码_约束条件_49和超平面支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_50,定义超平面支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_50关于样本点支持向量机 分类 python python支持向量机代码_约束条件_52的函数间隔为:
支持向量机 分类 python python支持向量机代码_约束条件_53
超平面支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_50关于样本点支持向量机 分类 python python支持向量机代码_约束条件_52的函数间隔为所有样本点函数间隔的最小值:
支持向量机 分类 python python支持向量机代码_约束函数_56
函数间隔虽然可以表示预测的确信度,但是当支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_57支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_58成比例增加时,超平面没有改变,但函数间隔却成倍增加。
例如超平面支持向量机 分类 python python支持向量机代码_约束函数_59支持向量机 分类 python python支持向量机代码_约束条件_60等价,但是支持向量机 分类 python python支持向量机代码_约束函数_61
为了解决这个问题,引入了几何间隔。

2.2.2 几何间隔

在二维坐标系中,点是样本,线是分离超平面,那么点到线的距离就是几何间隔。
点到线的距离公式:
支持向量机 分类 python python支持向量机代码_约束函数_62
扩展到多维坐标系:
支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_63
其中支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_64为L2范数。
我们记支持向量机 分类 python python支持向量机代码_约束条件_65
同样:
支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_66

支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_67

2.2.3 间隔最大化

回想一下,我们SVM的目标是什么来着?
是寻找一个“确信度”尽可能高的超平面,也就是一个几何间隔尽可能大的超平面。

那么我们可以得到一个约束最优化问题:
使几何间隔最大化的支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_57支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_58,并且满足约束条件所有样本的几何间隔大于支持向量机 分类 python python支持向量机代码_约束条件_70.
支持向量机 分类 python python支持向量机代码_约束条件_71
又因为函数间隔和几何间隔的关系:
支持向量机 分类 python python支持向量机代码_约束函数_72
上述问题可以化为:
支持向量机 分类 python python支持向量机代码_约束函数_73
函数间隔支持向量机 分类 python python支持向量机代码_约束函数_74的取值并不影响最优化问题的解,则可以令支持向量机 分类 python python支持向量机代码_极值_75

这个其实也好理解,我们看上式,支持向量机 分类 python python支持向量机代码_约束函数_76支持向量机 分类 python python支持向量机代码_约束函数_77就是两个参数,令支持向量机 分类 python python支持向量机代码_约束函数_78,就相当于将支持向量机 分类 python python支持向量机代码_约束函数_79支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_80,对于该约束最优化问题的解没有任何影响。就好比解方程时等式两边同时除以一个数。

另外,最大化支持向量机 分类 python python支持向量机代码_约束函数_81和最小化支持向量机 分类 python python支持向量机代码_约束条件_82是等价的。
所以上面的问题就变成了:
支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_83
这就是SVM的基本型(对于线性可分问题),后面主要就是这个约束问题的求解。

2.3 对偶问题

我们可以发现上面的约束最优化问题本身就是一个凸二次规划问题,所以我们可以使用更高效的方法去求解,也就是使用拉格朗日乘子法得到其“对偶问题”。
首先稍微做一下调整:
支持向量机 分类 python python支持向量机代码_极值_84
定义拉格朗日乘子支持向量机 分类 python python支持向量机代码_极值_04,根据
支持向量机 分类 python python支持向量机代码_约束函数_22
得到:
支持向量机 分类 python python支持向量机代码_极值_87
支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_34支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_57支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_58求偏导等于零,得:
支持向量机 分类 python python支持向量机代码_约束函数_91
虽然把支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_58给消去了,但是并没有影响,后续过程中原式的支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_58也会消去。
支持向量机 分类 python python支持向量机代码_极值_94代入支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_34中,即可用支持向量机 分类 python python支持向量机代码_极值_04支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_57支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_58替换掉,得到对偶问题:
支持向量机 分类 python python支持向量机代码_极值_99
另外,需要注意,这里的约束问题需要用到KKT条件:

  1. 支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_26支持向量机 分类 python python支持向量机代码_约束函数_76支持向量机 分类 python python支持向量机代码_约束函数_77求偏导等于支持向量机 分类 python python支持向量机代码_约束函数_28
  2. 支持向量机 分类 python python支持向量机代码_极值_104
  3. 支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_31
  4. 支持向量机 分类 python python支持向量机代码_约束条件_106
  5. 支持向量机 分类 python python支持向量机代码_约束函数_29; (这里没有等式约束,可以忽略)

整理一下,可简化为两种情况:

  1. 支持向量机 分类 python python支持向量机代码_约束条件_108时,支持向量机 分类 python python支持向量机代码_极值_104
  2. 支持向量机 分类 python python支持向量机代码_约束条件_110时,支持向量机 分类 python python支持向量机代码_极值_111
    支持向量机 分类 python python支持向量机代码_极值_112为松弛变量,下面会讲。

到这里,只要解出支持向量机 分类 python python支持向量机代码_极值_04,就可以得到支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_57支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_58,从而得到分离超平面。
那么如何去求解支持向量机 分类 python python支持向量机代码_极值_04呢?一种比较高效的方法就是SMO算法。
在此之前需要先讲一下松弛变量和核函数。

2.4 松弛变量和核函数

2.4.1 线性SVM与松弛变量

在此之前所讲的都是数据集时线性可分的,但是还有的数据集是线性不可分的,这两种情况合起来就是线性SVM。
如下图,线性不可分的数据集:

支持向量机 分类 python python支持向量机代码_极值_117

线性不可分意味着某些样本点支持向量机 分类 python python支持向量机代码_约束条件_52不能满足函数间隔大于等于1的约束条件。为了解决这个问题,给每一个样本点支持向量机 分类 python python支持向量机代码_约束条件_52引进一个松弛变量支持向量机 分类 python python支持向量机代码_约束条件_120,使函数间隔加上松弛变量大于等于1。这样,约束条件变为:
支持向量机 分类 python python支持向量机代码_极值_121
同时,对每个松弛变量支持向量机 分类 python python支持向量机代码_极值_122,需要支付一个代价支持向量机 分类 python python支持向量机代码_极值_122,则目标函数就由原来的支持向量机 分类 python python支持向量机代码_约束条件_124变为:
支持向量机 分类 python python支持向量机代码_约束条件_125
其中支持向量机 分类 python python支持向量机代码_约束函数_126称为惩罚参数,用来控制松弛变量的代价高低。
所以对于线性不可分的SVM的基本模型就是:
支持向量机 分类 python python支持向量机代码_约束函数_127

2.4.2 非线性SVM与核函数

2.4.2.1 核技巧和核函数

简单说就是,对于非线性问题,可以将样本通过一个支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_128从原始空间映射到一个更高维的特征空间,使得样本在这个特征空间内线性可分。

如下图,对于异或问题,就是一个非线性问题,原始问题是在一个二维空间中,当我们将样本特征空间做一个映射,提升到三维空间中,就能容易找到一个分离超平面。

支持向量机 分类 python python支持向量机代码_约束函数_129


非线性SVM的基本模型为:

支持向量机 分类 python python支持向量机代码_约束条件_130

其对偶问题为:
支持向量机 分类 python python支持向量机代码_约束条件_131

特征空间的维数可能非常高。如果支持向量机的求解只用到内积运算,而在低维输入空间又存在某个函数 支持向量机 分类 python python支持向量机代码_约束条件_132 ,它恰好等于在高维空间中这个内积,即支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_133那么支持向量机就不用计算复杂的非线性变换,而由这个函数 支持向量机 分类 python python支持向量机代码_约束条件_132 直接得到非线性变换的内积,使大大简化了计算。这样的函数 支持向量机 分类 python python支持向量机代码_约束条件_132 称为核函数。
则该对偶问题可以改写为:
支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_136

2.4.2.2 常用的核函数

支持向量机 分类 python python支持向量机代码_约束条件_137

3 SMO算法

序列最小最优化(sequential minimal optimization,SMO)算法,可以高效地实现支持向量机问题。SMO算法在这里用来更新优化支持向量机 分类 python python支持向量机代码_约束函数_138的值。
算法的基本思想就是,每次挑选出两个变量(假设为支持向量机 分类 python python支持向量机代码_约束函数_139支持向量机 分类 python python支持向量机代码_约束条件_140),固定其它的变量(支持向量机 分类 python python支持向量机代码_约束函数_138),每次只更新支持向量机 分类 python python支持向量机代码_约束函数_139支持向量机 分类 python python支持向量机代码_约束条件_140,循环迭代多次,尽可能接近最优解。

SMO算法其实就做了两件事:

  1. 变量的挑选方法:挑选出支持向量机 分类 python python支持向量机代码_约束函数_139支持向量机 分类 python python支持向量机代码_约束条件_140
  2. 两个变量二次规划的求解方法:更新支持向量机 分类 python python支持向量机代码_约束函数_139支持向量机 分类 python python支持向量机代码_约束条件_140

3.1 两个变量二次规划的求解方法

假设选择的两个变量为支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_148支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_149,其它变量支持向量机 分类 python python支持向量机代码_约束函数_150是固定的,则SMO的最优化问题的子问题可以写成:
支持向量机 分类 python python支持向量机代码_约束函数_151
其中支持向量机 分类 python python支持向量机代码_极值_152是常数,目标函数中省略了不含支持向量机 分类 python python支持向量机代码_约束函数_153的常数项。
接下来,我们从约束条件入手:
支持向量机 分类 python python支持向量机代码_极值_154

我们假设考虑为变量支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_149的最优化问题。

假设问题的初始可行解为支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_156支持向量机 分类 python python支持向量机代码_极值_157,更新后的解为支持向量机 分类 python python支持向量机代码_约束条件_158支持向量机 分类 python python支持向量机代码_极值_159,并记未经剪辑时支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_149的最优解为支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_161。(未经剪辑就是不一定满足支持向量机 分类 python python支持向量机代码_极值_162

我们先求支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_161,再对其约束得到支持向量机 分类 python python支持向量机代码_极值_159

我们假设最优值支持向量机 分类 python python支持向量机代码_极值_159必须满足:

支持向量机 分类 python python支持向量机代码_极值_166

如上图所示,分两种情况讨论,L、H的值就是线段支持向量机 分类 python python支持向量机代码_约束条件_167和边界相交的点,可以求出:

支持向量机 分类 python python支持向量机代码_约束条件_168

  1. 支持向量机 分类 python python支持向量机代码_约束条件_169
    支持向量机 分类 python python支持向量机代码_约束条件_170
  2. 支持向量机 分类 python python支持向量机代码_约束函数_171
    支持向量机 分类 python python支持向量机代码_极值_172

得到支持向量机 分类 python python支持向量机代码_极值_173后,我们先放一放,先去求支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_161的值:

支持向量机 分类 python python支持向量机代码_约束函数_175

支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_176为预测值,支持向量机 分类 python python支持向量机代码_极值_177为预测值与真实值之差。
则:
支持向量机 分类 python python支持向量机代码_约束条件_178
其中,
支持向量机 分类 python python支持向量机代码_约束条件_179
再求支持向量机 分类 python python支持向量机代码_极值_159
支持向量机 分类 python python支持向量机代码_约束条件_181

根据
支持向量机 分类 python python支持向量机代码_约束条件_182
得到:
支持向量机 分类 python python支持向量机代码_约束条件_183
于是得到新的支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_148支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_149

3.2 变量的挑选方法

SMO算法要挑选的两个变量,一个(支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_148)是违反KKT条件的,另一个(支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_149)的选择标准是希望能使支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_149有足够大的变化。

3.2.1 第一个变量的选择

SMO称选择第一个变量的过程称为外层循环。外层循环在训练样本中选取违反KKT条件的样本点,并将其对应的支持向量机 分类 python python支持向量机代码_极值_04作为第一个变量。
KKT条件:

  1. 支持向量机 分类 python python支持向量机代码_约束条件_108时,支持向量机 分类 python python支持向量机代码_极值_104
  2. 支持向量机 分类 python python支持向量机代码_约束条件_110时,支持向量机 分类 python python支持向量机代码_极值_111
    一般把松弛变量统一为一个量,记为支持向量机 分类 python python支持向量机代码_约束条件_194。则:
    支持向量机 分类 python python支持向量机代码_约束函数_195时,支持向量机 分类 python python支持向量机代码_极值_111

在检验选取过程中,外层循环首先遍历符合支持向量机 分类 python python支持向量机代码_约束条件_197条件的,再遍历符合支持向量机 分类 python python支持向量机代码_约束条件_198条件的,检验他们是否满足KKT条件,将第一个不满足KKT条件的的作为支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_148

3.2.2 第二个变量的选择

SMO称选择第二个变量的过程称为内层循环。我们的选择标准是希望能使支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_149有足够大的变化。
根据公式:
支持向量机 分类 python python支持向量机代码_约束函数_201
可知支持向量机 分类 python python支持向量机代码_极值_159是依赖于支持向量机 分类 python python支持向量机代码_约束函数_203的,所以:

  1. 支持向量机 分类 python python支持向量机代码_约束函数_204时,选择所有样本点中最小的支持向量机 分类 python python支持向量机代码_约束函数_205作为支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_206
  2. 支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_207时,选择所有样本点中最大的支持向量机 分类 python python支持向量机代码_约束函数_205作为支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_206

同时将挑选出的支持向量机 分类 python python支持向量机代码_极值_177相对应的支持向量机 分类 python python支持向量机代码_极值_04作为第二个变量(支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_149)

3.2.3 计算并更新阈值支持向量机 分类 python python支持向量机代码_支持向量机 分类 python_58和差值支持向量机 分类 python python支持向量机代码_极值_177

支持向量机 分类 python python支持向量机代码_约束条件_215

4 Python代码实现SVM

import numpy as np

class SVM:
    def init_args(self, max_iter, features, labels):
        self.max_iter = max_iter
        self.m, self.n = features.shape
        self.X = features
        self.Y = labels
        self.b = 0.0
        self.alpha = np.ones(self.m)
        self.E = [self.calc_E(i) for i in range(self.m)]
        self.C = 1.0

    # 核函数,这里选用线性核
    def kernel(self, x1, x2):
        sum = 0
        for i in range(self.n):
            sum += x1[i]*x2[i]
        return sum

    # 计算预测值
    def calc_g(self, i):
        g = self.b
        for j in range(self.m):
            g += self.alpha[j]*self.Y[j]*self.kernel(self.X[i], self.X[j])
        return g

    # 计算预测值与真实值的差值
    def calc_E(self, i):
        return self.calc_g(i) - self.Y[i]

    # 判断是否满足KKT条件
    def judge_KKT(self, i):
        if self.alpha[i]==0 and self.Y[i]*self.calc_g(i)>=1:
            return True
        elif 0<self.alpha[i]<self.C and self.Y[i]*self.calc_g(i)==1:
            return True
        return False

    def get_alpha(self):
        # 外层循环,找第一个变量,遍历样本点,找到第一个不满足KKT条件的
        for i in range(self.m):
            if self.judge_KKT(i) == False:
                # 内层循环,找第二个变量
                E1 = self.E[i]
                if E1 >= 0:
                    j = min(range(self.m), key=lambda index : self.E[index])
                else:
                    j = max(range(self.m), key=lambda index : self.E[index])
                return i, j

    def train(self, max_iter, features, labels):
        # 迭代训练
        self.init_args(max_iter, features, labels)
        for i in range(self.max_iter):
            # 选择 alpha1和alpha1
            i1, i2 = self.get_alpha()

            # 边界
            if self.Y[i1] == self.Y[i2]:
                L = max(0, self.alpha[i2]+self.alpha[i1]-self.C)
                H = min(self.C, self.alpha[i2]+self.alpha[i1])
            else:
                L = max(0, self.alpha[i2]-self.alpha[i1])
                H = min(self.C, self.alpha[i2]+self.alpha[i1]+self.C)
            
            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])
            alpha2_new_unc = self.alpha[i2] + self.Y[i2] * (self.E[i1] - self.E[i2]) / eta

            if alpha2_new_unc > H:
                alpha2_new = H
            elif L <= alpha2_new_unc <= H:
                alpha2_new = alpha2_new_unc
            elif alpha2_new_unc < L:
                alpha2_new = L
            alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (self.alpha[i2] - alpha2_new)
            
            b1_new = -self.E[i1] - 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 = -self.E[i2] - 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.calc_E(i1)
            self.E[i2] = self.calc_E(i2)
        print("Train: {0} iterations have been done.".format(self.max_iter))



from sklearn.svm import SVC
svc = SVC()
svc.fit(X, Y)
svc.score(Xt, Yt)

参考: