在SMO算法中,如果使用遍历所有的 α i α i ,再随机选择第二个变量进行优化,也能实现功能,但是算法速度非常的慢,为了提高算法的运算速度,目前一般采用一种启发式的变量选择。
在周志华先生的《机器学习》中提到:
只需要选取的 α i α i 与 α j α j 中有一个不满足KKT条件,目标函数就会在迭代会变化的幅度增大。只管看来,KKT条件违背程度越大,变量更新后可能导致目标函数数值的变化幅度就会越大。于是,SMO可以首先取违背KKT条件程度最大的向量,第二个变量选取一个使目标函数减小最快的变量。
1.第一个变量的选择
第一变量的选择称为外循环;需要选择一个违反KKT条件的变量。而与原优化问题对应的KKT条件为:
怎样才算是违反KKT条件呢?
1)如果 α i <C α i < C ,却有 y i f(x i )<1 y i f ( x i ) < 1 ,本来它应该等于1的。
2)如果 α i >0 α i > 0 ,却有 y i f(x i )>1 y i f ( x i ) > 1 ,本来它应该等于1的。
上述的两个情况,就是违背KKT的情况,我们对上面的两个情况进行变形如下:
于是违反KKT条件的两种情况可以描述为:
1)如果 α i <C α i < C ,并且有 y i E i <0 y i E i < 0 ,即 y i f(x i )<1 y i f ( x i ) < 1
2)如果 α i >0 α i > 0 ,并且有 y i E i >0 y i E i > 0 ,即 y i f(x i )>1 y i f ( x i ) > 1
于是我们在写代码的时候判断条件可以写成这样:
if (y[i]E[i]>0 and alpha[i]>0) or (y[i]E[i]<0 and alpha[i]<C):
但是我们在《Machine Learning in Action》一书中关于第一个变量选择时使用的判断条件为:
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)):
这里我稍做一下解释,oS.labelMat[i]相当于y[i],oS.C相当于C,唯一的不同是多了一个oS.tol,这个变量是干嘛用的,我之前也不了解,于是我去查了一下发现oS.tol是一个被称之为容错率的东西,这是用来干嘛的呢。
我在支持向量机SMO算法内循环的一个问题?中找到了答案,他是这么说的:
至于这里的容错率,是由于KKT条件本身是比较严苛的,所以需要设定一个容忍值,只要误差在容忍值的范围内则优化可以结束。
所以到这里我们明白了这个tol变量是容忍值,我们回过头去看书中关于函数的调用时,这个容忍值一般设为0.001。
2.第二个变量的选择
假设在外循环中找到的第一个变量记为α1,那么第二个变量的选择我希望能使α2有较大的变化。由于α2是依赖于|E1−E2|,当Ei为正时,那么选择最小的Ei作为E2 ,如果Ei为负,选择最大Ei作为E2,通常为每个样本的Ei保存在一个列表中,选择最大的|E1−E2|来近似最大化步长。
在《Machine Learning in Action》中我们可以看到关于第二变量的代码如下:
def selectJ(i, oS, Ei): #this is the second choice -heurstic, and calcs Ej
maxK = -1; maxDeltaE = 0; Ej = 0
oS.eCache[i] = [1,Ei] #set valid #choose the alpha that gives the maximum delta E
validEcacheList = nonzero(oS.eCache[:,0].A)[0]
if (len(validEcacheList)) > 1:
for k in validEcacheList: #loop through valid Ecache values and find the one that maximizes delta E
if k == i: continue #don't calc for i, waste of time
Ek = calcEk(oS, k)
deltaE = abs(Ei - Ek)
if (deltaE > maxDeltaE):
maxK = k; maxDeltaE = deltaE; Ej = Ek
return maxK, Ej
else: #in this case (first time around) we don't have any valid eCache values
j = selectJrand(i, oS.m)
Ej = calcEk(oS, j)
return j, Ej
参考资料:
周志华《机器学习》
《Machine Learning in Action》