一、简介
支持向量机是一类按监督学习方式对数据进行二元分类的广义线性分类器,其决策边界是对学习样本求解的最大边距超平面。SVM使用铰链损失函数计算经验风险并在求解系统中加入了正则化项以优化结构风险,是一个具有稀疏性和稳健性的分类器。SVM可以通过核方法进行非线性分类,是常见的核学习方法之一。
二、基本思想
1、它是针对线性可分情况进行分析,对于线性不可分的情况,通过使用非线性映射算法将低维输入空间线性不可分的样本转化为高维特征空间使其线性可分,从而使得高维特征空间采用线性算法对样本的非线性特征进行线性分析成为可能;
2、它基于结构风险最小化理论之上在特征空间中构建最优超平面,使得学习器得到全局最优化并且在整个样本空间的期望以某个概率满足一定上界。
三、最大间隔与分类
如果一个线性函数能够将样本分开,称这些数据样本是线性可分的。显然不只有这一条直线可以将样本分开,而是有无数条,我们所说的线性可分支持向量机就对应着能将数据正确划分并且间隔最大的直线。在样本空间中寻找一个超平面, 将不同类别的样本分开。它的容忍性好, 鲁棒性高, 泛化能力最强。


寻找最大间隔相当于寻找w和b使得以下式子最大

四、对偶问题
最大间隔问题的拉格朗日乘法
第一步:引入拉格朗日乘子 𝛼_𝑖≥0得到拉格朗日函数

第二步:令𝐿(𝑤,𝑏,𝛼) 对w和b的偏导为零

第三步:将w, b回代到第一步

五、求解SMO

基本思路:
不断执行如下两个步骤直至收敛.
第一步:选取一对需更新的变量𝛼𝑖和𝛼𝑗.
第二步:固定𝛼𝑖和 𝛼𝑗以外的参数, 求解对偶问题更新𝛼𝑖和𝛼𝑗.
算法流程:

设最优解为:

可得:

得到分类平面:

六、核函数
基本思想:不显示地构造核映射,而是设计核函数

Mercer定理(充分非必要):只要对称函数值所对应的核矩阵半正定, 则该函数可作为核函数.
常用核函数:

七、python实现
数据集:
1.196604 4.951851 1
0.275221 9.543647 -1
0.470575 9.332488 -1
-1.889567 9.542662 -1
-1.527893 12.150579 -1
-1.185247 11.309318 -1
-0.445678 3.297303 1
1.042222 6.105155 1
-0.618787 10.320986 -1
1.152083 0.548467 1
0.828534 2.676045 1
-1.237728 10.549033 -1
-0.683565 -2.166125 1
0.229456 5.921938 1
-0.959885 11.555336 -1
0.492911 10.993324 -1
0.184992 8.721488 -1
-0.355715 10.325976 -1
-0.397822 8.058397 -1
0.824839 13.730343 -1
1.507278 5.027866 1
0.099671 6.835839 1
-0.344008 10.717485 -1
1.785928 7.718645 1
-0.918801 11.560217 -1
-0.364009 4.747300 1
-0.841722 4.119083 1
0.490426 1.960539 1
-0.007194 9.075792 -1
0.356107 12.447863 -1
0.342578 12.281162 -1
-0.810823 -1.466018 1
2.530777 6.476801 1
1.296683 11.607559 -1
0.475487 12.040035 -1
-0.783277 11.009725 -1
0.074798 11.023650 -1
-1.337472 0.468339 1
-0.102781 13.763651 -1
-0.147324 2.874846 1
0.518389 9.887035 -1
1.015399 7.571882 -1
-1.658086 -0.027255 1
1.319944 2.171228 1
2.056216 5.019981 1
-0.851633 4.375691 1
-1.510047 6.061992 -1
-1.076637 -3.181888 1
1.821096 10.283990 -1
3.010150 8.401766 1
-1.099458 1.688274 1
-0.834872 -1.733869 1
-0.846637 3.849075 1
1.400102 12.628781 -1
1.752842 5.468166 1
0.078557 0.059736 1
0.089392 -0.715300 1
1.825662 12.693808 -1
0.197445 9.744638 -1
0.126117 0.922311 1
-0.679797 1.220530 1
0.677983 2.556666 1
0.761349 10.693862 -1
-2.168791 0.143632 1
1.388610 9.341997 -1
0.317029 14.739025 -1
代码:

点击查看代码
import matplotlib.pyplot as plt
import numpy as np
import random
 
def loadDataSet(fileName):
    dataMat = [];
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():  # 逐行读取,滤除空格等
        lineArr = line.strip().split()
        dataMat.append([float(lineArr[0]), float(lineArr[1])])  # 添加数据
        labelMat.append(float(lineArr[2]))  # 添加标签
    return dataMat, labelMat
 
 
