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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4896|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:
* `4 ]# w7 g3 C6 P; L$ a8 x* m! Q! Z; H) V学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。) K$ |% `, H' I4 E+ K: h& J
假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:
$ S% L$ h. r9 l0 ^0 u' G# ]7 B3 f1 @7 @8 `$ ~  u$ Y( ~4 D' t
SVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:$ D& R( y: n0 K9 b4 S

" i" \/ X  r& F3 t6 @( m上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。
# B( y# \6 X" K; L, L2 H  K. i那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。
8 A9 S- F, Z+ C8 P4 o- j第一步:实现传统的 SMO 算法( q, R  C% w8 A( M1 o& D
现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:  U& B8 L, m* u, C1 k& C! ?
procedure takeStep(i1,i2)
& I, D; A$ w$ Y6 ]7 N4 l* g) h if (i1 == i2) return 0
/ w5 ^8 @, Z- m3 q( Z8 i: K alph1 = Lagrange multiplier for i14 @0 |! Q% O# O0 T
" c+ B7 C) s! G3 |$ _6 g
y1 = target[i1]2 T- U: f# g" @9 p: t
* W* E  Z& O. g3 G* k& g
E1 = SVM output on point[i1] – y1 (check in error cache)3 c& {* a* q/ K/ e  i! U

; U) w% W( U- H" e% X' ys = y1*y2
! x3 w0 R/ a8 d* e  P
. H! H4 ]- l% h* Z  B6 JCompute L, H via equations (13) and (14)6 z1 N5 {5 f. _) S/ g' [

! \/ D8 t& I1 k, a6 P: F) pif (L == H)
, ~2 Y7 Q1 I) p; m! F' z8 @! m( e0 f7 k5 t* I' Z0 \$ ]
return 0- Q9 X+ h8 V& d+ m3 _0 i$ B

