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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4898|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:
  r0 A/ B" e/ z+ Z* I! [* i学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。0 N3 N* x" ]; s
假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:
+ `* w, t3 Q$ d  W5 }; V, u/ N/ Y, K8 w8 F8 S. R- O. k* a
SVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:
" w1 e5 n2 T" B& e( ]5 |1 r: q5 ^5 k: x, Q) f- V* N& e
上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。( A: \: ?: o# L3 o' m5 Q
那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。: T5 E6 f* {- ~4 t7 `
第一步:实现传统的 SMO 算法
6 Q7 Y, z7 o( l/ y4 _" |! A9 O现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:/ N( y1 b" ]: P: j: w' C: {( M
procedure takeStep(i1,i2)
) [4 V3 b) l3 P if (i1 == i2) return 0
- K; g/ R3 O2 [$ L alph1 = Lagrange multiplier for i12 v' j' D% E" D, @0 v) t
. c- E1 ]# C/ h, m9 Y
y1 = target[i1]$ Z0 _7 d8 I& b, h' {

3 b( v/ }1 x% T; |E1 = SVM output on point[i1] – y1 (check in error cache)
( {2 d; `7 J0 x7 n: p! Z" v
8 V4 L9 U4 O$ M$ [  ~& I8 bs = y1*y2! Z# l: e/ Y1 c$ E( M' q

. y/ N; w' o  ]Compute L, H via equations (13) and (14)
- ^4 U0 b; r7 R, F
$ g% h/ D! A7 \' f- ~if (L == H)
& ^* U  c1 ?1 _4 g3 c& I% s1 E5 `1 O
return 0
! z4 Z" u3 F1 v2 U! R
: O2 |; F1 h5 @) Y4 Kk11 = kernel(point[i1],point[i1])2 u( n% a' y# o  O: I  Q  ?1 c  P

" y) |- o: d4 ~, k. c. vk12 = kernel(point[i1],point[i2])) H; P: L5 ]; Q; a( |' I/ e) P* t

* T- b  X3 M5 n) v" o) sk22 = kernel(point[i2],point[i2])0 k% W( l. O: R2 q5 T; x8 }; l
% i- z2 a( c5 |6 W3 M6 S& |
eta = k11+k22-2*k12% ^2 Z- v& D  n# j7 h, J

, I. Y' a/ c8 w# ?$ i, s0 Aif (eta > 0)
7 E  X* `! e: {1 T( Z; R* P7 N2 U0 B8 V9 x; M8 w% \  X8 B7 G& z
{
: t; [* y6 `  t' H: B6 y
3 T7 y6 c+ g) {a2 = alph2 + y2*(E1-E2)/eta
3 k- W, I6 v7 O. m1 E# j0 Z4 F4 h
( T3 d' l$ x+ N3 c8 K7 aif (a2 < L) a2 = L8 _. [% i7 D+ i/ u
# w" z& ]$ }. c+ g+ ~
else if (a2 > H) a2 = H
( M" x) A4 c' l9 }4 P" Q9 N/ a: M3 X  m9 _- Q% J. d
}
4 r, C$ X% M& \! g( o; J$ C0 {: ?
' r+ f, R) b/ |  g! I4 W2 @# {: Aelse
6 k" ~6 I4 J  L) c, W. o1 F7 [
{& n' h) H  c5 i. O2 ^- I! @

3 x, }# s! w- l# C0 [) }' iLobj = objective function at a2=L
  H. D2 g: m' T# _1 _# y4 D+ Z8 a/ q; `. B2 h. u3 {! B* R
Hobj = objective function at a2=H
5 ~5 V  v3 {& n, J
. h: @2 ^: K9 j6 |4 b+ a: Xif (Lobj < Hobj-eps)
' E& t% ?1 u1 C8 S2 n
" _9 ^/ f' j  ]a2 = L- V. a( J5 h4 z

# E3 ^3 ~2 H, W! Aelse if (Lobj > Hobj+eps)" o0 P5 P4 Y, s3 a0 P& T! W
- p1 Y2 b) r& ~0 w" H
a2 = H
& K0 v; s) ?6 s: P3 X
9 O' V8 G4 a2 [else
5 [+ A6 G: L3 A% Z2 I; R! T1 `; U) N7 U7 c
a2 = alph26 g3 v( I  v8 Z
. Y! u. d4 w8 ]2 W3 `4 K
}
4 D" e) {1 J; v/ T  R6 w! A! [6 I2 O$ _+ t# {9 R# Y
if (|a2-alph2| < eps*(a2+alph2+eps)); y6 f8 @! ~: k* d! ^) J

1 i9 k/ w3 s2 S0 L; e; Kreturn 0, Q1 m3 z5 U  q4 B4 a# v

" p3 s" k7 t4 S! d( M$ ~! m( M/ @a1 = alph1+s*(alph2-a2)( X# }4 V' I- x& [1 p) R, v+ a
- ^: K! M% Q& p' f0 e1 H
Update threshold to reflect change in Lagrange multipliers
! q7 X4 I. z: h8 N
, m" [& x5 E3 s. SUpdate weight vector to reflect change in a1 & a2, if SVM is linear
2 Z  L/ r  d+ q& v& D5 Z( w; H2 ^; j1 O" {9 I& ^; _3 |0 n
Update error cache using new Lagrange multipliers% ~4 t# _# _* H& `- Q) K
* L! L7 L7 c0 T5 m8 V# z' K
Store a1 in the alpha array- W6 j0 O) v5 Z7 h9 \( p+ ^! i: p
* J* d9 l. g% b
Store a2 in the alpha array- K  d2 Z9 C. A) W9 `6 Q: z

8 V" ?& s% H+ T; |2 u. S; V8 g$ treturn 18 `8 s7 X4 \5 i& l: k1 N. r5 S
endprocedure! n4 ~8 \0 x* n% E* m
核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。# H5 x* A# z, q. W) P5 C8 L
第二步:实现核函数缓存
* M& d8 g# y" c- {$ L* X观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。
! O' L4 M/ r* |7 B样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。
, \# X* I" f% m' }" @2 o8 Y有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。3 s& z$ w+ Y/ x( C. Q- T
第三步:优化误差值求解; E4 s. |3 {9 a
注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:
( O9 {% D9 @" h$ M& u2 OE(i) = f(xi) - yi
6 p2 d$ G) x, K这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。1 n7 H# O3 w, c+ t# K
platt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:6 O$ Q! G' [2 i4 t6 C& ~6 E

, w7 ^  j+ ?# H( @2 i- d) N5 |也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:& P3 y! Y. u( Q6 i. D' r- t" R5 i7 L
E(j) = g(xj) + b - yj
  g* _4 g  P5 n2 G3 g2 u& m. O2 r所以最好的办法是对 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 的偏导数就行了,具体代码类似:' O0 R* i; _; }
double Kik = kernel(i, k);3 d+ ~8 [( Z0 m* J4 k0 B( [- E* S1 o
double Kjk = kernel(j, k);
% R% l* F; |# n$ e- v/ Q, r+ E8 YG[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];; p! q# C- l( e) W( P, ?
把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。! X2 i6 Q& P: [0 v0 |% c
这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。' l, \& t1 B. y6 \: O' l7 J- u( F$ M
第四步:实现冷热数据分离8 e$ \" t3 o1 u7 s8 f
Platt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。
, S/ }2 V5 g9 Q) t0 ]( \那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。
1 q+ I" h- c# i+ [随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。% o2 {. o' @+ x9 E
第五步:支持 Ensemble0 R+ n* ^% y5 x8 F
大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。
( X: N! j- [6 W" x& R最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。
7 K0 v* r" a, Q5 Y& Y- @4 ?这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。; A* B% l: g% Z* P1 C5 G0 p
实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。& U: u. `) J, b; v
第六步:继续优化核函数计算. r$ ]" b0 K( e- H
核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。
7 ~* q! v- ^8 j由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。1 O7 v3 J3 s0 l0 b# k0 T; N
针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。
$ T3 L, l. _/ a0 \第七步:支持稀疏向量和非稀疏向量
8 B% t) c' P# g' `* N对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。6 s: {+ i& y$ T* W7 x
但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。' F4 R+ w8 l9 J  k) b
非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。
9 M7 h0 T9 L. U" P+ C0 f9 o1 z  N所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。
; A7 |  \( g2 f0 U+ J% o第八步:针对线性核进行优化
; I1 Z+ r$ d9 F4 `& f  i1 H传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:' B- Y# U  \( g+ {
K(xi, xj) = xi . xj) P4 h2 b# B: Y6 ~) s
还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。
' I- Z' F0 p0 W* [同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。0 y! j  l& v6 n9 x& q, _
但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。4 Z  s, a1 ?- W2 w1 k, d
或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。9 F& H% s* b: v
后话
5 a# Q7 K6 }/ H6 Z4 u9 q上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。
+ k1 U) u5 x5 A7 X5 S7 R/ N- y上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。
% ^1 x4 J" X( Z) c5 j# v9 K' p2 D1 G1 R
来源:http://www.yidianzixun.com/article/0Lv0UIiC( j) I2 k3 R3 j" J7 i
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-3-4 09:01 , Processed in 0.039633 second(s), 24 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2026 Discuz! Team.

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