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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4807|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:
- E7 A; p) g% d$ s0 m; v4 [5 ?学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。
& f, x' p! U9 H6 A: \5 V" K1 }0 B假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:) s( {# U& B$ ~3 U, I/ M3 V% R
. b: j- D# p8 N/ g2 C2 u3 ^4 Y
SVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:
5 b' _. I& o( ~! Z2 Z" S0 r- \3 k. G: h& N- g! s. {! N( v$ L$ A/ t
上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。
0 B  }) u! b% w6 T' r, t那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。3 l# Q, s0 k2 Q* }5 e- U9 X$ z
第一步:实现传统的 SMO 算法
- {7 Q  O) H/ e/ V" v4 B现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:
+ ?$ i$ p6 j/ z0 s0 G# s; _procedure takeStep(i1,i2): X/ {; G4 i2 g  o) f6 j
if (i1 == i2) return 0
. a+ I, i  K! ]1 j alph1 = Lagrange multiplier for i1
$ Q& m2 D, E6 l
& w; y: Q$ D4 S5 I7 w& By1 = target[i1]3 I6 }, ?2 `! b% T% E+ M' m
& s6 l( l3 @, z0 Y6 C1 O& o# G
E1 = SVM output on point[i1] – y1 (check in error cache)
+ g% y0 h1 S% n- v; l( o* g
+ |4 m, u! f- H# |s = y1*y2' E& T( q/ ~2 o: d; N8 W% t% h
$ @# x0 h" P( A: `( E
Compute L, H via equations (13) and (14)$ ~: u! c! p' V- _6 A0 r* |
; \" \9 F6 }+ N2 {( u+ R
if (L == H)
% L, T3 A  G/ R/ A2 J1 c) d" _* ]* {7 Q8 M: V
return 09 c# l/ S% u6 L9 H- I

6 K1 N$ Z0 B$ d4 \4 G! @  k' F! @k11 = kernel(point[i1],point[i1])
, S! n6 O- q6 |* _" C+ i
4 c) U' A* c, g; P" P1 f0 gk12 = kernel(point[i1],point[i2])" T* f  {, h+ f# R+ D0 H$ z3 h

$ _/ l" D% _! s; E5 Vk22 = kernel(point[i2],point[i2])
7 H; g! M& b" A- r9 n( q8 P7 q* I0 k. d
eta = k11+k22-2*k12
0 x# Q9 R# j! l+ k
$ {0 ]7 L; T8 _4 m1 Tif (eta > 0)1 i  Y* G/ ~0 R8 B

