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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4906|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:
' M& p5 O1 ?8 u* M' M; X学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。
: A! i; t* k! M/ x  p假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:
! t/ z6 W( H: K* K$ @
! {* g: a- p9 y4 H: ^: i+ |SVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:
" q: f/ h, R7 ]; c
  B) F) q* }2 G上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。
4 [( s% G/ b) q& t那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。
' s7 b( ~6 F# i$ V7 R第一步:实现传统的 SMO 算法
; H7 _* X( r7 X  ~1 l5 T现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:* B5 C: L2 u! W) l8 c) N+ [" y/ u
procedure takeStep(i1,i2)
1 I1 I1 `7 y- Y4 N4 s: p4 x if (i1 == i2) return 0
$ R# t% [) _/ V! a$ y) R alph1 = Lagrange multiplier for i1
9 g  x( S; q% t( ]
5 m, c# U4 }+ ?, C3 P; Ty1 = target[i1]6 [. d( O, ?4 E3 o
" |( a, V; M; k* T, l4 V
E1 = SVM output on point[i1] – y1 (check in error cache)
. x8 A' H" u# C5 c+ Q# \6 [) }' r; D) }
4 f6 m+ V1 p6 Zs = y1*y2& l; `5 K: \2 j- V# m2 o

$ O4 z- X  M! }# A+ y% T' C7 g) C  o' B* `7 DCompute L, H via equations (13) and (14)
0 @2 r8 P7 x1 c4 s& S$ G' Y) @0 `2 n# n1 {8 y
if (L == H)8 Q; S% A. I0 L) ?4 l
3 R( u# U' ~2 i9 n! B7 t
return 0
  ^. p( {* O( ?. u
" X  E+ z+ {, W3 G0 ~  \k11 = kernel(point[i1],point[i1])" h- }4 ^1 h" N7 G- ]

7 j9 [3 l' [+ }" Gk12 = kernel(point[i1],point[i2])
' ~; H3 J" J( J% B
) D6 {; \8 U% f, Z' R( Z$ ?( u8 ]k22 = kernel(point[i2],point[i2])
% `! B' {1 \  o3 H6 a+ v% A4 w6 d) B' u8 E/ b
eta = k11+k22-2*k12
5 B; z+ B( @% \1 l  c* |% a/ b+ e% w0 o
if (eta > 0)3 J! `6 X/ T9 _

- ^# |  h9 f  [0 B6 @{/ @" \! W7 S  F  H0 O5 i

/ _9 L$ Q0 k& ~- ma2 = alph2 + y2*(E1-E2)/eta
0 c, t3 k, v0 n$ X( Z* A7 y" j" [7 l/ L) \
if (a2 < L) a2 = L7 A/ `0 p2 l" j9 s4 {
  o* X  r- w& F" j
else if (a2 > H) a2 = H% ~* F# B! F) i# A4 N
8 E1 S) Z: z8 k' x2 |- _4 ~5 @$ z
}
4 H7 G8 _% `. q5 V9 T8 A6 S7 ]
$ u  _5 I% C# Z6 Uelse4 ^: q) u3 o- T2 E! t2 Z
( B: g4 A6 |, O
{% `/ N/ c5 r. w5 C' K& y# @
- p4 x6 C+ `' y* w1 b2 L/ G
Lobj = objective function at a2=L
8 ], A2 O# }, G" R  w% S8 l
6 U- H8 h! F+ @Hobj = objective function at a2=H
0 q- {/ G5 e, Y: x& M
+ W; _9 ?# [* @* Y* u  hif (Lobj < Hobj-eps)
5 ~9 O! y  e# T, ]& L4 i% O- v+ }
! H( l. s! B1 N2 l, g' S& Ta2 = L3 l0 E$ Q# L! h" }2 Y, X
8 y+ v* h' p0 i
else if (Lobj > Hobj+eps)" h7 i% P: p% c$ ^$ A0 ?+ j8 `

* T8 I# y& Z4 p! `! b) x; Aa2 = H. [5 B3 g) a$ ?& s+ q! p" Z1 x& `

+ m, m8 ~# \: {" B7 u5 Uelse
. h/ U) ]% s; A  M, n
2 I1 l' [9 u$ E4 n% t3 ?a2 = alph2
5 G* c: A5 Q8 d: y2 {
' Z- P! @5 S; k- l! W( ~}% j7 _) w9 u$ l" O3 T: l2 B
8 u+ |. M1 e; Z" _2 T$ E7 d
if (|a2-alph2| < eps*(a2+alph2+eps)), ^' q0 z( F' C6 h% }0 P8 b5 |" ~
7 J( c6 L7 q  P  v
return 04 f7 J% k( q. F4 i8 n, n
# D" x  Y) k: q- \) J3 C* u
a1 = alph1+s*(alph2-a2)
: ~7 n- V; [) e5 F6 g
7 J4 T; p% m1 {; PUpdate threshold to reflect change in Lagrange multipliers
7 q/ c2 h4 Z0 h: u' f# @3 }( K4 I0 ^5 G: o
Update weight vector to reflect change in a1 & a2, if SVM is linear
7 x+ |" Y- \7 b3 v  q# T# }  t' K) r
Update error cache using new Lagrange multipliers7 h, Q; O% D" u: D3 F/ |

' C; Y" S8 ]8 u6 T: UStore a1 in the alpha array1 |' ?  g3 X8 z6 C( r/ U8 ?( [; |

9 V% B% Z- |; p, yStore a2 in the alpha array
/ ?" b, [. L7 c" @8 s. P" X& z6 X; s* q  e9 k1 h
return 1
5 x7 l; I, l; K( s& {% fendprocedure* i" ?5 U" V4 ?; p& |: D
核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。9 Z6 C% C+ I4 g
第二步:实现核函数缓存0 z& o2 |  _" y8 c
观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。8 g! A& X, n& n
样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。. W* d/ l2 s1 i) e
有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。
: [) L, s8 d1 i2 [  n! Y. y第三步:优化误差值求解; [; b2 l5 M  X2 {# e. S% B
注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:
0 m$ z: y( b# Y: A# ~E(i) = f(xi) - yi; w% |# f9 z. [3 `# |% O! [# K* L
这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。
% @( ?! @  g' q6 X+ q+ O4 tplatt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:
$ ~( c( b' z- Q6 ~0 @+ P
! `/ y4 w7 J' v. l也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:
2 d- ^2 o% P% H+ ME(j) = g(xj) + b - yj
% G: ]0 ~( B" e所以最好的办法是对 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 的偏导数就行了,具体代码类似:
9 x" ]" s& b4 @double Kik = kernel(i, k);' {, `- u6 A( g' l8 h7 @, D
double Kjk = kernel(j, k);7 z7 g3 Y& o, V) S6 Y- ?. N8 j
G[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];
/ \6 a$ x" n' L. T: E把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。% \  s0 i+ E$ Q7 t: W; Y
这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。% c$ h$ G6 N1 x( U
第四步:实现冷热数据分离
* [! N6 z  j+ B- Y7 N1 f' J! pPlatt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。
* \4 {8 i8 e5 o% Z% C/ `) t- t$ J那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。- l* l/ |: b& g* f6 Z2 B: n3 u
随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。+ u3 y2 W+ O. z
第五步:支持 Ensemble
. [: b% j+ z; |2 Z; i2 b大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。
' E& c" j8 X. [/ _8 h最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。
" x5 N) e# P* u9 x) @% t8 A9 v  G这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。
3 h% B7 L3 u, W. ^$ S4 e4 @4 A  m实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。' p) K! K3 J/ Q3 a) J
第六步:继续优化核函数计算
" V- l& s$ I4 B8 h6 X6 B5 i核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。
: }. X/ ?8 H% \/ r; ]5 @由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。
3 y$ Z" j' W+ T! @针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。6 X" N7 z3 q0 n, _5 Y
第七步:支持稀疏向量和非稀疏向量
, ~% R9 U8 b8 T( D- v4 ^. c) J对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。3 r; ^2 r2 G- W. U: V
但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。
* I/ l0 H8 k/ t/ L非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。
! P- q5 g6 {* F) Y所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。  h7 ]- e5 c  @( Z8 c" Q
第八步:针对线性核进行优化
2 }9 I/ e5 {, C3 C: V/ v2 V传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:
8 h4 F$ u3 }7 O" _  ]K(xi, xj) = xi . xj% S, e; G1 B0 L; t8 ]) W( |
还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。1 S/ j5 H5 Q0 D4 u/ ?* C% T
同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。
7 W5 z  M8 |! k2 }/ h) r8 W但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。. @' w5 J  q. L! m+ R# [
或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。
) k2 x9 U( \1 f( S% u/ U% o' r# Z  U后话$ L, n$ v* f  C+ w
上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。
" w8 y+ M; q" o+ \; ^1 x# f; g! X; w上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。& Y2 S2 C+ `) ?. R
- x3 ?6 t7 m) T. s
来源:http://www.yidianzixun.com/article/0Lv0UIiC+ t5 t8 ~: h  ^8 k3 n8 R; ?* b
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-3-19 09:53 , Processed in 0.036648 second(s), 23 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2026 Discuz! Team.

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