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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 5072|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:6 {) h+ c' }) E, c
学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。; [! `. E$ W5 p# f0 i1 R
假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:0 y2 R5 ^1 I9 _0 R) w5 b1 b7 l! [
( J9 L; S4 R; p5 O
SVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:  i; `) e+ Q+ h( [7 p

0 h6 v  W! N9 w5 W# H; C上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。" h. y& U4 b) O. p. k" B& x
那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。, X# b* W# U  U$ V0 ~" D
第一步:实现传统的 SMO 算法, {9 r# a3 }) \( t
现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:
3 Q- U  T* i8 A/ ]procedure takeStep(i1,i2)' m5 i, D8 l) k  _3 F
if (i1 == i2) return 0
- g- C6 B9 E4 @: ` alph1 = Lagrange multiplier for i1
! c. p, a' L* M& G; v5 z8 O5 o  ~! H* `
y1 = target[i1]. y6 H) c. }# K/ ]& `* _" N& K

+ e1 G+ H$ K+ Q9 f5 xE1 = SVM output on point[i1] – y1 (check in error cache)+ i, K, X4 ^" ?  {
5 W# v6 n; Z! y9 S4 X& p) D- G
s = y1*y2  F- r$ O3 Z4 _2 D( ^

" Q0 M+ u1 }; KCompute L, H via equations (13) and (14)
0 G4 t/ F) X7 W0 c: H" v4 W. [' a3 M# n5 Q' m. s
if (L == H)
  G5 e; w1 s! A9 o3 B+ S4 X$ y. e& H. W8 q
