zhuqibs
作者zhuqibs2020-04-10 00:21
软件开发工程师, Adidas

跟我一起学机器学习 -- 通过内核技巧实现SVM

字数 6064阅读 1024评论 0赞 6

SVM理解到了一定程度后,是的确能在脑海里从头至尾推导出相关公式的,最初分类函数,最大化分类间隔,max1/||w||,min1/2||w||^2,凸二次规划,拉格朗日函数,转化为对偶问题,SMO算法,都为寻找一个最优解,一个最优分类平面。一步步梳理下来,为什么这样那样,太多东西可以追究,最后实现。
sklearn.svm
Sklearn包含的常用算法里介绍过常用的算法,scikit-learn中学习模式的调用,有很强的统一性,调用机器学习的方法都是一个道理,算法就是一个类,其中包含fit(),predict()等等许多方法,我们只要输入训练样本和标记,以及模型的一些可能的参数,自然就直接出分类的结果。

总结起来就是8个字:导入-建模-训练-预测

先看个小例子,然后再细解。

  1. import numpy as np
  2. X = np.array([[-1, -1], [-2, -1], [1, 1], [2, 1]])
  3. y = np.array([1, 1, 2, 2])
  4. from sklearn.svm import NuSVC
  5. clf = NuSVC()
  6. clf.fit(X, y)
  7. print(clf.fit(X,y))
  8. NuSVC(cache_size=200, class_weight=None, coef0=0.0,
  9. decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
  10. max_iter=-1, nu=0.5, probability=False, random_state=None,
  11. shrinking=True, tol=0.001, verbose=False)
  12. print(clf.predict([[-0.8, -1]]))
    [1]

更多案例,大家可以移步scikit-learn官网

https://scikit-learn.org/stable/modules/svm.html#svm-classification

scikit-learn中SVM的算法库分为两类,

一类是分类的算法库,包括SVC, NuSVC,和LinearSVC 3个类。

另一类是回归算法库,包括SVR, NuSVR,和LinearSVR 3个类。

相关的类都包裹在sklearn.svm模块之中。

对于SVC, NuSVC,和LinearSVC 3个分类的类,SVC和 NuSVC差不多,区别仅仅在于对损失的度量方式不同,而LinearSVC从名字就可以看出,他是线性分类,也就是不支持各种低维到高维的核函数,仅仅支持线性核函数,对线性不可分的数据不能使用。

同样的,对于SVR, NuSVR,和LinearSVR 3个回归的类, SVR和NuSVR差不多,区别也仅仅在于对损失的度量方式不同。LinearSVR是线性回归,只能使用线性核函数。

下面我们只说看一下SVC详细用法,NuSVC、LinearSVC建议大家看一下刘建平Pinard@cnblogs统计的表格

https://www.cnblogs.com/pinard/p/6117515.html

SVC函数一共有14个参数:

SVC参数解释

(1)C: 目标函数的惩罚系数C,用来平衡分类间隔margin和错分样本的,default C = 1.0;
(2)kernel:参数选择有RBF, Linear, Poly, Sigmoid, 默认的是"RBF";
(3)degree:if you choose 'Poly' in param 2, this is effective, degree决定了多项式的最高次幂;
(4)gamma:核函数的系数('Poly', 'RBF' and 'Sigmoid'), 默认是gamma = 1 / n_features;
(5)coef0:核函数中的独立项,'RBF' and 'Poly'有效;
(6)probablity: 可能性估计是否使用(true or false);
(7)shrinking:是否进行启发式;
(8)tol(default = 1e - 3): svm结束标准的精度;
(9)cache_size: 制定训练所需要的内存(以MB为单位);
(10)class_weight: 每个类所占据的权重,不同的类设置不同的惩罚参数C, 缺省的话自适应;
(11)verbose: 跟多线程有关;
(12)max_iter: 最大迭代次数,default = 1, if max_iter = -1, no limited;
(13)decision_function_shape :‘ovo’ 一对一, ‘ovr’ 多对多 or None 无, default=None
(14)random_state :用于概率估计的数据重排时的伪随机数生成器的种子。

