京东6.18大促主会场领京享红包更优惠

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 5000|回复: 0

如何学习SVM(支持向量机)以及改进实现SVM算法程序

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:
( @/ `% W: D, v6 B1 y" L学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。
2 s6 q) a0 b, F4 g* j% q假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:
' L& k! |% B, W/ j  O: v! w3 j% M6 K9 Z( d# s+ W, v" ~2 F3 E2 Y  ~7 _
SVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:
7 k, u: t! @& e) Q- D2 Y2 f* R( c
上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。0 k& S% m2 x2 G6 K& ]2 b" k
那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。
9 b  F! R% q5 f/ E6 J5 b5 w2 Q( w# L$ y第一步:实现传统的 SMO 算法
5 a* ^  J7 W/ _' u现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:
7 ^1 I' ]* W- K4 z% _6 m& Fprocedure takeStep(i1,i2)
2 m% Z$ K& T1 _  c. @ if (i1 == i2) return 0
8 @, G: {' t* C2 I  e% n4 P alph1 = Lagrange multiplier for i1
1 _( ^# l, B/ \$ m+ t# ^- a
2 J& D: s2 |( Cy1 = target[i1]
: F+ ~$ O8 M8 v* k: V8 J- E( |. ?( G. M( m: O
E1 = SVM output on point[i1] – y1 (check in error cache)
: i/ o% K6 T: k) L6 \* h6 J+ N/ y# v( V
s = y1*y2
7 `2 r2 L4 X6 L; H- P2 y9 a% i0 v2 O$ }3 h2 X
Compute L, H via equations (13) and (14)
7 c  E' e) C( z7 L' H/ J% U. i. J! |. Q
if (L == H)9 `( h5 V+ {7 B0 m# p$ p
( p' Z6 x3 O& Y' k: Z
return 0
7 Y  T; @" ~2 `( n' j# P9 y& q; L+ E1 _
k11 = kernel(point[i1],point[i1]); i8 t7 K8 u$ n' ]! v

' o3 _" J, [+ s, E4 N3 S5 o. rk12 = kernel(point[i1],point[i2])
# z$ }2 r% p4 H1 Z9 K6 z" w& e! F# @
k22 = kernel(point[i2],point[i2])
4 f4 t7 y( H' H. D; ^/ z7 u9 W' b
1 G/ `* W$ `0 C1 P( ceta = k11+k22-2*k12# _" M' Q; c% e

* a# c; n1 I" }( T- Lif (eta > 0)0 A% Q4 D4 p" B  ~4 t$ O

! }: g$ P& b4 d( U5 i{
; `4 O, j: o$ i6 C& F! K) ]0 }+ a& U2 \. M: {- q: [
a2 = alph2 + y2*(E1-E2)/eta
) G. s' y3 {' t& ?5 F8 F7 R( v! b6 E6 s+ N  O' z
if (a2 < L) a2 = L9 x& p) c6 I) X
& z. M7 P# D" H* ^: z
else if (a2 > H) a2 = H: D$ K1 V: r& p1 y# O) A

2 @% X9 I( Y2 P5 N}# t/ F( ?9 ]* U" t/ K. a: t9 y0 T

) O8 P) \  F! K  telse
6 _: q  s5 b# m, a* b. U6 G4 D  G( W% M$ i
{
9 w1 m/ e' `+ t) }3 [- X6 C! }9 }* X! L( P; A6 K7 L( p6 G! \" ?8 O' M
Lobj = objective function at a2=L
0 P$ B1 f& k: @: h
1 [9 K' `. q6 c1 z& g1 T) o7 cHobj = objective function at a2=H& g" q" Q5 h. ]& \' T8 {8 I5 h  g

1 \& l& b& S( K! @- `3 Iif (Lobj < Hobj-eps)
/ B' ?) |0 s  ^* b+ A# `- z
3 h% a1 i$ X# f6 `a2 = L* C8 t/ _' l1 v) s  e1 _

7 y! k3 w7 \. B$ v! oelse if (Lobj > Hobj+eps)
. ?9 ^) H" _! m0 Z& i: F3 k4 C1 `1 A. M7 C! y9 P; @
a2 = H- _$ W: M& I7 W( d
2 j( F4 }. h) Y9 Z2 p9 S: P
else
9 g" _1 X3 y( k, e! W
& s6 w' P3 k1 {6 Na2 = alph2
$ r. w$ S2 K( i' C
; S9 H* N) G# [/ m* ]/ J* v5 f3 G}- \5 I; H, l) `( ?5 i
6 _* e. d3 A& ]4 i2 N
if (|a2-alph2| < eps*(a2+alph2+eps))7 e( _( @0 |, t' \7 t9 N

1 A1 f& o- \' O- X' t" ~1 @return 0
7 g) P- d+ Z; w" T3 T" o+ b! A! Y- i/ Q* p& d3 e' N8 ?
a1 = alph1+s*(alph2-a2)
( ^4 V5 A! y8 p$ J# W7 H% X9 @
4 v& D' R0 X. j% n6 y- V% ~Update threshold to reflect change in Lagrange multipliers) @+ d8 V# F, N& ^+ n5 B

! y! e1 o, L7 N% q( ^Update weight vector to reflect change in a1 & a2, if SVM is linear0 s6 Y' A- M2 f/ h0 r; J( H2 x- B
2 N# }* \$ _+ ^
Update error cache using new Lagrange multipliers
  Y/ f( U8 g. [
- D6 @, A  G3 ?5 |! m+ {Store a1 in the alpha array5 @' @. _! ]: P, s' \" d1 }

! s4 w( W% c' ^* mStore a2 in the alpha array8 F" I; W6 r3 |2 ]' z9 t

3 ?+ X: x, B7 [& }9 v7 U4 e$ Lreturn 1! K& r" Q9 q8 u) e' F4 o
endprocedure! ^' {$ I# Y9 K
核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。
% h8 D! O4 o0 H  U. H第二步:实现核函数缓存' `- l8 Q* M; J2 }
观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。  B$ o5 i( ^9 I/ p* v
样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。# K- t+ q3 a+ p8 P
有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。
- E' v! U, W! J  o# s- w第三步:优化误差值求解
5 I3 y+ e* C) W8 n' ~% Z  F, J2 m注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:
9 R" r% t9 x4 h) F1 q3 w) w" WE(i) = f(xi) - yi
1 v3 d' `9 T8 p( J( e这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。$ \$ G1 e  ^1 D6 c3 |: ]9 m5 k
platt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:5 \/ X3 a8 g' E. a# A. \2 A
9 H# Y% \- E# I$ ~8 b5 }" P6 r6 o1 J
也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:0 ^! X2 }8 B2 a# Y% u. g' _
E(j) = g(xj) + b - yj' W0 ?+ z  R- _* [) H& c
所以最好的办法是对 g(x) 进行缓存,platt 的方法里因为所有 alpha 值初始化成了 0,所以 g(x) 一开始就可以全部设置成 0,稍微观察一下 g(x) 的公式,你就会发现,因为去掉了 b 的干扰,而每次 SMO 迭代更新 ai, aj 参数时,这两个值都是线性变化的,所以我们可以给 g(x) 求关于 a 的偏导,假设 ai,aj 变化了步长 delta,那么所有样本对应的 g(x) 加上 delta 乘以针对 ai, aj 的偏导数就行了,具体代码类似:) j2 z! H# ~! S. M1 m8 L" I
double Kik = kernel(i, k);
3 M( K5 Q9 N% }) T$ v( g, cdouble Kjk = kernel(j, k);
2 v9 x+ R5 H) }2 S% c" R8 q& CG[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];
9 a! Y/ g, H5 G/ V* C) D把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。
" q7 E4 |5 _, R, ~1 H$ R2 m这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。$ [+ J6 B, O0 D/ w
第四步:实现冷热数据分离
+ T% D$ d0 B( a. \* h# L! bPlatt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。
6 B6 Z! P9 T  F那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。
: ^$ y4 _, h# {" E: F随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。/ o; \& O! {  `9 u% l
第五步:支持 Ensemble
8 Y; F) d# k+ @# }+ R大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。
. z+ Y  v, {* d3 J" l2 m最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。
" U. l: o* k/ w' B+ E1 e这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。
- r$ s8 _/ r0 |* D+ C# n实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。
2 k7 O! I: X$ x1 e1 Q第六步:继续优化核函数计算
: Y9 M! i  R% x8 I2 S核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。, f: S% P4 _5 N9 Q; T2 o0 F+ ^
由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。9 U2 ]2 ?7 j: J( c3 K$ L, U
针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。% v4 u# v+ [0 E4 G" M, _
第七步:支持稀疏向量和非稀疏向量
% l4 r' w$ S- F+ ]5 [( f3 ^对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。
% J# B# P0 O, Z% |6 `$ S但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。
( ^3 T% b* a$ [" y: d$ F2 T非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。
3 X( k: Y4 `  }0 a* P0 I所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。0 J/ ?0 _5 ~3 u' B8 q$ y: V- w
第八步:针对线性核进行优化2 B8 ]1 ~. `. [
传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:
6 w8 H" |1 X5 h, GK(xi, xj) = xi . xj
4 Q& r- j, a: r1 i$ t, V) U还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。/ f# u/ j% D0 c) U
同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。
) |# i2 H: ]# }% l; K! t! Q- d) I0 ?但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。2 x2 l: g4 b& F2 o+ l
或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。3 v& L% H, Q8 {/ d
后话
3 u  G' h  @" C; Z  }6 C上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。
; J) u5 ~  n# [4 {7 x4 j上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。4 r% \. T. |8 M) U3 i+ f; @/ }1 H, S

7 [) l0 l) Q7 O* v" \来源:http://www.yidianzixun.com/article/0Lv0UIiC
5 `$ k' s! @9 g免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

帖子地址: 

梦想之都-俊月星空 优酷自频道欢迎您 http://i.youku.com/zhaojun917
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|梦想之都-俊月星空 ( 粤ICP备18056059号 )|网站地图

GMT+8, 2026-7-17 12:18 , Processed in 0.039123 second(s), 24 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表