最近在看《机器学习实战》,程序清单6-2,代码很长,但是代码解释里面作者有一些细节没有讲解到,通过学习SMO公式推导和看了一些博客,弄明白有2处判断条件,当作记录以后备查,也分享给大家,欢迎讨论交流。
准备工作
公式推导
首先当然是公式推导了,这是必备的,应该先看完公式推导再来编程,不然完全看不懂书里的代码在做什么。这里我查了周志华的《机器学习》、bishop的《Pattern Recognition and Machine Learning》、《Pattern Classification》、《Pattern Recognition》竟然都没有相关的详细推导过程。最后找到了李航的《统计学习方法》的7.4节有SMO非常详尽的从头到尾的推导,和《机器学习实战》书中代码是完全对应的,这里强烈推荐,本文的公式符号包括公式的标号也是按李航的书中书写。当然,想要深一步理解的同学,可以直接去看 platt的论文。《Learning with Kernels》里面也有推导,但是前后章节联系太密,要看懂这本书里面的推导,要前面很多小节都有看过,所以不是很建议。
这里有一篇博客也有详尽的推导过程,和李航的书是一模一样的,估计是书的扫描,不过好像也不是很全
代码
这里直接给出原书代码的github地址
https://github.com/pbharrin/machinelearninginaction/tree/master/Ch06
2个判断条件
好了,这里开始进入正题
2个判断条件主要出现在这个代码块中
while (iter < maxIter):
alphaPairsChanged = 0
for i in range(m):
fXi = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
Ei = fXi - float(labelMat[i])#if checks if an example violates KKT conditions
if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
j = selectJrand(i,m)
fXj = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
Ej = fXj - float(labelMat[j])
alphaIold = alphas[i].copy(); alphaJold = 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]*(Ei - Ej)/eta
alphas[j] = clipAlpha(alphas[j],H,L)
if (abs(alphas[j] - alphaJold) < 0.00001): print "j not moving enough"; continue
alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])#update i by the same amount as j
#the update is in the oppostie direction
b1 = b - Ei- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[i,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[i,:]*dataMatrix[j,:].T
b2 = b - Ej- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[j,:].T - labelMat[j]*(alphas[j]-alphaJold)*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" % (iter,i,alphaPairsChanged)
第一个判断条件
if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
这个判断条件按原书上写是为了
如果 α \alpha α可以更新进入优化进程
这里主要看
E
i
E_i
Ei的公式,
E
i
=
g
(
x
i
)
–
y
i
E_i = g(x_i) – y_i
Ei=g(xi)–yi。外循环是要找违背KKT条件最严重的样本点(每个样本点对应一个
α
\alpha
α)。而在这里主要看的KKT条件是
0
<
α
i
<
C
⟺
y
i
g
(
x
i
)
=
1
(
7.112
)
0<\alpha_i<C \Longleftrightarrow y_ig(x_i) = 1 \quad (7.112)
0<αi<C⟺yig(xi)=1(7.112)
这里判断
α
\alpha
α可以更改进入优化过程的条件写成数学式子是
- 【 y i E i < − t o l e r y_iE_i < -toler yiEi<−toler 且 α i < C \alpha_i<C αi<C 】或【 y i ∗ E i > t o l e r y_i*E_i>toler yi∗Ei>toler 且 α i > C \alpha_i>C αi>C】
条件中
y i ∗ E i = y i ∗ ( g ( x i ) – y i ) = y i ∗ g ( x i ) – y i 2 y_i*E_i = y_i*( g(x_i) – y_i) = y_i* g(x_i) – y_i^2 yi∗Ei=yi∗(g(xi)–yi)=yi∗g(xi)–yi2
由于
y
i
=
±
1
y_i=\pm1
yi=±1,有
y
i
2
=
1
y_i^2 =1
yi2=1
最后,我们将代码中的原条件化简成
- 【 y i ∗ g ( x i ) < 1 − t o l e r y_i*g(x_i) < 1-toler yi∗g(xi)<1−toler 且 α i < C \alpha_i<C αi<C】 或【 y i ∗ g ( x i ) > 1 + t o l e r y_i*g(x_i)>1+toler yi∗g(xi)>1+toler 且 α i > C \alpha_i>C αi>C】
此时重新对比我们所说的的KKT条件
0 < a l p h a [ i ] < C ⟺ y i ∗ g ( x i ) = 1 0<alpha[i]<C \Longleftrightarrow yi*g(xi) = 1 0<alpha[i]<C⟺yi∗g(xi)=1
我们要找的就是违背这个条件的 α \alpha α与此同时判断这个 α \alpha α值不值得更新
可以看出原条件中“或”字前方的方框是属于"小于违背"的情况,后面的方括号是属于"大于违背"的情况。在前面的方括号里我们之所以只需要限定
α
i
<
C
\alpha_i <C
αi<C的条件,而不需要它大于0,是因为我们同时在判断这个alpha值不值得更新。
所谓更新就是要从违背KKT条件的状态更新到不违背KKT的状态。
对于“小于违背”的情况下,由于
g
(
x
i
)
=
∑
j
=
1
N
α
j
y
j
K
(
x
i
,
x
j
)
+
b
g(x_i) =\sum_{j=1}^N\alpha_jy_jK(x_i,x_j)+b
g(xi)=j=1∑NαjyjK(xi,xj)+b
也就是,
α
i
\alpha_i
αi越大
g
(
x
i
)
g(x_i)
g(xi)越大。在这里我们要更新到不违背KKT的状态,就是需要g(xi)越大。所以判断这个
α
\alpha
α值不值得更新我们只需要判断
α
\alpha
α是否足够小,有继续增大的空间,但是又不能增大到等于C。故有
α
i
<
C
\alpha_i<C
αi<C。而不需要判断
α
i
\alpha_i
αi是否大于0。(其实
α
i
\alpha_i
αi应该肯定会大于等于0,由于
α
\boldsymbol\alpha
α初始化时为全0向量,而后面又有L、H这个机制来保证
α
i
\alpha_i
αi的范围)
第二个判断条件
if eta >= 0: print "eta>=0"; continue
这个判断条件按原书上写
如果eta为0,那么计算新的alpha[j]就比较麻烦了,这里我们就不对此进行详细的介绍
判断
e
t
a
>
=
0
eta>=0
eta>=0,个人感觉其实只需要判断
e
t
a
=
0
eta = 0
eta=0就可以了,因为eta是一个完全平方式
(7.107)
η
=
K
11
+
K
22
−
2
K
12
=
∥
Φ
(
x
1
)
−
Φ
(
x
2
)
∥
2
\eta = K_{11}+K_{22}-2K_{12} =\left \| \Phi(x_1)-\Phi(x_2) \right \|^2 \tag{7.107}
η=K11+K22−2K12=∥Φ(x1)−Φ(x2)∥2(7.107)
此处数学式子中的
η
\eta
η为李航书中的式子与代码的eta正好是相反数。
判断eta等于0跳出循环的原因是,在后面更新
α
\alpha
α的时候,有公式
α
2
n
e
w
,
u
n
c
=
α
2
o
l
d
+
y
2
(
E
1
−
E
2
)
η
\alpha_2^{new,unc}=\alpha_2^{old}+\frac{y_2(E_1-E_2)}{\eta}
α2new,unc=α2old+ηy2(E1−E2)
eta作为分母,不能等于0。
最后,感谢这篇博客https://blog.csdn.net/lyl771857509/article/details/79433184
,第一个判断条件主要是看这篇博客的注释得到的启发,另外这是博主的第一篇正式博客,由于博主水平所限,文章中难免有错误和不当之处,欢迎读者给予批评指正,也欢迎大家在评论区讨论提问题