核函数如何选取

1)线性核函数(Linear Kernel)表达式为:K(x,z)=x∙z,就是普通的内积,LinearSVC 和 LinearSVR 只能使用它。

2) 多项式核函数(Polynomial Kernel)是线性不可分SVM常用的核函数之一,表达式为:,其中,γ,r,d都需要自己调参定义,比较麻烦。

3)高斯核函数(Gaussian Kernel),在SVM中也称为径向基核函数(Radial Basis Function,RBF),它是libsvm默认的核函数,当然也是scikit-learn默认的核函数。表达式为:, 其中,γ大于0,需要自己调参定义。

4)Sigmoid核函数(Sigmoid Kernel)也是线性不可分SVM常用的核函数之一,表达式为:, 其中,γ,r都需要自己调参定义。

更多案例,大家可以移步scikit-learn官网

最常用的是核函数是Linear与RBF,需要注意的是对数据归一化处理。

1、Linear:主要用于线性可分的情形。参数少,速度快,对于一般数据,分类效果已经很理想了。

2、RBF:主要用于线性不可分的情形。参数多,分类结果非常依赖于参数。

吴恩达也曾经给出过选择核函数的方法:

1、如果Feature的数量很大,跟样本数量差不多,这时候选用LR或者是Linear Kernel的SVM

2、 如果Feature的数量比较小,样本数量一般,不算大也不算小,选用SVM+Gaussian Kernel

3、 如果Feature的数量比较小,而样本数量很多,需要手工添加一些feature变成第一种情况

