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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 5051|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:
- r2 |8 n' J( t9 p9 r学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。
" Z6 x! V/ J( c; g- V$ e9 S9 L! {假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:
( v' w5 Z7 R& B  |- \: b( T/ j& K3 l+ S3 F9 E1 F
SVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:" Q8 v8 f# X% ^" F

% t  T  d$ {# B4 Z8 L* ]上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。
+ c1 t2 [6 c* E1 w那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。
, j' t) O, D6 j: I# x; J4 `+ x! d第一步:实现传统的 SMO 算法" Z5 M: V# |! n$ o- j. P
现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:" N7 U& ~1 l4 e! v5 x6 ^! l( E
procedure takeStep(i1,i2)
! a3 r2 s# L6 @; d* V  T if (i1 == i2) return 0" c. N" w" o' x: K. k8 d/ I
alph1 = Lagrange multiplier for i1
: |. z) e  [! V
. P9 K& Q! s6 l' X8 qy1 = target[i1]3 |  e3 z: s( u  f' c! o6 B8 E
( \8 j7 w- C* F
E1 = SVM output on point[i1] – y1 (check in error cache)+ t0 s& R5 [' K
+ ~# [  x5 i+ D3 ?0 G
s = y1*y2; @0 Z4 o3 G1 d- b8 R0 E& g

' \6 z' N( r8 R4 q9 QCompute L, H via equations (13) and (14)' ~! m0 J$ d- N- X

7 X0 d  C* m& h6 Gif (L == H)  n0 V) {# r# \" @: ]
, \! n8 w1 d5 t3 z. G8 t
return 00 s/ i5 u5 C" Z1 S& p

4 A$ w" {( J7 p2 E) U( F9 u) ?+ K7 a$ lk11 = kernel(point[i1],point[i1])
& e" T1 Y( c& E; |; r/ ^! f. v( U. V, }# t' G
k12 = kernel(point[i1],point[i2]). L! u( n9 k0 J* {0 L' i. O/ ~; p0 l

" ^9 v; d+ n3 Z7 G2 g" S1 `& F9 ek22 = kernel(point[i2],point[i2])8 w! e5 R* q7 W$ W1 W

! m) t+ }/ ~3 e* Beta = k11+k22-2*k12: A$ L8 Y0 ^0 W; Z5 @/ p0 [

0 |. H& F5 U1 X- o: R2 c/ jif (eta > 0)
! s& r7 u' Y$ d) q) P$ E, r
5 J6 E: E- m7 V{; i& _" s" h* o. T8 `" [
! G! q) ]2 F; F* Y8 d4 M
a2 = alph2 + y2*(E1-E2)/eta
0 _5 N! z# T. E5 Q5 }, W; I: I
# k5 ^7 g' D& Q% ?if (a2 < L) a2 = L/ ?5 i/ n9 s3 L! n

. B2 ^) }( C3 O0 {- N1 K4 Z. I0 aelse if (a2 > H) a2 = H
  s# ^8 U5 D$ C% w, v% }5 b, J: p' G$ o8 I* w" R. w
}" Y3 n( j$ y7 z- Y* K: H, B# _. ]* l

0 v- R& K; X4 d5 q3 ^else' b$ K# |/ Q; b# c, z

- l: k7 J7 x. p{; k" J. W) Z+ z+ C( h( C3 }; o# `2 x
" l0 p4 A& q, t# W5 g- q9 s* H
Lobj = objective function at a2=L  t  f% M8 ^+ e

: |! m& A' F) uHobj = objective function at a2=H
0 `  p: z( L& ^1 J3 Y* S2 B1 I  Q! X/ _7 V4 }  F
if (Lobj < Hobj-eps)
8 E* b- x3 {. e) {0 @  N
+ W( b* I  I* u. z- B+ Za2 = L
' B5 I1 b/ F3 X1 |/ ], X* n' q
1 @% T( U7 N# E. D+ j5 zelse if (Lobj > Hobj+eps)5 f7 A& B$ ?$ M

( b( z, D8 G8 r  @3 H" ~! Pa2 = H
  Q" k; w% T4 @8 B& p/ N0 L  l/ h2 c. X7 }2 n" V5 S- ?/ {
else
' ^1 y* F2 a6 Y- ]; a
6 i3 v& g3 m3 ~9 J) z: va2 = alph2! ]1 L) a8 |, X. P0 i1 h' u8 |
/ K, G& c: t) B6 I
}* M$ l% J+ v8 T4 n% z; O' T
# t% q7 C! P. c0 E7 O/ m0 r
if (|a2-alph2| < eps*(a2+alph2+eps)): H+ ]& o% \; S' }

- b: w0 S, z3 z  ~1 ireturn 0
. Q1 {& _7 N% m* j' x8 H( u6 ^7 B$ M
a1 = alph1+s*(alph2-a2)' z: V6 I' G+ y( O* r

+ W6 Y5 L' ~* W, c; \# HUpdate threshold to reflect change in Lagrange multipliers
- s, ~5 n& q' Z' a: k, w4 e6 c* {" ^$ E. v$ e, ^7 D$ a2 h' X+ h! B& V
Update weight vector to reflect change in a1 & a2, if SVM is linear
5 r8 ?/ N& v: l9 w; b# }- ?! N! h/ P* |  E. H& Z3 H& D, \
Update error cache using new Lagrange multipliers
% f( X3 h# O/ L) C
  a0 b1 b5 g8 q1 r& uStore a1 in the alpha array
- K! r& ?8 B! t6 X! {- }2 U2 A: ?& b) q# V: `
Store a2 in the alpha array7 s: e1 e# ?1 [) c* g' p3 h1 D
$ R' n5 D; a( w
return 1
* |* F1 m2 U. D% G' {9 }' ~+ L/ I) X& X4 Qendprocedure0 |4 I6 v/ J7 Z' Q
核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。; @4 r. t1 q# y: b5 E
第二步:实现核函数缓存
; v; {1 r# J0 f+ }0 _# ]& W观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。( m7 U: ^2 d3 s& X6 u
样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。5 o: W  Z2 J' ~2 ~( C' b; t; u0 W
有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。
+ `% h# h- m# Y- V; t第三步:优化误差值求解
( u6 \* e; X4 m# ^4 B+ ]注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:
8 d- Y- K0 ?. y0 u" J. s- X+ TE(i) = f(xi) - yi
" O) N. B. s  n& Y2 f- ~9 i这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。! W9 h8 t7 }5 e! z/ R& I
platt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:
" X$ e5 G+ a/ _: V/ `$ l) b3 j  _7 b8 F
也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:
2 c, B/ C( I, B8 Z* BE(j) = g(xj) + b - yj; |& V. O+ L: V; R: @8 |: k
所以最好的办法是对 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 的偏导数就行了,具体代码类似:
% P3 z' u/ [4 W3 f$ hdouble Kik = kernel(i, k);0 b4 _2 p: U" Q1 `, {
double Kjk = kernel(j, k);4 X, |* }; g" F7 A; w6 Z, ]9 Q6 w* J
G[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];
& X6 E$ k2 B8 S6 _# M+ ^% l4 K# N把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。0 R3 n8 i9 e0 |$ Z6 I; M( l) w2 b* D* c
这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。
! v7 @: P5 v" ?4 V( W第四步:实现冷热数据分离
  L  u2 C* `1 r! O" V+ H0 dPlatt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。1 g& R1 f8 Q! W& U. V) x4 n2 h# x
那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。# }& A: f5 a2 Y! h6 j* c/ S
随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。
9 A! q' k: @1 ]$ o: B! z3 v: N第五步:支持 Ensemble5 z' _# H- f% ?4 N8 d
大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。
0 F% \7 v1 T2 v" _: B' u- o最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。
. f$ x; }4 b5 z这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。
9 H7 O; U+ k8 w实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。) [* E3 [5 m9 C: w3 n8 N
第六步:继续优化核函数计算
1 N5 I( y7 K5 o" k' d核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。
- k) h- M3 v" k! q- X由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。
( Q- Q" z4 F8 Q, ]针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。6 ~- x  ^& z* S, A7 g
第七步:支持稀疏向量和非稀疏向量2 A- S9 ^, s) q' T4 u
对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。
' K9 A# h+ U% U8 z% m( L但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。6 t$ y( o9 M5 @) q
非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。( D, o/ L$ V# h# p& h
所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。: y* q' Q4 o1 t
第八步:针对线性核进行优化; f5 K; A6 V% X! b) |" N$ R; r0 ^
传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:$ D: |2 _! ~8 T; Z" q4 V: i/ r" r
K(xi, xj) = xi . xj3 F# Y$ Q9 ?6 k% ]
还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。
+ J& Z0 w7 F% |9 c2 Q同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。
( S8 t, n) g+ A' R3 F) h: Z! X# u- _但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。6 _1 H6 y; R* w8 e5 O- Y
或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。# g& C: W& W/ i: x; J2 }
后话
1 @. W( j5 o/ K6 V$ s* M4 F上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。2 F7 N5 S5 @" V- B5 V6 x7 y
上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。
! E1 A$ G+ v% j3 {- a0 G* |3 A& i0 j8 u  b7 o' c; G1 f
来源:http://www.yidianzixun.com/article/0Lv0UIiC
2 C8 ?3 E9 _$ ~6 e- }/ T% E2 Z免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-9-16 09:13 , Processed in 0.047464 second(s), 25 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2026 Discuz! Team.

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