1、理论

1.1 间隔与间隔最大化

      给定训练样本样本集D={(X1,y1),(X2,y2), (X3,y3),…,(Xm,ym)},其中yispark 线性支持向量机算法参数 支持向量机线性核_确信度{-1, +1}。

      对于一个二分类问题,我们的基本思想是基于训练集D在样本空间中找到一个划分超平面,将不同类别的样本分开。但是如下所示:

spark 线性支持向量机算法参数 支持向量机线性核_确信度_02


图1 存在多个超平面将二分类问题数据进行分开


      能将训练样本分开的超平面有很多,哪一个是最好的呢?
      由于我们的训练集的局限性和噪声影响,训练集外的样本可能更接近两个类的分隔,这将使许多超平面出现错误,所以最好的分离超平面应该是处于"正中间"的划分超平面,这时的分离超平面是最鲁棒的。

(1) 超平面

      在样本空间中,划分超平面的通过如下方程表示:
spark 线性支持向量机算法参数 支持向量机线性核_最小值_03
      其中spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_04= (spark 线性支持向量机算法参数 支持向量机线性核_确信度_051, spark 线性支持向量机算法参数 支持向量机线性核_确信度_052,spark 线性支持向量机算法参数 支持向量机线性核_确信度_053,…,spark 线性支持向量机算法参数 支持向量机线性核_确信度_05n)为法向量,决定超平面的方向;
             b为位移项,决定超平面和原点之间的距离。

(2) 函数间隔和几何间隔

      一般来说,一个点距离分离超平面的远近可以表示分类预测的确信程度。在超平面spark 线性支持向量机算法参数 支持向量机线性核_最小值_09 确定的情况下,|spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_10| 能够相对的表示点x距离超平面的远近。并且spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_10 的符号与类标记y的符号能否一致能够表示分类是否正确。
      所以最后我们可以用 spark 线性支持向量机算法参数 支持向量机线性核_样本空间_12 来度量分类的正确性和确信度,即函数间隔
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_13
      其中定义超平面 spark 线性支持向量机算法参数 支持向量机线性核_样本空间_14 关于训练集D的函数间隔为超平面 spark 线性支持向量机算法参数 支持向量机线性核_样本空间_14 关于D中所有样本点的函数间隔中最小值,即:
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_16
      函数间隔可以表示分类预测的正确性及确信度,但是选择分离超平面时候,只有函数间隔是不够的。因为只要成比例的改变 spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_04spark 线性支持向量机算法参数 支持向量机线性核_样本空间_18 ,超平面并不会发生改变,但是间隔也成比例的改变了,不能准确的表示确信度。所以我们下面要对函数间隔进行归一化,即几何间隔
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_19
      其中,||spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_04||为spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_04spark 线性支持向量机算法参数 支持向量机线性核_最小值_22范数。其中定义超平面 spark 线性支持向量机算法参数 支持向量机线性核_样本空间_14 关于训练集D的几何间隔为超平面 spark 线性支持向量机算法参数 支持向量机线性核_样本空间_14 关于D中所有样本点的函数间隔中最小值,即:
spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_25
      所以一般地,当样本点被超平面正确分类时,点spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_26与超平面的几何距离是:spark 线性支持向量机算法参数 支持向量机线性核_样本空间_27

(3) 间隔最大化

      支持向量机学习的基本想法是求解能够正确划分训练数据集并且几何间距最大的分离超平面。
      对线性可分的训练数据集而言,线性可分分离超平面有无穷多个,但是几何间距最大的分离超平面是唯一的。这里的间隔最大化又称为硬间隔最大化。(与将要讨论的训练数据集近似线性可分时的软间隔最大化相对应)
      间隔最大化的直观解释是:对训练数据集找到几何间隔最大的超平面意味着以充分的确信度对训练数据进行分类。即找到支持向量对应的几何距离即可。
      假设超平面(spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_04, spark 线性支持向量机算法参数 支持向量机线性核_样本空间_18)能将训练样本正确分开,即对应(spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_26,spark 线性支持向量机算法参数 支持向量机线性核_最小值_31),若spark 线性支持向量机算法参数 支持向量机线性核_样本空间_32,则有spark 线性支持向量机算法参数 支持向量机线性核_确信度_33;若有spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_34,则有spark 线性支持向量机算法参数 支持向量机线性核_确信度_35。其中支持向量表示为:spark 线性支持向量机算法参数 支持向量机线性核_最小值_36。两个异类支持向量到超平面的距离为:spark 线性支持向量机算法参数 支持向量机线性核_样本空间_37
      此时欲找到"最大间隔"的划分超平面,即满足:spark 线性支持向量机算法参数 支持向量机线性核_样本空间_38
      等价于:
spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_39
      这是一个凸二次规划问题。

1.2 拉格朗日对偶问题

      为了求解线性可分支持向量机的最优化问题,将它作为原始最优化问题,应用拉格朗日对偶性,通过求解对偶问题得到原始问题的最优解。

