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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4854|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:
0 U: q$ h; G2 M; f学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。
/ s1 d0 s7 {! a$ K5 x假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:
* j5 E5 Z7 X7 Q) E
$ a5 [2 G$ w5 V0 y. }6 x& GSVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:: M. R4 {2 _, z+ y- t- H$ p; ~. S* u% O

" Q; o( }3 G/ ?. ~+ K上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。
0 P  d7 `$ w( P; e+ P) V3 Z5 u那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。
' M$ s4 \, v4 z  m* K" G/ E2 J第一步:实现传统的 SMO 算法
. a! `( V' o, [" C+ G! ]8 T' K现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:, N4 V7 s# N, F4 D+ S
procedure takeStep(i1,i2)# X* H& R6 R8 a$ Q+ F- u
if (i1 == i2) return 0* J+ R  R: G- ^; L# d5 s# j
alph1 = Lagrange multiplier for i1. ~; g& [" u; [$ j
# L( f, K( x" @, `8 Q
y1 = target[i1]
8 A) m8 X9 s- d' l. U: A% U+ ?' d6 u# z2 D* l' p
E1 = SVM output on point[i1] – y1 (check in error cache)
' Z' \& M" i) R, D( E: d! D* y! B: f5 ~, t1 [. \+ O
s = y1*y2
. Q5 L. ~3 T& f" X! s0 @- x" i: I# \! d# `- i  n
Compute L, H via equations (13) and (14)2 m* X  B6 w( H& ~
; W9 c. d6 G6 n4 W* R6 `
if (L == H)
8 x* b! e; {5 C$ J, m4 E" d2 Y, @
return 0
7 D' L3 o7 y  D0 B/ Y3 r# U5 G# y# \3 m0 H% ^
k11 = kernel(point[i1],point[i1])' y. h) }. \/ b9 ~, o
* V) G2 _* R8 D0 W" x7 W& ^
k12 = kernel(point[i1],point[i2])6 N* z3 y0 \( q; S) \/ N9 ?

( e9 q5 }8 V/ ]- \" K) E( ok22 = kernel(point[i2],point[i2])
4 V5 T+ w, z. b& A
8 X! d, M7 @% i6 m1 geta = k11+k22-2*k12: d5 z. k+ Q8 _6 k
9 f' t/ h- A" I
if (eta > 0)
2 M, y; q% Z, s$ @% C9 R) z: j) D
" A3 F4 S. \5 _1 m$ S9 u{
; i: a- C4 z2 `% n
- }/ D$ _" Z( J# _! n: \3 {a2 = alph2 + y2*(E1-E2)/eta  U) t9 i; u+ O

% T) e# ?. f/ Z5 z* jif (a2 < L) a2 = L
8 g9 F( ^% q) J$ s5 o5 w/ V3 `/ \  K
else if (a2 > H) a2 = H
3 r: i6 ~5 K. F4 C: x/ _$ ^- X
& s6 B; O: a" W: o! r}! k% `9 U6 G5 p! Y

& x: z( c0 H8 L$ M7 `9 t2 s5 ]else. |5 A+ K$ M: P, l& m

8 ^. s8 u/ P5 e+ I7 ^2 {{1 [+ b# j  v% t
3 ^2 Q* `# K) M0 E! R+ g. l# O
Lobj = objective function at a2=L
( d: y' f. H! G' d  H
, e) ~0 d% f, v3 c$ O0 E5 NHobj = objective function at a2=H
* z( D. E  K+ G& O; }) ]; [4 m5 P1 \9 g
if (Lobj < Hobj-eps)* }2 M% [* h! g% E% [1 @* v

5 x1 O$ d1 n1 y4 c; ja2 = L/ c$ {" n+ T8 Y  l

1 f: T: m3 ]+ @0 Yelse if (Lobj > Hobj+eps), v" q) T2 x0 V
* h$ S) x5 a* \% @6 i* h
a2 = H
1 H/ h* l( p. Y9 e' R3 \2 I% y
6 h6 B+ K& G& |' R4 `8 Zelse0 q: o  R3 X/ J+ Q5 j- G2 p2 w

  N  N. S6 G, g5 za2 = alph2
3 Z5 J2 S1 F' P1 O/ ~, Y& f+ Q  [6 V- t) W  m
}
* O* E5 C% T" j) l) ~0 p1 A8 r5 X9 r' p! |
if (|a2-alph2| < eps*(a2+alph2+eps))
: e7 a4 C3 o. _9 Y% [, g6 O
+ F* y) ?  V- U2 w( Z5 m/ Ereturn 02 Q) z% l: ^/ H" c& j

) d2 J: f9 n. t% B8 ?8 sa1 = alph1+s*(alph2-a2)3 I1 w: k) f5 W. X% h& _% H1 `; W
& T" }4 u$ n; @( h) O! A" K8 D: W
Update threshold to reflect change in Lagrange multipliers' x& `  Z0 ^% B8 a1 r+ G; w& D

  b+ a& U! H9 R3 y8 v/ `Update weight vector to reflect change in a1 & a2, if SVM is linear) k* M9 m2 o8 |* r4 K, ^1 h

) F# h) {+ M3 F. l7 v' fUpdate error cache using new Lagrange multipliers
9 d0 y7 U  j$ Y% K! w! w* Y" W* C) ~5 @0 p/ g; X% ]6 S" X- s( K1 s
Store a1 in the alpha array  F6 ^% h' Z4 N

) B, Z0 ~! C, r* V6 OStore a2 in the alpha array% m$ P; z# M* W: s4 e6 t& |
$ n  M, i2 {" M5 K! W4 D
return 1
6 R' y5 i2 |9 S8 d8 `. gendprocedure- ^5 J  z" H8 o+ Y
核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。
- x1 P: |% R9 L2 S) [第二步:实现核函数缓存+ m6 ^; w; S5 f- r" R6 S
观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。- ]9 L5 l; ~! }2 _. K, [
样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。
' ~5 y7 r9 O5 h9 {有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。
. P9 |8 x5 N/ ]2 r" t8 ~第三步:优化误差值求解# h! v6 J9 j/ X3 ^9 |/ B
注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:. t4 m. j8 t6 k5 Y; f9 Q
E(i) = f(xi) - yi6 r9 u" \* `+ I1 K; \
这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。0 I- b1 f" T' C+ l) h7 W
platt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:
9 T! H5 t( C0 }/ f* j! S
1 V$ E% c" n1 Q# s也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:
, L4 O$ Q! o- A' [; b; @/ V7 [! XE(j) = g(xj) + b - yj
$ X, C5 i2 a  z所以最好的办法是对 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 k( @  s0 n  [3 Kdouble Kik = kernel(i, k);
9 F- o/ P* }' s. L! Vdouble Kjk = kernel(j, k);
- n! f1 X. `5 k: z! c  bG[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];, @( u4 a. \" j! Y
把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。2 L8 q1 h/ t9 P8 I
这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。9 U7 k! T, k) k. q  q; D: t
第四步:实现冷热数据分离; ]+ y- y' N  k( L+ D+ K8 v
Platt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。
: n# B" b+ w/ f1 J4 H4 O8 h7 ^那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。
5 ~" V, A0 T) ?( X$ s. g: {随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。. C* Y; s0 E# B$ H7 N/ F
第五步:支持 Ensemble; i2 V/ Z6 J3 T5 Y6 \, I- M' d/ o
大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。
5 L9 ?- C; y- e) R" a' e# r+ i4 Z9 K最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。' r5 L% w+ N" @: }# X
这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。) k( {! V# C/ ^; m: L: b' L
实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。* [6 N! V  p+ ^) Q" s
第六步:继续优化核函数计算8 m+ G! i6 Q+ X  e2 }* ^- k
核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。
9 j  X& Q( s, _( x% y9 @4 P, s+ j由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。
. K+ Z% v' ]: w( ~7 M  l1 J2 }# d; H' M  ?针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。
' P- A% l( f6 I6 r5 S第七步:支持稀疏向量和非稀疏向量
1 t; U. W( [7 o对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。' T$ c! p3 K3 x0 ?% U
但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。& ]$ J- l; L7 V/ i" w# ~
非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。
' h# m. t, I3 S# _" B0 Y* Q% q所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。
! f  |/ h0 i. {; C第八步:针对线性核进行优化
* ^% Y6 X3 W5 Q$ X# h: p传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:- g5 m% z) C) q0 h6 l
K(xi, xj) = xi . xj
! b; r5 F/ e! A$ e4 M7 b还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。6 w- z' Q' D7 C6 K, y( ?% |! e
同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。
& Q$ d# P: e% ~' U  q. N: y! O7 v但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。6 U0 c$ B$ g2 f( }0 v& c2 h8 \
或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。
) U9 \: X# ]3 c* n后话5 H% S' v2 b9 @% d$ _9 T
上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。
! X. q% w# e0 G* M& F' R上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。
& h+ ?* I; U& _5 p% `& A2 `
1 G" n0 C" k& h& `' F0 I来源:http://www.yidianzixun.com/article/0Lv0UIiC
# ^2 q5 i$ Y# E. B免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-11-6 13:30 , Processed in 0.041580 second(s), 23 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2025 Discuz! Team.

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