; |3 R$ r% ]2 w0 N/ N$ @k11 = kernel(point[i1],point[i1]): b7 n& G( j" X$ l' M" E

& Y. j9 a6 c4 v) ]* O; kk12 = kernel(point[i1],point[i2])& \) {8 `) h& t+ w

' o2 R  h- m% m' P! Wk22 = kernel(point[i2],point[i2]): f* o; @( f, s; ]. Y8 Q8 A

( F6 j& {% b! h, p2 x( heta = k11+k22-2*k12% c6 G% ^! }# e* o" O; y

' @4 p8 l% f! e2 ?" f) n( Jif (eta > 0)5 f( N* g' {+ `1 r/ ~6 e
/ t; r# r1 f" l) f
{; E1 ^9 ?) r, N% X- \7 K) z
" ?" c4 @1 u2 s! a: z
a2 = alph2 + y2*(E1-E2)/eta8 ]3 x: Y% u, e$ Y# y/ @! x1 k1 J7 e
" R$ q. B( L3 j6 ^0 Q
if (a2 < L) a2 = L
: m& f4 ^4 w8 J
5 h& Z. j; J+ f& b5 ?else if (a2 > H) a2 = H
$ c; h/ w8 ?1 B' d3 l" {0 X
: {' ]6 ]5 E6 B; c2 F" d7 I}
) H( ^( z7 k3 ^" \
. B2 A- {% }0 C- a& _, I  Lelse2 f2 T5 p" y9 l$ c" S4 e3 J  ^
# A& Z$ T5 D! E- t
{
9 v" V2 N% w- `1 D& @: z9 I8 ?9 b2 |
+ N/ \, ^- d5 V9 H) WLobj = objective function at a2=L
2 }+ j2 L4 [4 O& u; _7 p1 F: J/ V9 {6 U" K0 J- `' [
Hobj = objective function at a2=H% m3 g9 y9 {) T  M8 T9 V( d

' ?- B: m& T. U+ J9 P. z) _. [if (Lobj < Hobj-eps)0 o) V5 J3 P7 j; v
+ B- w7 n, L# ?5 O0 P
a2 = L" }% i: n* F8 Q9 ?# W( q
7 o5 B$ U" D: ]- p: {" n" g
else if (Lobj > Hobj+eps)* V$ n! B" I/ c; T; x6 H' q$ w/ P- X
3 I$ m! T) w7 i+ J
a2 = H7 Y- W0 ~7 F8 ?& `! F! }2 X- b

/ i2 ~& ]7 u* Eelse
2 l& h  i3 A' G+ I+ B- Z* B: Q0 y5 d. x, Y( a! x  R
a2 = alph2
, P2 ]8 n0 ^( L& ]/ O8 L6 j3 {- h* e" P2 q
}
$ D8 T! e- D4 E2 ~4 J) J
' Q# W9 V2 |6 _. J0 D0 wif (|a2-alph2| < eps*(a2+alph2+eps))
9 E2 w: w4 [+ Y4 @+ t. ^# N! y( i. [$ e" l. `2 B, f# V
return 0  |& P, m! ^2 Y+ x+ b* V5 m/ U( E

' W% P- X1 v0 p4 G5 [3 [a1 = alph1+s*(alph2-a2)
5 I; k$ x. n# O/ j6 M
- ~) [: a5 u# h9 jUpdate threshold to reflect change in Lagrange multipliers7 [0 H0 Z4 u( y2 U9 v
: j. ^, r  I/ u7 R( B' r0 c
Update weight vector to reflect change in a1 & a2, if SVM is linear' q4 `4 g+ s9 r3 q! v

2 n7 _2 o$ w& S3 A  G3 sUpdate error cache using new Lagrange multipliers1 E! @; |) b8 |4 E9 n' M6 b

# z3 }! H; B* L7 t* F9 OStore a1 in the alpha array
3 B1 W! P% \  g0 C( Y: P
) d3 o: ^& A. C1 e  M, kStore a2 in the alpha array8 T( [/ d1 B  T! u  e, W3 ?8 z

+ b2 z9 J4 Y6 z& R1 W, ]return 1
4 P) a% c# M* B! ^# E6 Wendprocedure
0 r2 ?0 [' q- A2 Y6 l- X7 @核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。
7 n' _1 a1 _5 u1 E- L3 e3 d6 V第二步:实现核函数缓存
4 s+ Y6 m6 o* t, ~# [) S观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。' ?6 _0 i$ y- W; v
样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。2 i5 G+ Q* Q  T/ N- f  e' r- i
有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。$ \4 F% B4 Y& B& R  |- R3 ^
第三步:优化误差值求解
. D- x( O8 y% k, H注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:, ?% n9 S& N% _8 i3 B" @; V% z+ q( s: K, m
E(i) = f(xi) - yi
# N, d: J- }% U这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。
0 J) K2 p/ R. fplatt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:
: Y# ^1 b) |. I1 B( `  J2 A  j: r1 l5 o
也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:
/ {$ c9 j$ C- r. c- cE(j) = g(xj) + b - yj& L) V6 U5 h! e, C. x2 L
所以最好的办法是对 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 的偏导数就行了,具体代码类似:
7 B+ A8 ]0 S; \7 N+ ^; E, Sdouble Kik = kernel(i, k);
9 e! l- n& _; j2 V: Z0 t% ddouble Kjk = kernel(j, k);
$ c4 n# O. V: U. `7 @G[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];
! l6 z; M% W! u" E( O6 M: y把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。
2 \- @" O4 h, u5 ?( y% a这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。! K& t" V) B5 u& u# O9 k
第四步:实现冷热数据分离' r. W2 G2 o) k+ i8 Z( j
Platt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。
3 N" t- W8 m4 h& t那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。" ~. A: ^' B& h) ^! v# g
随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。
- J- \0 N) g+ \7 Z3 Y第五步:支持 Ensemble
0 Q/ Y& u3 u% Q$ Y! f4 \! B* O大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。( ]( S" S( w, L7 O! M
最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。0 E! J/ b6 L. s& Q
这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。' @* ^6 ]- j/ M
实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。2 R5 e( x# p$ l0 e1 r/ `  v
第六步:继续优化核函数计算
$ V2 T' H/ [- x4 Q5 W% v核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。5 {) _; F4 W  m' W3 f+ ?
由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。
: h/ o6 V( O! P' \& {7 t针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。
6 u: s1 {' |) Q- d$ W9 m& A第七步:支持稀疏向量和非稀疏向量
/ j% O8 t( K. D( Y- l" B0 i对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。
+ V" r! {! v" z. R但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。) E3 y* U" q. V% h& F
非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。( d2 i& M/ N/ b) o
所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。7 }8 A% R; U/ \. G& j: b3 d
第八步:针对线性核进行优化
/ @% S; p! T1 F( b传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:, f5 l# c* v. h: D9 ]$ ^2 Y9 E
K(xi, xj) = xi . xj- M% D# D( k4 @# A
还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。
. Y4 g1 @2 z. v同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。- O( u0 h" J3 t+ }
但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。0 b- z! @  l1 r# P, n" C
或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。9 w  Q4 _  a% R
后话2 m2 \! n: C7 k+ R# R3 K+ M
上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。
' n9 c* V# q- o上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。" _5 u- D2 h7 `7 W3 V

  ]! c9 O, ]" [& ^9 U( W) V4 S2 {来源:http://www.yidianzixun.com/article/0Lv0UIiC& F+ J6 [1 C4 Z  M
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-3-4 07:37 , Processed in 0.058216 second(s), 24 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2026 Discuz! Team.

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