(1) 拉格朗日对偶函数

      我们的原始问题为:
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_40
      我们将这个问题一般化:
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_41
      首先构建拉格朗日函数。为此,对每一个不等式约束,引进拉格朗日乘子spark 线性支持向量机算法参数 支持向量机线性核_样本空间_42spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_43,定义拉格朗日函数为:
spark 线性支持向量机算法参数 支持向量机线性核_确信度_44
      其中我们令:
spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_45
      这个函数 spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_46 对于满足原始问题约束条件的那些 spark 线性支持向量机算法参数 支持向量机线性核_确信度_47 来说,其值等于 spark 线性支持向量机算法参数 支持向量机线性核_最小值_48。因为满足约束条件的 spark 线性支持向量机算法参数 支持向量机线性核_确信度_47 会使得 spark 线性支持向量机算法参数 支持向量机线性核_样本空间_50 ,因此最后一项消掉了,而 spark 线性支持向量机算法参数 支持向量机线性核_最小值_51 ,并且我们要求了 spark 线性支持向量机算法参数 支持向量机线性核_确信度_52 ,因此 spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_53 ,所以最大值只能在它们都取零的时候得到,这个时候就只剩下 spark 线性支持向量机算法参数 支持向量机线性核_最小值_48 了。因此,对于满足约束条件的那些 spark 线性支持向量机算法参数 支持向量机线性核_确信度_47 来说,spark 线性支持向量机算法参数 支持向量机线性核_确信度_56 。这样一来,原始的带约束的优化问题其实等价于如下的无约束优化问题:
spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_57
      我们把上述式子称为原始问题的,下面等式为原始问题的对偶问题:
spark 线性支持向量机算法参数 支持向量机线性核_确信度_58

(2) KKT条件推导

      求解上面的对偶问题,我们同样可以使用等式约束条件的求解思路,对所有的参数进行求导,但是对于求解出的最优解,必须满足KKT条件 (Karush-Kuhn-Tucker)。
      我们令:
spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_59
      求解析式可先将 spark 线性支持向量机算法参数 支持向量机线性核_确信度_60 看成是关于 spark 线性支持向量机算法参数 支持向量机线性核_确信度_47 的函数,而将拉格朗日乘子看作常数,求出 spark 线性支持向量机算法参数 支持向量机线性核_确信度_60 的极小值点,再将该点代入 spark 线性支持向量机算法参数 支持向量机线性核_确信度_60 ,得到的关于 spark 线性支持向量机算法参数 支持向量机线性核_最小值_64spark 线性支持向量机算法参数 支持向量机线性核_确信度_65 的表达式就是对偶函数。
对偶函数具有如下两条重要性质
i. 对偶函数一定是凹函数,其凹性与原目标函数和约束函数凹凸与否无关。
ii. 对 spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_66(泛指向量中的每个分量),如果原问题最优解对应的目标函数值为 spark 线性支持向量机算法参数 支持向量机线性核_最小值_67 ,则 spark 线性支持向量机算法参数 支持向量机线性核_最小值_68

1 slater条件

spark 线性支持向量机算法参数 支持向量机线性核_样本空间_69 ,对偶函数 spark 线性支持向量机算法参数 支持向量机线性核_样本空间_70 是原问题最优值 spark 线性支持向量机算法参数 支持向量机线性核_确信度_71 的一个下界,最好的下界就是最大化对偶函数,因此构造原问题的对偶问题:式(13)和式(14)。
      由于对偶函数是凹函数,故拉格朗日对偶问题一定是凸优化问题,其对应的最优解为 spark 线性支持向量机算法参数 支持向量机线性核_确信度_72(最优拉格朗日乘子),若对应的最优值为 spark 线性支持向量机算法参数 支持向量机线性核_确信度_73 ,则总有spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_74
其中:
1、当 spark 线性支持向量机算法参数 支持向量机线性核_最小值_75时,称为弱对偶(weak duality)
2、当 spark 线性支持向量机算法参数 支持向量机线性核_样本空间_76
3、将 spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_77
注:在解存在的情况下,弱对偶总是成立的。满足强对偶时,可以通过求解对偶问题来得到原始问题的解。
      Slater 条件用于判断什么情况下强对偶是成立的。在原问题是凸问题的情况下,若spark 线性支持向量机算法参数 支持向量机线性核_最小值_78 ,使得约束条件满足:
spark 线性支持向量机算法参数 支持向量机线性核_确信度_79
      则强对偶成立。
      其中spark 线性支持向量机算法参数 支持向量机线性核_确信度_80 表示原始凸问题定义域的相对内部,即在定义域上除了边界点以外的所有点。只要能找到一个这样的点使原凸问题等式约束依然成立且不等式约束都严格小于 spark 线性支持向量机算法参数 支持向量机线性核_确信度_81 即可。对大多数一般的原凸问题,强对偶都是成立的。

2 KKT条件

