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

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4997|回复: 0

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

[复制链接]

10

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2019-5-8 03:16:31 | 显示全部楼层 |阅读模式 来自 中国
雷锋网 AI 科技评论按,本文为韦易笑在知乎问题如何学习SVM(支持向量机)以及改进实现SVM算法程序下面的回复,雷锋网 AI 科技评论获其授权转载。以下为正文:2 i% M, I' T7 g0 k, n9 R
学习 SVM 的最好方法是实现一个 SVM,可讲理论的很多,讲实现的太少了。& b  n& r/ L$ `
假设你已经读懂了 SVM 的原理,并了解公式怎么推导出来的,比如到这里:
2 T% z3 B! O4 G. y* N& F2 @: y* m  z7 s3 T: k# j/ V) ]6 _' A: L
SVM 的问题就变成:求解一系列满足约束的 alpha 值,使得上面那个函数可以取到最小值。然后记录下这些非零的 alpha 值和对应样本中的 x 值和 y 值,就完成学习了,然后预测的时候用:
' ]8 h5 I, V. ^9 a6 H. c. k' ]& P5 \0 `
1 E9 f% r! Z5 D  b- {上面的公式计算出 f(x) ,如果返回值 > 0 那么是 +1 类别,否则是 -1 类别,先把这一步怎么来的,为什么这么来找篇文章读懂,不然你会做的一头雾水。
# |9 w* O- R9 [; _% J6 r  ^那么剩下的 SVM 实现问题就是如何求解这个函数的极值。方法有很多,我们先找个起点,比如 Platt 的 SMO 算法,它后面有伪代码描述怎么快速求解 SVM 的各个系数。
" [, y- C9 ~7 [, u第一步:实现传统的 SMO 算法$ D% s, b! i% Q' k+ Q
现在大部分的 SVM 开源实现,源头都是 platt 的 smo 算法,读完他的文章和推导,然后照着伪代码写就行了,核心代码没几行:6 s! T4 z% z5 c
procedure takeStep(i1,i2): N' N7 Q* U0 Y$ r, \* [. `
if (i1 == i2) return 0
; y* s& l: M9 u$ p! }- m; a' j alph1 = Lagrange multiplier for i1& F5 d; b. ^' H* h/ ?$ y8 ~
. i8 G/ x$ h- v% u
y1 = target[i1]7 j2 `0 X( Z. k# `2 G/ y1 ?3 X

) C  b& t6 _& [  K  A& v8 P! S) HE1 = SVM output on point[i1] – y1 (check in error cache)( J# a8 k0 n$ J7 K6 _

7 R$ x! Y# e7 ]s = y1*y2
" i4 v, e6 u4 S! e9 i8 _; ?
! A1 s* `! s& NCompute L, H via equations (13) and (14)
  S/ y% h2 q( w* ~! G3 N" v
0 y- T6 V0 I7 C2 c% x% Tif (L == H)
) ]* Z1 W- M4 @& o% S! g& B6 ~$ |) k- A; ?* ]+ s& @; X
return 0
3 I! }( l: \  D( Q! T4 l( t, @# M$ A$ P2 b; ], b
k11 = kernel(point[i1],point[i1])
; L8 o( U$ n% @4 A9 C
) L, Z8 Y. @6 Bk12 = kernel(point[i1],point[i2])# p3 j$ ~9 n' C# p0 t1 v+ t

7 C8 p6 c" H: N1 f- \k22 = kernel(point[i2],point[i2])+ d" z  M* y9 Y# A0 k  u
( V6 p. ]5 ^1 {2 b! A0 W
eta = k11+k22-2*k12, n# {/ ]' G; T+ w; e1 @( [- [
5 A  c9 D9 r9 h9 X+ r
if (eta > 0)7 x" v1 i2 {0 k+ c" ~0 }
: s, _( D' e6 P; R0 ~
{
6 R$ ]' ~3 [' e+ `
8 B# i2 H$ h# o- wa2 = alph2 + y2*(E1-E2)/eta
8 t/ x- f. L3 _
7 ^: R* f, U8 Lif (a2 < L) a2 = L2 D: C+ J: J$ q( J$ i

& Y+ K, \6 I, W$ lelse if (a2 > H) a2 = H( P( a+ s7 O( ?) K
$ l( O) f+ ^) a
}/ D- G# T+ C2 R) i- b

4 e8 Q$ ?% y- z/ S% v  c% Velse- P' |/ {. r' j( j- c+ c

' I! {% `. O* [0 |7 J+ V% U{
- G0 l/ j+ Q5 W% B8 C# M
0 B7 z& L) P" Y- a: \- zLobj = objective function at a2=L3 `) J$ f% g0 g9 E% r5 k9 k

+ n5 J; |2 g+ w' D- O: ^Hobj = objective function at a2=H
0 _6 I: P# _+ e/ F1 f7 k/ q; Q8 h2 t5 n, K) e
if (Lobj < Hobj-eps)
$ N  Y6 p, V0 j# y4 d. b; d
2 _# s1 b+ d. S9 _a2 = L+ y4 {4 n0 L  m8 n- y) A
! v5 V! _* Z5 i
else if (Lobj > Hobj+eps)9 b  N- w, c9 }; \
$ c/ p, f7 ]. w, o. p4 v& [, M
a2 = H
  ^9 L5 d4 e3 c
5 y6 [9 ]' @& ]else
& G1 G: p2 O0 o/ V- s8 t
7 p  i& o  ]( |/ i9 Pa2 = alph2$ b  c; B4 S: y: y' ^, j6 |
1 {0 W& T; i: s1 k" N& b4 M& I6 s: T
}
* D8 l) U8 t1 E3 y. z+ Z9 p9 r* }* L0 Q9 [! B4 b$ E
if (|a2-alph2| < eps*(a2+alph2+eps)); ?. G& N  f4 i" Q; x' y  I$ T/ t6 j  ~
5 [8 J# l5 i" y0 ]3 l9 x* H/ C
return 0& E- [6 E5 Q$ W/ \

, J* a5 w3 F) Na1 = alph1+s*(alph2-a2)
1 g9 G! ^! \. k% V* N
1 e8 n8 @9 K! eUpdate threshold to reflect change in Lagrange multipliers
; Z# l/ a' L4 h7 K7 Y( K: m% W  j2 B8 K3 a3 F& W  d
Update weight vector to reflect change in a1 & a2, if SVM is linear
9 ]8 H+ ?4 \1 y' h
6 H4 q9 H' ~# E# _" F0 VUpdate error cache using new Lagrange multipliers6 Z2 v# F; c+ c. L; @% d% t# B

( ?& y0 E8 z+ D9 K2 MStore a1 in the alpha array" H( |$ T( |: B: Z0 y- W

0 Z& G3 ^  O9 R9 P3 m9 n6 R' eStore a2 in the alpha array) U' \7 v% v* _. x8 M
% }5 `  L8 Y( O9 M% t$ C' q2 o
return 1
3 t( d! s/ y% B  b2 A' Dendprocedure/ Z# U7 P- l2 h3 J. u; k
核心代码很紧凑,就是给定两个 ai, aj,然后迭代出新的 ai, aj 出来,还有一层循环会不停的选择最需要被优化的系数 ai, aj,然后调用这个函数。如何更新权重和 b 变量(threshold)文章里面都有说,再多调试一下,可以用 python 先调试,再换成 C/C++,保证得到一个正确可用的 SVM 程序,这是后面的基础。1 \; e" z$ P9 ^5 y0 m
第二步:实现核函数缓存
4 Z+ H* ?; C4 d* x: Z2 ~& `/ ^观察下上面的伪代码,开销最大的就是计算核函数 K(xi, xj),有些计算又反复用到,一个 100 个样本的数据集求解,假设总共要调用核函数 20 万次,但是 xi, xj 的组和只有 100x100=1 万种,有缓存的话你的效率可以提升 20 倍。
& p! y. s2 g2 x* Y* g( ~样本太大时,如果你想存储所有核函数的组和,需要 N*N * sizeof(double) 的空间,如果训练集有 10 万个样本,那么需要 76 GB 的内存,显然是不可能实现的,所以核函数缓存是一个有限空间的 LRU 缓存,SVM 的 SMO 求解过程中其实会反复用到特定的几个有限的核函数求解,所以命中率不用担心。
( I% F( V+ f4 q8 N4 p有了这个核函数缓存,你的 SVM 求解程序能瞬间快几十倍。
) _7 q' t9 c  ?/ t' M第三步:优化误差值求解+ W9 E# c5 ?  |# Q2 @5 r
注意看上面的伪代码,里面需要计算一个估计值和真实值的误差 Ei 和 Ej,他们的求解方法是:
) a; J4 L% r- |& f, P. d! F2 l0 g; Y9 WE(i) = f(xi) - yi
2 L8 e* s+ k+ {  d5 d" j5 _这就是目前为止 SMO 这段为代码里代价最高的函数,因为回顾下上面的公式,计算一遍 f(x) 需要 for 循环做乘法加法。
5 B3 p, R0 K/ d* q- P3 G+ ]1 h( [platt 的文章建议是做一个 E 函数的缓存,方便后面选择 i, j 时比较,我看到很多入门版本 SVM 实现都是这么做。其实这是有问题的,后面我们会说到。最好的方式是定义一个 g(x) 令其等于:
2 \0 L2 D3 g* V9 C9 b/ V8 H* o2 j! {# d( u! ?3 H$ |5 l# m$ w
也就是 f(x) 公式除了 b 以外前面的最费时的计算,那么我们随时可以计算误差:
, p! r" k# I; oE(j) = g(xj) + b - yj7 |) u  u7 Q6 _# I8 W9 u0 Y& _
所以最好的办法是对 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 的偏导数就行了,具体代码类似:, r: j9 V+ \5 N/ f+ i6 a; D
double Kik = kernel(i, k);  Z( F# H) t2 P0 S# ?
double Kjk = kernel(j, k);. |$ X5 F9 L& ]' V+ M
G[k] += delta_alpha_i * Kik * y + delta_alpha_j * Kjk * y[j];
) T0 L1 `) g/ b2 ]2 j: e1 i' S把这段代码放在 takeStep 后面,每次成功更新一对 ai, aj 以后,更新所有样本对应的 g(x) 缓存,这样通过每次迭代更新 g(x) 避免了大量的重复计算。
- E7 a# r5 q8 _% l1 c$ ^- N这其实是很直白的一种优化方式,我查了一下,有人专门发论文就讲了个类似的方法。
0 q$ Z  Q* q( u第四步:实现冷热数据分离
3 C. Z6 D- q2 S( p5 R5 F% KPlatt 的文章里也证明过一旦某个 alpha 出于边界(0 或者 C)的时候,就很不容易变动,而且伪代码也是优先在工作集里寻找 > 0 and < C 的 alpha 值进行优化,找不到了,再对工作集整体的 alpha 值进行迭代。& k1 Z( B4 p3 k& r* U8 j! e
那么我们势必就可以把工作集分成两个部分,热数据在前(大于 0 小于 C 的 alpha 值),冷数据在后(小于等于 0 或者大于等于 C 的 alpha)。3 e0 e; m+ @8 M/ |, B
随着迭代加深,会发现大部分时候只需要在热数据里求解,并且热数据的大小会逐步不停的收缩,所以区分了冷热以后 SVM 大部分都在针对有限的热数据迭代,偶尔不行了,再全部迭代一次,然后又回到冷热迭代,性能又能提高不少。$ m+ H7 K& ]1 A& a( W1 A4 ]
第五步:支持 Ensemble
% Q8 Q3 }* p) w8 ]) m! |* O" ~3 @大家都知道,通过 Ensemble 可以让多个不同的弱模型组和成一个强模型,而传统 SVM 实现并不能适应一些类似 AdaBoost 的集成方法,所以我们需要做一些改动。可以让外面针对某一个分类传入一个“权重”过来,修正 SVM 的识别结果。
2 B, H9 }5 B9 R: @* v% r* C! q% ]最传统的修改方式就是将不等式约束 C 分为 Cp 和 Cn 两个针对 +1 分类的 C 及针对 -1 分类的 C。修改方式是直接用原始的 C 乘以各自分类的权重,得到 Cp 和 Cn,然后迭代时,不同的样本根据它的 y 值符号,用不同的 C 值带入计算。
4 V. V2 C: n7 F这样 SVM 就能用各种集成方法同其他模型一起组成更为强大精准的模型了。: V9 K5 L4 u3 @0 j% G
实现到这一步你就得到了功能上和性能上同 libsvm 类似的东西,接下来我们继续优化。& ~* P) [! N: z, E+ k/ B8 |+ w
第六步:继续优化核函数计算
2 ]$ H$ c" g9 w) C  [/ q核函数缓存非常消耗内存,libsvm 数学上已经没得挑了,但是工程方面还有很大改进余地,比如它的核缓存实现。
" G) G, T4 D$ w由于标准 SVM 核函数用的是两个高维矢量的内积,根据内积的几个条件,SVM 的核函数又是一个正定核,即 K(xi, xj) = K(xj, xi),那么我们同样的内存还能再多存一倍的核函数,性能又能有所提升。
: O7 R2 Y) O- {% w针对核函数的计算和存储有很多优化方式,比如有人对 NxN 的核函数矩阵进行采样,只计算有限的几个核函数,然后通过插值的方式求解出中间的值。还有人用 float 存储核函数值,又降低了一倍空间需求。
" B2 Y1 k$ J5 U7 w% o, ~8 u$ k" l) B第七步:支持稀疏向量和非稀疏向量
4 `% S5 h: t) l- t3 d对于高维样本,比如文字这些,可能有上千维,每个样本的非零特征可能就那么几个,所以稀疏向量会比较高效,libsvm 也是用的稀疏向量。
, w( }4 ?$ b7 W7 @4 t, q9 K但是还有很多时候样本是密集向量,比如一共 200 个特征,大部分样本都有 100个以上的非零特征,用稀疏向量存储的话就非常低效了,openCV 的 SVM 实现就是非稀疏向量。* f8 ]8 I, t1 s2 g0 i
非稀疏向量直接是用数组保存样本每个特征的值,在工程方面就有很多优化方式了,比如用的最多的求核函数的时候,直接上 SIMD 指令或者 CUDA,就能获得更好的计算性能。
% [# n* R0 d5 ^1 R% _# b5 N( m所以最好的方式是同时支持稀疏和非稀疏,兼顾时间和空间效率,对不同的数据选择最适合的方式。
! P. G9 y8 g- k. l1 {& A第八步:针对线性核进行优化
' ~  q) N. R6 l5 y9 y) ~5 w5 o8 s传统的 SMO 方法,是 SVM 的通用求解方法,然而针对线性核,就是:2 k4 r; j9 h$ V  j! U
K(xi, xj) = xi . xj/ r3 ^# K. l& ^: z/ z9 V
还有很多更高效的求解思路,比如 Pegasos 算法就用了一种类似随机梯度下降的方法,快速求 SVM 的解权重 w,如果你的样本适合线性核,使用一些针对性的非 SMO 算法可以极大的优化 SVM 求解,并且能处理更加庞大的数据集,LIBLINEAR 就是做这件事情的。" F; }2 \( F' }8 \! I
同时这类算法也适合 online 训练和并行训练,可以逐步更新增量训练新的样本,还可以用到多核和分布式计算来训练模型,这是 SMO 算法做不到的地方。
- ^* b* w! I: d( P: c但是如果碰到非线性核,权重 w 处于高维核空间里(有可能无限维),你没法梯度下降迭代 w,并且 pegasos 的 pdf 里面也没有提到如何用到非线性核上,LIBLINEAR 也没有办法处理非线性核。
( ^7 b! C; o; R" N( g" W( u- c( r2 f或许哪天出个数学家又找到一种更好的方法,可以用类似 pegasos 的方式求解非线性核,那么 SVM 就能有比较大的进展了。% e# N* n  D" M* I6 q; p
后话7 j6 f6 _, l( }! y
上面八条,你如果实现前三条,基本就能深入理解 SVM 的原理了,如果实现一大半,就可以得到一个类似 libsvm 的东西,全部实现,你就能得到一个比 libsvm 更好用的 SVM 库了。
& ?& A& s+ b  J) l上面就是如何实现一个相对成熟的 SVM 模型的思路,以及配套优化方法,再往后还有兴趣,可以接着实现支持向量回归,也是一个很有用的东西。$ B& v2 J5 E. Z
+ W3 G' y/ ^8 i8 K
来源:http://www.yidianzixun.com/article/0Lv0UIiC
1 V( R  E8 x, l: o' s3 d5 R% [免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

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

×

帖子地址: 

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-7-17 10:11 , Processed in 0.038966 second(s), 24 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2026 Discuz! Team.

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