Kernel Trick实现svm
04
主要思想及算法流程来自李航的《统计学习方法》和之前推荐的《理解SVM的三重境界》(文末有PDF)

  1. coding=utf-8

  2. import time
  3. import random
  4. import numpy as np
  5. import math
  6. import copy
  7. a=np.matrix([[1.2,3.1,3.1]])
  8. print a.astype(int)

  9. print a.A

  10. class SVM:
  11. def __init__(self,data,kernel,maxIter,C,epsilon):
  12. self.trainData=data
  13. self.C=C #惩罚因子
  14. self.kernel=kernel
  15. self.maxIter=maxIter
  16. self.epsilon=epsilon
  17. self.a=[0 for i in range(len(self.trainData))]
  18. self.w=0 for i in range(len(self.trainData[0))]
  19. self.eCache=[[0,0] for i in range(len(self.trainData))]
  20. self.b=0
  21. self.xL=self.trainData[i for i in range(len(self.trainData))]
  22. self.yL=self.trainData[i for i in range(len(self.trainData))]
  23. def train(self):
  24. support_Vector=self.__SMO()

  25. self.__SMO()
  26. self.__update()
  27. def __kernel(self,A,B):
  28. 核函数 是对输入的向量进行变形 从低维映射到高维度

  29. res=0
  30. if self.kernel=='Line':
  31. res=self.__Tdot(A,B)
  32. elif self.kernel[0]=='Gauss':
  33. K=0
  34. for m in range(len(A)):
  35. K+=(A[m]-B[m])**2
  36. res=math.exp(-0.5K/(self.kernel[1]*2))
  37. return res
  38. def __Tdot(self,A,B):
  39. res=0
  40. for k in range(len(A)):
  41. res+=A[k]*B[k]
  42. return res
  43. def __SMO(self):
  44. SMO是基于 KKT 条件的迭代求解最优化问题算法

  45. SMO是SVM的核心算法

  46. support_Vector=[]
  47. self.a=[0 for i in range(len(self.trainData))]
  48. pre_a=copy.deepcopy(self.a)
  49. for it in range(self.maxIter):
  50. flag=1
  51. for i in range(len(self.xL)):
  52. print self.a

  53. 更新 self.a 使用 机器学习实战的求解思路

  54. 计算 j更新

  55. diff=0
  56. self.__update()
  57. 选择有最大误差的j 丹麦理工大学的算法是 对j在数据集上循环, 随机选取i 显然效率不是很高

  58. 机器学习实战 硬币书表述正常 代码混乱且有错误 启发式搜索

  59. Ei=self.__calE(self.xL[i],self.yL[i])
  60. j,Ej=self.__chooseJ(i,Ei)
  61. 计算 L H

  62. (L,H)=self.__calLH(pre_a,j,i)
  63. 思路是先表示为self.a[j] 的唯一变量的函数 再进行求导(一阶导数=0 更新)

  64. kij=self.__kernel(self.xL[i],self.xL[i])+self.__kernel(self.xL[j],self.xL[j])-2*self.__kernel(self.xL[i],self.xL[j])
  65. print kij,"aa"

  66. if(kij==0):
  67. continue
  68. self.a[j] = pre_a[j] + float(1.0self.yL[j](Ei-Ej))/kij
  69. 下届是L 也就是截距,小于0时为0

  70. 上届是H 也就是最大值,大于H时为H

  71. self.a[j] = min(self.a[j], H)
  72. self.a[j] = max(self.a[j], L)
  73. self.a[j] = min(self.a[j], H)

  74. print L,H

  75. self.eCache[j]=[1,self.__calE(self.xL[j],self.yL[j])]
  76. self.a[i] = pre_a[i]+self.yL[i]self.yL[j](pre_a[j]-self.a[j])
  77. self.eCache[i]=[1,self.__calE(self.xL[i],self.yL[i])]
  78. diff=sum([abs(pre_a[m]-self.a[m]) for m in range(len(self.a))])
  79. print diff,pre_a,self.a

  80. if diff < self.epsilon:
  81. flag=0
  82. pre_a=copy.deepcopy(self.a)
  83. if flag==0:
  84. print (it,"break")
  85. break
  86. return support_Vector

  87. def __chooseJ(self,i,Ei):
  88. self.eCache[i]=[1,Ei]
  89. chooseList=[]
  90. print self.eCache

  91. 从误差缓存中得到备选的j的列表 chooseList 误差缓存的作用:解决初始选择问题

  92. for p in range(len(self.eCache)):
  93. if self.eCachep!=0 and p!=i:
  94. chooseList.append(p)
  95. if len(chooseList)>1:
  96. delta_E=0
  97. maxE=0
  98. j=0
  99. Ej=0
  100. for k in chooseList:
  101. Ek=self.__calE(self.xL[k],self.yL[k])
  102. delta_E=abs(Ek-Ei)
  103. if delta_E>maxE:
  104. maxE=delta_E
  105. j=k
  106. Ej=Ek
  107. return j,Ej
  108. else:
  109. 最初始状态

  110. j=self.__randJ(i)
  111. Ej=self.__calE(self.xL[j],self.yL[j])
  112. return j,Ej
  113. def __randJ(self,i):
  114. j=i
  115. while(j==i):
  116. j=random.randint(0,len(self.xL)-1)
  117. return j
  118. def __calLH(self,pre_a,j,i):
  119. if(self.yL[j]!= self.yL[i]):
  120. return (max(0,pre_a[j]-pre_a[i]),min(self.C,self.C-pre_a[i]+pre_a[j]))
  121. else:
  122. return (max(0,-self.C+pre_a[i]+pre_a[j]),min(self.C,pre_a[i]+pre_a[j]))
  123. def __calE(self,x,y):
  124. print x,y

  125. y_,q=self.predict(x)
  126. return y_-y
  127. def __calW(self):
  128. self.w=0 for i in range(len(self.trainData[0))]
  129. for i in range(len(self.trainData)):
  130. for j in range(len(self.w)):
  131. self.w[j]+=self.a[i]self.yL[i]self.xLi
  132. def __update(self):
  133. 更新 self.b 和 self.w

  134. self.__calW()
  135. 得到了self.w 下面求b

  136. print self.a

  137. maxf1=-99999
  138. min1=99999
  139. for k in range(len(self.trainData)):
  140. y_v=self.__Tdot(self.w,self.xL[k])
  141. print y_v

  142. if self.yL[k]==-1:
  143. if y_v>maxf1:
  144. maxf1=y_v
  145. else:
  146. if y_v

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

6

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广