spark 线性支持向量机算法参数 支持向量机线性核_确信度_82
      如果原始问题是一个凸优化问题,且存在 spark 线性支持向量机算法参数 支持向量机线性核_确信度_83spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_84 满足 KKT 条件,那么它们分别是 原始问题 和 对偶问题 的极值点并且强对偶性成立。(因为SVM是一个凸二次规划问题,且满足KKT条件,所以可以用对偶问题进行解决。)

1.3 求解对偶问题

spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_85
      其中:
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_86
      所以,为了得到对偶问题的解,需要先求spark 线性支持向量机算法参数 支持向量机线性核_确信度_87spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_88 的极小,然后再求对spark 线性支持向量机算法参数 支持向量机线性核_最小值_64的极小。

(1) 求spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_90

spark 线性支持向量机算法参数 支持向量机线性核_确信度_87分别对spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_88求偏导数并令其等于0。
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_93
      最后得:
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_94
      将式(19)代入式(18),并利用式(20),即得:
spark 线性支持向量机算法参数 支持向量机线性核_确信度_95
      即:
spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_96

(2) 求spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_97spark 线性支持向量机算法参数 支持向量机线性核_最小值_64的极大,即是对偶问题:

spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_99
      考虑原始最优化问题和对偶最优化问题,原始问题满足强对偶条件,所以存在spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_100,使spark 线性支持向量机算法参数 支持向量机线性核_样本空间_101是原始问题的解,spark 线性支持向量机算法参数 支持向量机线性核_最小值_102是对偶问题的解。
      对线性可分训练数据集,假设对偶最优化问题对spark 线性支持向量机算法参数 支持向量机线性核_最小值_64的解为spark 线性支持向量机算法参数 支持向量机线性核_样本空间_104
      根据KKT条件,有:
spark 线性支持向量机算法参数 支持向量机线性核_确信度_105
      由此得:
spark 线性支持向量机算法参数 支持向量机线性核_确信度_106
      其中至少有一个spark 线性支持向量机算法参数 支持向量机线性核_样本空间_107(用反证法,假设spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_108,由上式可得spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_109,而spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_109不是原始最优化问题的解,产生矛盾),对此spark 线性支持向量机算法参数 支持向量机线性核_确信度_111有:
spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_112
      将式24代入式25中,且注意到spark 线性支持向量机算法参数 支持向量机线性核_样本空间_113可得:
spark 线性支持向量机算法参数 支持向量机线性核_最小值_114
      最后根据式24和式26可得,分离超平面可以写成:
spark 线性支持向量机算法参数 支持向量机线性核_样本空间_115
      分类决策函数可以写成:
spark 线性支持向量机算法参数 支持向量机线性核_确信度_116
      我们可以得知,分类决策函数只依赖于输入spark 线性支持向量机算法参数 支持向量机线性核_spark 线性支持向量机算法参数_117和训练样本输入的内积。

2、实践

# 导入numpy
import numpy as np
# 导入画图工具
import matplotlib.pyplot as plt
# 导入支持向量机svm
from sklearn import svm
# 导入数据集生成工具
from sklearn.datasets import make_blobs

# 先创建50个数据点,让他们分为两类
X, y = make_blobs(n_samples=50, centers=2, random_state=6)
# 创建一个线性内核的支持向量机模型
clf = svm.SVC(kernel='linear', C=1000)
clf.fit(X, y)
# 把数据点画出来
plt.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap=plt.cm.Paired)

# 建立图像坐标
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# 生成两个等差数列
xx = np.linspace(xlim[0], xlim[1], 30)
yy = np.linspace(ylim[0], ylim[1], 30)
YY, XX = np.meshgrid(yy, xx)
xy = np.vstack([XX.ravel(), YY.ravel()]).T
Z = clf.decision_function(xy).reshape(XX.shape)

# 把分类的决定边界画出来
ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=100, linewidth=1, facecolors='none')
plt.savefig('./svm.png')
plt.show()

      最后输出的图为:

spark 线性支持向量机算法参数 支持向量机线性核_确信度_118


图2 线性可分SVM图

3、面试算法题之剑指offer1:找出数组中重复的数字

3.1 题目描述

      给定一个长度为 n 的整数数组 nums,数组中所有的数字都在 0∼n−1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
      注意:如果某些数字不在 0∼n−1 的范围内,或数组中不包含重复数字,则返回 -1;
      样例:给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。返回 2 或 3。

解题思路:遍历数组,然后将每个数字放在改放的位置,如nums[0]=0,nums[1]=1,....。
                 时间复杂度:O(N)
from collections import defaultdict
class Solution(object):
    def duplicateInArray(self, nums):
        """
        :type nums: List[int]
        :rtype int
        """
        counters = defaultdict(lambda:0)
        range_right = len(nums)
        res = -1        # save duplicate num
        for num in nums:
            if num not in range(range_right):
                return -1   # none legal num in range 0 ~ n-1
            if counters[num] > 0:
                res = num
            else:
                counters[num] += 1
        return res

4、参考文献

(1)《统计学习方法》
(2)《机器学习》
(3) Python-SVM (4)《剑指offer》