return 0
2 I6 z, C% r7 {2 x
- I$ g2 A4 M( pk11 = kernel(point[i1],point[i1])
) F' Y7 Y! f9 q/ k( e: p, c$ W: [6 _4 s
' J4 O  q+ z. U, L+ x* vk12 = kernel(point[i1],point[i2])
! L  ?$ K. y/ z( e& |- U
+ H: Q3 a6 O& q( bk22 = kernel(point[i2],point[i2])
  V, N- y2 G4 V! D  W+ X. r7 a+ U# R: e$ E" E6 s% q
eta = k11+k22-2*k12
' F" W3 b' I$ {( h% q' Q' O6 J6 d0 M8 H3 a
if (eta > 0)& `- D# e1 p  `% |/ H# E+ R1 r

1 _' ^. ^! p' U) M7 @- g0 `/ ^{
1 n5 o  m9 e3 E) Z  ~6 V7 t( g! x) C0 u- Y' M; J4 W
a2 = alph2 + y2*(E1-E2)/eta3 i# m" j) N% G, _$ Z
' k2 o" I+ R8 m8 {- R( i6 z8 v
if (a2 < L) a2 = L
' ~8 L, z2 x* T& C4 F
- S4 C8 e% S8 J  Z  Velse if (a2 > H) a2 = H2 [$ b& h5 ~* ?; j( X" l

! Q0 q' i0 H8 R  c8 u8 l& i}! k( T2 q: V) l$ j8 E9 K

$ E, J8 X0 }4 helse# Z6 J# S% T: H9 V/ v
) A$ R5 R8 b0 Q3 A
{3 a* Q% e3 E6 l! \/ x# j
5 O2 S- {& V# M' h! o2 W
Lobj = objective function at a2=L
; I' f/ s+ x, z  k9 c# h' J( ?; I' b1 i  {/ g) Z
Hobj = objective function at a2=H
+ L8 P2 _( O( ]( D/ `6 R* S, g& X  V
if (Lobj < Hobj-eps)6 M% W4 n4 p- u

3 `4 Y9 ?! t+ Z$ D: p2 _. A/ K8 ~4 Za2 = L
2 H) N; F, k3 u2 L$ J" P* _- a% Y; P- R/ \
else if (Lobj > Hobj+eps)$ g1 {* G. d' r. A3 X: |

1 f  G2 C0 I: R" X, Pa2 = H3 x& F& b! `: ~8 f

9 W7 j! b$ U% L4 ^3 V  h* Relse" B: ]' [! F( m/ o
, C; U4 ^, w& c5 }) u$ Z' n7 X
a2 = alph2
' @. D  i$ e1 [' E3 y
$ t1 X) Q" c; z" G}
8 {4 G6 G* x' P5 J* Q6 x: N: {& Q
if (|a2-alph2| < eps*(a2+alph2+eps))8 m+ Z( L* Q: D8 m

- ?. Y+ |; R0 N8 p  V" ^) X: c; {return 0. C) K  A9 {: A3 D" X, u5 N9 V( ?1 A3 L
$ |0 t7 k, s4 U& w/ I/ M* D
a1 = alph1+s*(alph2-a2)# y! _& u( H% G* q7 x
% Q$ ?: [: c; k2 F/ G+ D; |
Update threshold to reflect change in Lagrange multipliers
* h1 m. J+ R! T# [; y) M. c  w  |
2 z9 N! P1 E) P  mUpdate weight vector to reflect change in a1 & a2, if SVM is linear
' O" r# b1 Z: F* S" P7 ]7 O0 p, h( |
Update error cache using new Lagrange multipliers
1 ]5 o! R3 x8 g5 L: h- _2 b# L0 O/ H- D% Z+ h( T" f
Store a1 in the alpha array# _/ O% B8 }1 O7 M0 _; F

; o7 _& g7 b/ v' mStore a2 in the alpha array$ I# B! V! w) c4 h9 l( I
/ M& |" t( I3 e1 k2 V
return 1* J& Z3 Y  m& P7 L! b
endprocedure, ]- f6 j: n8 D  R% D% a1 ^( h
核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。
4 @5 f4 j+ o2 T* Y0 f" F) v第二步:实现核函数缓存
3 T7 \4 j6 L0 W4 l+ X2 S观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。6 n& i* Q9 q" i" z" L, N1 t) d- l/ M
样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。
5 }4 v9 H" X8 }; B有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。5 I% G2 E3 M' J) E: u2 M
第三步:优化误差值求解: \; _9 A; k% M' ]# D, [4 \
注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:
7 g0 `6 V4 t/ f6 A$ N8 u2 Y+ BE(i) = f(xi) - yi4 w5 U% |8 X6 @4 U6 y7 i/ q) t! r
这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。) M5 o& e- }& H% G4 S" w2 J" j8 E
platt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:
2 Z4 Q# b# u; {4 a
' ]# E0 L6 G3 l. W6 v4 z" m也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:8 }9 h3 Z& P- ^2 x. Z
E(j) = g(xj) + b - yj* u. ]/ c, q8 v( S4 e( U0 Y* p
所以最好的办法是对 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 N  T( h+ g0 Edouble Kik = kernel(i, k);
. C& B. R. ?* g( n$ [! Jdouble Kjk = kernel(j, k);
$ M* m0 a# M% \' y; F( nG[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];
  O" t+ j: @4 M9 [1 P% D0 G$ X, ~把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。% l) r* r* Y& h" ?) ?) Q& h, S
这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。; r5 S+ _( C2 P/ ~9 k3 q0 Y
第四步:实现冷热数据分离
2 p/ D% a# F# O  D% a$ Y7 n* W) cPlatt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。% @- L9 C2 g9 q8 O, O
那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。
, |! S4 t- \% M5 c4 u* w8 R  ~2 {: \随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。' p/ F7 c% k* U4 X9 z: h
第五步:支持 Ensemble
6 E$ l0 y" U; `" `9 a大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。: j9 T) C7 W, x+ @& O2 g( m/ K
最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。3 R2 F9 Y$ Z1 x7 B& H
这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。
* r; a8 ?  V8 X实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。
6 r5 N6 c: s  ?5 M6 M1 U# X' L& M第六步:继续优化核函数计算7 I5 `. K7 D8 T% I- l, c$ j! z
核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。7 b% L- e5 L  |4 v- M
由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。
" m' Q4 k4 [1 i& X8 p0 i; D# `针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。
; j, C0 C5 t8 G4 W) y第七步:支持稀疏向量和非稀疏向量
" t  w6 T9 H- f对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。
9 D% A8 R0 f! l& h7 l* I但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。7 D& O* d. ~% a' V
非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。2 \1 W4 F/ f' d4 l  s7 A% d! y
所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。
# r) ^# H% t4 w& y3 |3 |: H7 P第八步:针对线性核进行优化2 s/ @& H: ^5 T; W
传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:
  I1 L; ~+ r) m0 d9 F' TK(xi, xj) = xi . xj
! z: Z7 R! c$ n0 [  O$ |还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。
- q" {# @" J# h. K* d同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。
) ?& r+ ^) b; ?但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。
( }6 q& `, Z/ ~8 [- J% E2 e( q或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。
6 i) r3 Z3 ?0 Z& [9 A后话; c- U: K& C4 l; |" S
上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。! o* b& E. H0 l: F, H
上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。  q* E: h( O# F  _3 ]

/ E8 ^# [5 l7 }" }来源:http://www.yidianzixun.com/article/0Lv0UIiC, c6 v3 S4 k4 A' l8 y( m
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-10-15 05:46 , Processed in 0.043512 second(s), 25 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2026 Discuz! Team.

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