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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4877|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:
, G7 e0 V5 B5 R  n学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。
  i2 Q8 z0 c, K" z' U6 q/ {% F. D9 x假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:
* i/ B2 E' J) ]% E2 u
" z% W+ g0 u: w1 G" CSVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:
+ q9 Y1 H7 j0 z3 Q* p" X/ {/ H, q* F( [4 O( Y. q9 ^( A8 L& e" e
上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。
' Z! Y3 `  v, ?0 k* Q2 U' F# b  s) U5 |那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。  |/ |2 W, h/ u' X: P, y
第一步:实现传统的 SMO 算法
; N* @* g* v9 Z4 W; r现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:/ @6 O# R: m9 U* e, e
procedure takeStep(i1,i2)
2 D! z1 a2 k3 r; e  y: L, n( M0 v" y" B if (i1 == i2) return 0
; n  l) W* ^+ n7 B/ T  E+ A: ]) Q alph1 = Lagrange multiplier for i12 w" l' D% _* g( G4 T, P) ~# V

) J" ^$ z: O* e% z: I: ~y1 = target[i1]& s; _0 w/ x. p. K' Q
8 o+ t. @; t$ I5 w0 _; A$ Q
E1 = SVM output on point[i1] – y1 (check in error cache)
8 S' }4 \# b, W- H. R6 S3 Y
% b, _; ^- S6 z. B3 p  z# Ss = y1*y2
1 E" U9 V' E" E0 C4 S; |& U  }4 c; M( \  d
Compute L, H via equations (13) and (14)
+ _, R9 j6 {7 B8 E& m: |
/ T+ F. P* A3 Tif (L == H), B0 r1 Q0 R, N. Z( k% Z) n3 X8 V3 @3 }
5 R& l& g* Q- B' h7 l. D7 J+ g- P
return 0* V, z- d5 [4 |" l

/ g7 _0 u3 c' [8 T9 jk11 = kernel(point[i1],point[i1])) V, h3 k7 o3 R, v  J3 ~

' D- o# E+ R- u7 K/ C* s7 z' xk12 = kernel(point[i1],point[i2])
0 N" d0 t" c4 z9 E2 O
% R2 [3 N: ?1 j6 Tk22 = kernel(point[i2],point[i2])& K( h1 S/ q' Q4 d# m+ y0 d+ i
5 `4 A/ k4 s5 F; j
eta = k11+k22-2*k12
  C3 ]( C4 z4 E  p
& h" o2 D6 y( C  k4 s+ B$ J+ _if (eta > 0)( w( C% D& q3 q: M$ l# X( F9 |( L
" t# T# s& d5 n: d' r" X2 ^
{/ ?- i1 U' b# G' ~9 Q& n! |* K, M* g, d

5 b; q6 @* x7 \; j% O$ Z+ fa2 = alph2 + y2*(E1-E2)/eta; l0 z3 w0 C9 w7 H3 A$ E
) L; w% v  R+ ?* r& g
if (a2 < L) a2 = L
9 T. I. p* U: {# N1 o; d* K0 S) w6 L6 S* {3 f" y3 Q
else if (a2 > H) a2 = H
' f  K: T7 H. b- Y: Y! o7 s  w7 Z- P' u3 H$ b
}% }. d) B$ n+ E$ r# \8 b; u1 X" T
/ r! h+ D& e$ I7 x
else
  h1 g& m; f: b/ q3 @- c! }+ ?* `0 ]6 f  {- y. I
{5 ^8 v# B: W5 \9 L* C

5 m; h/ N+ Z: |% pLobj = objective function at a2=L- J1 `' S+ ^$ a# D2 W0 F

) q7 R; O  s/ g) zHobj = objective function at a2=H
6 f7 Y5 I$ K! g: D1 j! E
# ?7 D' `# ?5 {1 E* m6 Y% vif (Lobj < Hobj-eps)% _" N. ?6 \  H* \% U+ m
- t( ^/ J) y2 X, }5 I0 E
a2 = L
0 z. H4 O3 S, {& ]/ O, H( ?& V! [# q1 \6 u, @# A' `2 W
else if (Lobj > Hobj+eps)3 t) n7 R+ g7 O" C
9 a! h2 q- O! o
a2 = H! p( L2 a; u( f$ U5 n

' n* r% _4 f5 l) A0 E: relse8 G1 ^+ Q) A* u" }: ^0 I2 D$ J

2 D3 _4 \. w. u8 oa2 = alph2+ T9 b& c( S" g& D* x& i
* H' N8 o# W5 |- U; J/ e
}* I% G5 \+ [8 O
4 \. {3 [  R% O  g
if (|a2-alph2| < eps*(a2+alph2+eps))/ W) q1 S$ Y1 c8 B

0 i0 y  {% Y# H0 z8 [2 Preturn 07 T% f' O* N: ?3 W1 \+ R# @/ ?$ f7 j$ Y2 `& D
/ _2 d8 j, P7 W. o3 D( \
a1 = alph1+s*(alph2-a2)
' A) u: o& Y0 c) D+ ?
8 g- {& W6 y1 o" w9 H& A5 c$ ?Update threshold to reflect change in Lagrange multipliers
" j8 t& j' V9 P- o4 T+ x8 J0 r. K
& t" W+ b# E2 j! E" L, _( m, x% [Update weight vector to reflect change in a1 & a2, if SVM is linear4 b) G! c# s9 ^. S( ~

3 Z" U8 N) Q- S* I0 j( yUpdate error cache using new Lagrange multipliers' d+ w; g5 Z. s2 {5 ]3 g

5 O  H9 Q3 k1 {# T/ o7 @Store a1 in the alpha array( \) T: \7 @# m$ E0 b
( ?* X4 c9 u$ V, o; {
Store a2 in the alpha array
2 I+ h7 }3 U; @0 A& n; n+ u% Y7 I1 c! ]( y5 _; D( ^* X+ e
return 1
& _5 g, D7 M6 ], i7 x2 p$ sendprocedure
7 x! [" K( O8 p) U9 y# o% O* i核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。, E7 n+ N) b6 |9 a5 f
第二步:实现核函数缓存! D, s) o9 D8 r2 `
观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。- a4 q. b" o1 }* j
样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。+ a4 z/ ^; A. i
有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。
/ L- V8 A3 V' b6 _2 k7 u9 a第三步:优化误差值求解
( z- f8 j" S$ B; ^$ a注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:" I/ X2 M% r8 E4 d5 [
E(i) = f(xi) - yi
1 N4 y- z, w* `9 H! }这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。" P" }) d" \- X" M8 l* J8 ]
platt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:6 a- p5 L: E* V
$ j+ g: S- L' O! b6 t
也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:; H5 p; u5 d$ k6 J# [
E(j) = g(xj) + b - yj
0 @  Q2 h3 H' q: ?) Q5 n所以最好的办法是对 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 的偏导数就行了,具体代码类似:* w4 |) ]" F% e5 q
double Kik = kernel(i, k);
7 K" j' [  z: X1 ^* a9 A' N. Hdouble Kjk = kernel(j, k);% O) M& ~' Y" I2 O/ {1 y
G[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];
5 o4 H( J2 Y* _; b) J( {( u把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。: c, o! S; v3 ~. A1 {& Z: Q
这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。0 h; M: ~  G# f. D" Z- V% |
第四步:实现冷热数据分离1 b8 V9 q' X- R5 T1 @6 @
Platt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。/ O: a- @- D' J3 t7 H0 b
那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。
$ U# g- q5 M5 |随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。
: U2 d2 t- B* T( R. r; G第五步:支持 Ensemble
% h* Q: s1 L! Z; r) t大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。
: t) U/ L& Y* a5 |最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。
# y0 G  A" d- g& W0 ]6 Y这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。, x2 v% x1 P: V, S5 `) `+ t# N2 n9 d1 H
实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。1 G' r# _8 n) b, m4 C( Y
第六步:继续优化核函数计算% f1 u) x2 E. s9 r6 E$ Q5 y/ w, `5 `3 Z
核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。% H! t# I) X* L2 b2 |! F; g4 `3 H. P
由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。
) C4 {4 m1 C3 G4 Z针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。9 @$ A# Z8 p9 w* x# i; g/ h/ x, v
第七步:支持稀疏向量和非稀疏向量
% R) e& t. c- H对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。3 [% g0 A$ n6 p4 i6 l# x
但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。
1 P$ k$ T" i/ f5 ?9 X' |2 ^非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。1 `* a% b4 ]: a% \9 l& q
所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。0 `8 v; d& M8 ~" _! [( B' F8 N) p/ i
第八步:针对线性核进行优化" T: u; }; q  M+ S  {+ c1 [
传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:
: l' ?+ H1 r* t+ U1 @K(xi, xj) = xi . xj9 J3 j: I' s1 ?- W, |
还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。
9 `! g4 A7 \1 w9 Z同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。+ F+ f  U, E$ ]; u
但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。
. C) e1 o; @7 v5 s$ R) m$ l或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。- b; }. Z+ x: ]: P* E1 {- L
后话
( n4 ^: A3 H5 u( {! w上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。
$ {& {0 G. @) ~$ H0 v! Z1 `上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。
* h8 |1 m, C: J  \1 d$ D% y% |
6 s# W# Y$ D7 E  e* Y$ b来源:http://www.yidianzixun.com/article/0Lv0UIiC
' H4 \# A' p$ c4 j4 v免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-1-17 23:48 , Processed in 0.040720 second(s), 24 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2025 Discuz! Team.

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