def showDataSet(dataMat, labelMat):
    data_plus = []  # 正样本
    data_minus = []  # 负样本
    for i in range(len(dataMat)):
        if labelMat[i] > 0:
            data_plus.append(dataMat[i])
        else:
            data_minus.append(dataMat[i])
    data_plus_np = np.array(data_plus)  # 转换为numpy矩阵
    data_minus_np = np.array(data_minus)  # 转换为numpy矩阵
    plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1])  # 正样本散点图
    plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1])  # 负样本散点图
    plt.show()
 
 
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
    # 转换为numpy的mat存储
    dataMatrix = np.mat(dataMatIn);
    labelMat = np.mat(classLabels).transpose()
    # 初始化b参数,统计dataMatrix的维度
    b = 0;
    m, n = np.shape(dataMatrix)
    # 初始化alpha参数,设为0
    alphas = np.mat(np.zeros((m, 1)))
    # 初始化迭代次数
    iter_num = 0
    # 最多迭代matIter次
    while (iter_num < maxIter):
        alphaPairsChanged = 0
        for i in range(m):
            # 步骤1:计算误差Ei
            fXi = float(np.multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[i, :].T)) + b
            Ei = fXi - float(labelMat[i])
            # 优化alpha,设定一定的容错率。
            if ((labelMat[i] * Ei < -toler) and (alphas[i] < C)) or ((labelMat[i] * Ei > toler) and (alphas[i] > 0)):
                # 随机选择另一个与alpha_i成对优化的alpha_j
                j = selectJrand(i, m)
                # 步骤1:计算误差Ej
                fXj = float(np.multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[j, :].T)) + b
                Ej = fXj - float(labelMat[j])
                # 保存更新前的aplpha值,使用深拷贝
                alphaIold = alphas[i].copy();
                alphaJold = alphas[j].copy();
                # 步骤2:计算上下界L和H
                if (labelMat[i] != labelMat[j]):
                    L = max(0, alphas[j] - alphas[i])
                    H = min(C, C + alphas[j] - alphas[i])
                else:
                    L = max(0, alphas[j] + alphas[i] - C)
                    H = min(C, alphas[j] + alphas[i])
                if L == H: print("L==H"); continue
                # 步骤3:计算eta
                eta = 2.0 * dataMatrix[i, :] \
                      * dataMatrix[j,:].T - dataMatrix[i,:]\
                      * dataMatrix[i,:].T - dataMatrix[j,:]\
                      * dataMatrix[j, :].T
                if eta >= 0: print("eta>=0"); continue
                # 步骤4:更新alpha_j
                alphas[j] -= labelMat[j] * (Ei - Ej) / eta
                # 步骤5:修剪alpha_j
                alphas[j] = clipAlpha(alphas[j], H, L)
                if (abs(alphas[j] - alphaJold) < 0.00001): print("alpha_j变化太小"); continue
                # 步骤6:更新alpha_i
                alphas[i] += labelMat[j] * labelMat[i] * (alphaJold - alphas[j])
                # 步骤7:更新b_1和b_2
                b1 = b - Ei - labelMat[i] * (alphas[i] - alphaIold) * dataMatrix[i, :] * dataMatrix[i, :].T - labelMat[
                    j] * (alphas[j] - alphaJold) * dataMatrix[i, :] * dataMatrix[j, :].T
                b2 = b - Ej - labelMat[i] * (alphas[i] - alphaIold) * dataMatrix[i, :] * dataMatrix[j, :].T - labelMat[
                    j] * (alphas[j] - alphaJold) * dataMatrix[j, :] * dataMatrix[j, :].T
                # 步骤8:根据b_1和b_2更新b
                if (0 < alphas[i]) and (C > alphas[i]):
                    b = b1
                elif (0 < alphas[j]) and (C > alphas[j]):
                    b = b2
                else:
                    b = (b1 + b2) / 2.0
                # 统计优化次数
                alphaPairsChanged += 1
                # 打印统计信息
                print("第%d次迭代 样本:%d, alpha优化次数:%d" % (iter_num, i, alphaPairsChanged))
        # 更新迭代次数
        if (alphaPairsChanged == 0):
            iter_num += 1
        else:
            iter_num = 0
        print("迭代次数: %d" % iter_num)
    return b, alphas
 
def selectJrand(i, m):
    j = i  # 选择一个不等于i的j
    while (j == i):
        j = int(random.uniform(0, m))
    return j
 
 
def clipAlpha(aj, H, L):
    if aj > H:
        aj = H
    if L > aj:
        aj = L
    return aj
 
def showClassifer(dataMat, w, b):
    # 绘制样本点
    data_plus = []  # 正样本
    data_minus = []  # 负样本
    for i in range(len(dataMat)):
        if labelMat[i] > 0:
            data_plus.append(dataMat[i])
        else:
            data_minus.append(dataMat[i])
    data_plus_np = np.array(data_plus)  # 转换为numpy矩阵
    data_minus_np = np.array(data_minus)  # 转换为numpy矩阵
    plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1], s=30, alpha=0.7)  # 正样本散点图
    plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1], s=30, alpha=0.7)  # 负样本散点图
    # 绘制直线
    x1 = max(dataMat)[0]
    x2 = min(dataMat)[0]
    a1, a2 = w
    b = float(b)
    a1 = float(a1[0])
    a2 = float(a2[0])
    y1, y2 = (-b - a1 * x1) / a2, (-b - a1 * x2) / a2
    plt.plot([x1, x2], [y1, y2])
    # 找出支持向量点
    for i, alpha in enumerate(alphas):
        if abs(alpha) > 0:
            x, y = dataMat[i]
            plt.scatter([x], [y], s=150, c='none', alpha=0.7, linewidth=1.5, edgecolor='red')
    plt.show()
 
 
def get_w(dataMat, labelMat, alphas):
    alphas, dataMat, labelMat = np.array(alphas), np.array(dataMat), np.array(labelMat)
    w = np.dot((np.tile(labelMat.reshape(1, -1).T, (1, 2)) * dataMat).T, alphas)
    return w.tolist()
 
 
if __name__ == '__main__':
    dataMat, labelMat = loadDataSet('D:\\dataset\\testSet.txt')
    b, alphas = smoSimple(dataMat, labelMat, 0.6, 0.001, 40)
    w = get_w(dataMat, labelMat, alphas)
    showClassifer(dataMat, w, b)

结果截图: