1.支持向量机最简单的情况是线性可分支持向量机,或硬间隔支持向量机。构建它的条件是训练数据线性可分。其学习策略是最大间隔法。可以表示为凸二次规划问题,其原始最优化问题为
图片1.png
支持向量机 - 图2
求得最优化问题的解为支持向量机 - 图3支持向量机 - 图4,得到线性可分支持向量机,分离超平面是
支持向量机 - 图5
分类决策函数是
支持向量机 - 图6

最大间隔法中,函数间隔几何间隔是重要的概念。

线性可分支持向量机的最优解存在且唯一。位于间隔边界上的实例点为支持向量。最优分离超平面由支持向量完全决定。 二次规划问题的对偶问题是
支持向量机 - 图7
图片1.png
支持向量机 - 图9
通常,通过求解对偶问题学习线性可分支持向量机,即首先求解对偶问题的最优值支持向量机 - 图10,然后求最优值支持向量机 - 图11支持向量机 - 图12,得出分离超平面和分类决策函数。

2.现实中训练数据是线性可分的情形较少,训练数据往往是近似线性可分的,这时使用线性支持向量机,或软间隔支持向量机。线性支持向量机是最基本的支持向量机。
对于噪声或例外,通过引入松弛变量支持向量机 - 图13,使其“可分”,得到线性支持向量机学习的凸二次规划问题,其原始最优化问题是
图片1.png
图片1.png
支持向量机 - 图16
求解原始最优化问题的解支持向量机 - 图17支持向量机 - 图18,得到线性支持向量机,其分离超平面为
支持向量机 - 图19
分类决策函数为
支持向量机 - 图20
线性可分支持向量机的解支持向量机 - 图21唯一但支持向量机 - 图22不唯一。对偶问题是
图片1.png
支持向量机 - 图24
线性支持向量机的对偶学习算法,首先求解对偶问题得到最优解支持向量机 - 图25,然后求原始问题最优解支持向量机 - 图26支持向量机 - 图27,得出分离超平面和分类决策函数。
对偶问题的解支持向量机 - 图28中满足支持向量机 - 图29的实例点支持向量机 - 图30称为支持向量。支持向量可在间隔边界上,也可在间隔边界与分离超平面之间,或者在分离超平面误分一侧。最优分离超平面由支持向量完全决定。
线性支持向量机学习等价于最小化二阶范数正则化的合页函数
支持向量机 - 图31

3.非线性支持向量机
对于输入空间中的非线性分类问题,可以通过非线性变换将它转化为某个高维特征空间中的线性分类问题,在高维特征空间中学习线性支持向量机。由于在线性支持向量机学习的对偶问题里,目标函数和分类决策函数都只涉及实例与实例之间的内积,所以不需要显式地指定非线性变换,而是用核函数来替换当中的内积。核函数表示,通过一个非线性转换后的两个实例间的内积。具体地,支持向量机 - 图32是一个核函数,或正定核,意味着存在一个从输入空间x到特征空间的映射支持向量机 - 图33,对任意支持向量机 - 图34,有
支持向量机 - 图35
对称函数支持向量机 - 图36为正定核的充要条件如下:对任意
支持向量机 - 图37
任意正整数支持向量机 - 图38,对称函数支持向量机 - 图39对应的Gram矩阵是半正定的。
所以,在线性支持向量机学习的对偶问题中,用核函数支持向量机 - 图40替代内积,求解得到的就是非线性支持向量机
支持向量机 - 图41

4.SMO算法(序列最小最优化算法,sequential minimal optimization)
SMO算法是支持向量机学习的一种快速算法,其特点是不断地将原二次规划问题分解为只有两个变量的二次规划子问题,并对子问题进行解析求解,直到所有变量满足KKT条件为止。这样通过启发式的方法得到原二次规划问题的最优解。因为子问题有解析解,所以每次计算子问题都很快,虽然计算子问题次数很多,但在总体上还是高效的。


  • 分离超平面:支持向量机 - 图42
  • 点到直线距离:支持向量机 - 图43
  • 支持向量机 - 图44为2-范数:支持向量机 - 图45
  • 直线为超平面,样本可表示为:

    1. ![](https://cdn.nlark.com/yuque/0/2020/svg/1061864/1601955571709-587f79cd-78aa-4bc1-9928-c4c51d2a110d.svg#align=left&display=inline&height=13&margin=%5Bobject%20Object%5D&originHeight=13&originWidth=102&size=0&status=done&style=none&width=102)<br /> ![](https://cdn.nlark.com/yuque/0/2020/svg/1061864/1601956185203-8d7c7e6c-72d6-4b7b-975c-3ad16706abab.svg#align=left&display=inline&height=13&margin=%5Bobject%20Object%5D&originHeight=13&originWidth=102&size=0&status=done&style=none&width=102)

margin:

  • 函数间隔支持向量机 - 图46
  • 几何间隔支持向量机 - 图47,当数据被正确分类时,几何间隔就是点到超平面的距离

为了求几何间隔最大,SVM基本问题可以转化为求解:(支持向量机 - 图48为几何间隔,(支持向量机 - 图49为函数间隔)
支持向量机 - 图50

分类点几何间隔最大,同时被正确分类。但这个方程并非凸函数求解,所以要先①将方程转化为凸函数,②用拉格朗日乘子法和KKT条件求解对偶问题。
①转化为凸函数:
先令支持向量机 - 图51,方便计算(参照衡量,不影响评价结果)
支持向量机 - 图52
再将支持向量机 - 图53转化成支持向量机 - 图54求解凸函数,1/2是为了求导之后方便计算。
支持向量机 - 图55

②用拉格朗日乘子法和KKT条件求解最优值:
支持向量机 - 图56
整合成:
支持向量机 - 图57

推导:支持向量机 - 图58
根据KKT条件:
支持向量机 - 图59
代入支持向量机 - 图60
支持向量机 - 图61
支持向量机 - 图62
支持向量机 - 图63
支持向量机 - 图64
支持向量机 - 图65
再把max问题转成min问题:
支持向量机 - 图66
支持向量机 - 图67
支持向量机 - 图68
以上为SVM对偶问题的对偶形式


kernel

在低维空间计算获得高维空间的计算结果,也就是说计算结果满足高维(满足高维,才能说明高维下线性可分)。

soft margin & slack variable

引入松弛变量支持向量机 - 图69,对应数据点允许偏离的functional margin 的量。
目标函数:
图片1.png
对偶问题:
图片1.png


Sequential Minimal Optimization

首先定义特征到结果的输出函数:支持向量机 - 图72.
因为支持向量机 - 图73
支持向量机 - 图74


支持向量机 - 图75


实现SVM

  1. import numpy as np
  2. import pandas as pd
  3. from sklearn.datasets import load_iris
  4. from sklearn.model_selection import train_test_split
  5. import matplotlib.pyplot as plt
  6. %matplotlib inline
  7. # data
  8. def create_data():
  9. iris = load_iris()
  10. df = pd.DataFrame(iris.data, columns=iris.feature_names)
  11. df['label'] = iris.target
  12. df.columns = [
  13. 'sepal length', 'sepal width', 'petal length', 'petal width', 'label'
  14. ]
  15. data = np.array(df.iloc[:100, [0, 1, -1]])
  16. for i in range(len(data)):
  17. if data[i, -1] == 0:
  18. data[i, -1] = -1
  19. # print(data)
  20. return data[:, :2], data[:, -1]
  21. X, y = create_data()
  22. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
  23. plt.scatter(X[:50,0],X[:50,1], label='0')
  24. plt.scatter(X[50:,0],X[50:,1], label='1')
  25. plt.legend()
  26. Out[4]:
  27. <matplotlib.legend.Legend at 0x1d96e8af308>

image.png

  1. class SVM:
  2. def __init__(self, max_iter=100, kernel='linear'):
  3. self.max_iter = max_iter
  4. self._kernel = kernel
  5. def init_args(self, features, labels):
  6. self.m, self.n = features.shape
  7. self.X = features
  8. self.Y = labels
  9. self.b = 0.0
  10. # 将Ei保存在一个列表里
  11. self.alpha = np.ones(self.m)
  12. self.E = [self._E(i) for i in range(self.m)]
  13. # 松弛变量
  14. self.C = 1.0
  15. def _KKT(self, i):
  16. y_g = self._g(i) * self.Y[i]
  17. if self.alpha[i] == 0:
  18. return y_g >= 1
  19. elif 0 < self.alpha[i] < self.C:
  20. return y_g == 1
  21. else:
  22. return y_g <= 1
  23. # g(x)预测值,输入xi(X[i])
  24. def _g(self, i):
  25. r = self.b
  26. for j in range(self.m):
  27. r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j])
  28. return r
  29. # 核函数
  30. def kernel(self, x1, x2):
  31. if self._kernel == 'linear':
  32. return sum([x1[k] * x2[k] for k in range(self.n)])
  33. elif self._kernel == 'poly':
  34. return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1)**2
  35. return 0
  36. # E(x)为g(x)对输入x的预测值和y的差
  37. def _E(self, i):
  38. return self._g(i) - self.Y[i]
  39. def _init_alpha(self):
  40. # 外层循环首先遍历所有满足0<a<C的样本点,检验是否满足KKT
  41. index_list = [i for i in range(self.m) if 0 < self.alpha[i] < self.C]
  42. # 否则遍历整个训练集
  43. non_satisfy_list = [i for i in range(self.m) if i not in index_list]
  44. index_list.extend(non_satisfy_list)
  45. for i in index_list:
  46. if self._KKT(i):
  47. continue
  48. E1 = self.E[i]
  49. # 如果E2是+,选择最小的;如果E2是负的,选择最大的
  50. if E1 >= 0:
  51. j = min(range(self.m), key=lambda x: self.E[x])
  52. else:
  53. j = max(range(self.m), key=lambda x: self.E[x])
  54. return i, j
  55. def _compare(self, _alpha, L, H):
  56. if _alpha > H:
  57. return H
  58. elif _alpha < L:
  59. return L
  60. else:
  61. return _alpha
  62. def fit(self, features, labels):
  63. self.init_args(features, labels)
  64. for t in range(self.max_iter):
  65. # train
  66. i1, i2 = self._init_alpha()
  67. # 边界
  68. if self.Y[i1] == self.Y[i2]:
  69. L = max(0, self.alpha[i1] + self.alpha[i2] - self.C)
  70. H = min(self.C, self.alpha[i1] + self.alpha[i2])
  71. else:
  72. L = max(0, self.alpha[i2] - self.alpha[i1])
  73. H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])
  74. E1 = self.E[i1]
  75. E2 = self.E[i2]
  76. # eta=K11+K22-2K12
  77. eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel(self.X[i2],self.X[i2])
  78. - 2 * self.kernel(self.X[i1], self.X[i2])
  79. if eta <= 0:
  80. # print('eta <= 0')
  81. continue
  82. alpha2_new_unc = self.alpha[i2] + self.Y[i2] * (E1 - E2) / eta
  83. alpha2_new = self._compare(alpha2_new_unc, L, H)
  84. alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (
  85. self.alpha[i2] - alpha2_new)
  86. b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * (
  87. alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
  88. self.X[i2],
  89. self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.b
  90. b2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * (
  91. alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
  92. self.X[i2],
  93. self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.b
  94. if 0 < alpha1_new < self.C:
  95. b_new = b1_new
  96. elif 0 < alpha2_new < self.C:
  97. b_new = b2_new
  98. else:
  99. # 选择中点
  100. b_new = (b1_new + b2_new) / 2
  101. # 更新参数
  102. self.alpha[i1] = alpha1_new
  103. self.alpha[i2] = alpha2_new
  104. self.b = b_new
  105. self.E[i1] = self._E(i1)
  106. self.E[i2] = self._E(i2)
  107. return 'train done!'
  108. def predict(self, data):
  109. r = self.b
  110. for i in range(self.m):
  111. r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])
  112. return 1 if r > 0 else -1
  113. def score(self, X_test, y_test):
  114. right_count = 0
  115. for i in range(len(X_test)):
  116. result = self.predict(X_test[i])
  117. if result == y_test[i]:
  118. right_count += 1
  119. return right_count / len(X_test)
  120. def _weight(self):
  121. # linear model
  122. yx = self.Y.reshape(-1, 1) * self.X
  123. self.w = np.dot(yx.T, self.alpha)
  124. return self.w
  125. In [6]:
  126. svm = SVM(max_iter=200)
  127. In [7]:
  128. svm.fit(X_train, y_train)
  129. Out[7]:
  130. 'train done!'
  131. In [8]:
  132. svm.score(X_test, y_test)
  133. Out[8]:
  134. 0.64

scikit-learn实例

from sklearn.svm import SVC
clf = SVC()
clf.fit(X_train, y_train)
Out[9]:
SVC()

clf.score(X_test, y_test)
Out[10]:
0.96

sklearn.svm.SVC

(C=1.0, kernel=’rbf’, degree=3, gamma=’auto’, coef0=0.0, shrinking=True, probability=False,tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape=None, random_state=None)

参数:

  • C:C-SVC的惩罚参数C?默认值是1.0

C越大,相当于惩罚松弛变量,希望松弛变量接近0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样对训练集测试时准确率很高,但泛化能力弱。C值小,对误分类的惩罚减小,允许容错,将他们当成噪声点,泛化能力较强。

  • kernel :核函数,默认是rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’
    – 线性:u’v
    – 多项式:(gamma_u’_v + coef0)^degree
    – RBF函数:exp(-gamma|u-v|^2)
    – sigmoid:tanh(gamma_u’_v + coef0)

  • degree :多项式poly函数的维度,默认是3,选择其他核函数时会被忽略。

  • gamma : ‘rbf’,‘poly’ 和‘sigmoid’的核函数参数。默认是’auto’,则会选择1/n_features

  • coef0 :核函数的常数项。对于‘poly’和 ‘sigmoid’有用。

  • probability :是否采用概率估计?.默认为False

  • shrinking :是否采用shrinking heuristic方法,默认为true

  • tol :停止训练的误差值大小,默认为1e-3

  • cache_size :核函数cache缓存大小,默认为200

  • class_weight :类别的权重,字典形式传递。设置第几类的参数C为weight*C(C-SVC中的C)

  • verbose :允许冗余输出?

  • max_iter :最大迭代次数。-1为无限制。

  • decision_function_shape :‘ovo’, ‘ovr’ or None, default=None3

  • random_state :数据洗牌时的种子值,int值

主要调节的参数有:C、kernel、degree、gamma、coef0。