4 O) e  n7 d( h{2 n  {/ t: I4 |! ]0 q: B
8 b/ y" O; V! }
a2 = alph2 + y2*(E1-E2)/eta
% W' }) T- H5 |1 H5 E  M* ^. {0 U) d9 R( w) a% g
if (a2 < L) a2 = L
+ [0 F1 _; E: M5 a/ k! ~! v& D) c7 I& Z4 L6 e9 |' @. \
else if (a2 > H) a2 = H
2 k3 i% D% z6 r. w! K; V% n! ]1 S, d
}
1 M# u7 j# a0 H5 b7 S% J
8 H- W+ D: n# X7 v/ r% Zelse
. ?, z% l7 e1 f! `
3 h; [7 J/ L' V{
3 t* _9 y8 z6 O* W7 i) U+ _& ]5 b( Y- c3 P
Lobj = objective function at a2=L( W) C1 M6 t  b0 p" \: k) L

. |9 ]( L/ W; t6 p0 K; hHobj = objective function at a2=H6 Y/ x; S; V5 D$ m# G  r
1 p  U. e( r" D( B( Q% C
if (Lobj < Hobj-eps); j0 G& Z, [. U" d
' F/ y: ~" Z% Y2 M& M& W$ e+ T. _
a2 = L9 Y; l8 t! L9 f$ d! ?+ H; k

) |9 B; Z& v8 g# \5 o3 Q, relse if (Lobj > Hobj+eps)8 X- E7 W% }$ d7 Q( B( K" s

$ X" m6 G# B( P7 ta2 = H& t% v% c) \, |  i; g& u: l
, H0 E4 d" i0 q, I
else
) w) Y2 u2 `6 o1 \
2 G4 Y+ J+ ~# J" b' y* W6 ^  ca2 = alph26 W+ N9 B( @( y
) o/ i5 b% Z3 H3 A
}
( ~, J/ s9 e) H% F8 }  Y, l
9 y5 \( `; Z( J" [) \# Wif (|a2-alph2| < eps*(a2+alph2+eps))
0 G$ G8 {0 o$ t7 l  P% ?' ~) L9 i; }* ]6 U
return 0
8 g+ o( F9 f* L$ C$ r8 Q/ [8 M
0 z' v7 h3 m3 `; Q. wa1 = alph1+s*(alph2-a2)
* }$ j1 p/ c) }8 \5 m8 w3 s" a
8 u5 w$ P7 _( ~, g5 R+ X& `' lUpdate threshold to reflect change in Lagrange multipliers
! P, q% l! Y# @# E7 \/ t3 `" F7 E2 _+ C% f+ y6 O5 l9 }& ]6 q
Update weight vector to reflect change in a1 & a2, if SVM is linear- }  c3 ~  ~& ^5 O1 [* @
) H, y' K0 A2 a8 b+ V
Update error cache using new Lagrange multipliers
; c9 d8 y; [& }( D
! O% Q; ?7 Z9 z7 r, a$ BStore a1 in the alpha array
6 p" n7 l5 H0 T$ S$ g3 t  c7 j2 F$ F1 Y6 K) r( c
Store a2 in the alpha array, e( A6 l; h) A0 x% Q6 n% o

! p; Y9 P7 o: R8 freturn 1
  _% [" E; |& Y/ Qendprocedure
0 m1 p/ `! |% \0 ]9 B5 J0 D) v核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。
9 ?% \1 e/ {, R# }0 ?  T4 ~/ U+ J0 ~第二步:实现核函数缓存' b* ]  _' O1 }9 A
观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。0 V5 ]; k! t% @: J
样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。/ ~. f! S1 F3 c+ k; N) w% r1 G. m
有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。
0 N% a* s+ z) `# o第三步:优化误差值求解
" e' i0 Z0 X- l- l- x+ f注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:
. ~( v& \( D+ |; S( SE(i) = f(xi) - yi
5 j4 L, w1 f* G6 {9 K这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。8 J, R0 w5 G; M0 \1 n) x; g
platt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:/ m, ^9 G$ H7 P% V5 A  n
  i, o1 Q- O/ D* Z  o
也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:
5 y7 I' C- G! a2 D3 J" EE(j) = g(xj) + b - yj" I4 x1 _7 M/ M3 P7 {
所以最好的办法是对 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 的偏导数就行了,具体代码类似:# U5 j( K' Q% ~3 ?  y. E
double Kik = kernel(i, k);0 ^2 f% v& n5 p# {/ x
double Kjk = kernel(j, k);
) u) I& G4 l" A5 e) ~1 S% @1 @G[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];
, R2 l- l9 n1 h把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。3 i5 k2 \4 O& _1 v# w
这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。* y* _. D0 `- s, @; K
第四步:实现冷热数据分离  w' W- i) x+ `$ l* L
Platt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。
+ l! [8 Q5 z$ E2 t, q: ~. e  G那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。
) \) U5 a) Z7 s( p随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。
1 D. c  \: l: N7 ]) U; W第五步:支持 Ensemble
8 x9 `9 |( t2 ]) r5 O% ]大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。. C5 T+ a3 \( }2 U
最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。3 [3 {1 L" Y8 U" J
这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。1 Z; w* ^2 k- y6 o+ a: _8 ?/ O( _
实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。$ p3 J$ c) O1 q' [& W
第六步:继续优化核函数计算' Y6 r* {$ }" \4 }! ~2 f* L
核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。% X& ~: x9 Q& J! I/ d; G
由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。. P6 k1 q& Q; q% V2 Z
针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。( n( U1 b0 E8 y" e0 T  l) h
第七步:支持稀疏向量和非稀疏向量" t1 p; n4 Y8 Q1 T
对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。4 w  }7 q! o- B5 X- c! {
但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。
6 p: u( M2 x, L( b) f非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。
$ m5 x" O# {6 x# ]5 `所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。/ u7 M' i* P  U- \
第八步:针对线性核进行优化3 ]4 Z7 |5 \0 G, W/ E
传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:
% h$ R/ G6 W, c% L1 sK(xi, xj) = xi . xj
+ r& [$ m" k: @* i# i8 M" s" ^还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。# Q. b% V, O: j  H- A0 L8 G. L# D
同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。& r: p* E" {9 q' @* E& e$ b
但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。! T0 F5 w: K# ?4 E0 Q- r
或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。
; h: ]. a7 E- N$ |# G1 p% e5 \, w后话7 h0 p0 H5 a" ]4 ]( H3 G- p3 y
上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。
( }; e4 S  Z# j; e2 ?( e  m: d上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。" r6 j- \/ H. K7 q5 y7 i  E
2 [( O" \- @# [  V- R  d- H
来源:http://www.yidianzixun.com/article/0Lv0UIiC
  g. O1 ]8 d8 P- a- Z4 e免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

关闭

站长推荐上一条 /6 下一条

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

GMT+8, 2025-7-21 10:52 , Processed in 0.046680 second(s), 24 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2025 Discuz! Team.

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