SVM支持向量机的实现

首次上传时间:2019-4-16(持续修改)
由于暂时没有深入研究,理论方面暂时不写了。具体参考大牛写的,据说修补三年成型。如果你要了解SVM,打滚推荐。
传送点:https://blog.csdn.net/v_july_v/article/details/7624837

下面的代码几乎每一行都一一和上面博客对照了,有问题欢迎讨论。

实现思路:

出于测试的目的,我将整个代码分为了三个独立运行的文件。

  • 文件data.py:这是一份数据伪造代码,我假设我们要找的线性分类器是:
    y = 2 x + 1 y=2*x+1
    然后我随机在这条线的上下两侧,分别随机取10个点(点越多训练结果越准确)。直线上面10个点为正类,标记值为1。直线下面10个点为负类,标记值为-1。然后其放入一个n*3(我这里是20)的矩阵中,随机打乱其顺序,这样得到数据集。存放到了 ‘D:/pycharm/aicourse/test.txt’

  • 文件visual.py:这份代码,纯粹是参照,目的是把自己伪造的点可视化,与最终结果对比用的

  • 文件svm.py:这是算法的主要实现代码。这份代码几乎是参考网上的,运用时是SMO算法。过程大致是:

    确定目标函数约束条件(损失函数)→转化为拉格朗日函数→转化为对偶函数(由于是2维,不需要核函数降维)→SMO算法

因为我是根据已知线伪造的数据,所以我没有进行精确度测试,直接用原直线和训练结果对比。蓝色为已知直线,红色为训练之后得到的直线,画圈的点为支持向量,结果还是可以的。此外,我的数据是随机伪造的,每次运行data.py都不同。在这里插入图片描述

代码

​ data.py文件


"""
2019-3-27
这是制造SVM的数据集代码,点集存放在D:/pycharm/aicourse/test.txt'文件
生成2*x+1面两侧的随机点,一面有tol_point个点

"""

import numpy as np

filename = 'D:/pycharm/aicourse/test.txt'
tol_point = 10

x0 = np.random.uniform(0, 100, tol_point)
y0 = np.random.uniform(0, 2*x0+1, tol_point)
x1 = np.random.uniform(0, 100, tol_point)
y1 = np.random.uniform(2*x1+1, 201, tol_point)
np.set_printoptions(precision=2)
z = [-1 for i in range(2*tol_point)]
for i in range(tol_point):
    z[i+tol_point] = 1

x = np.append(x0, x1)
y = np.append(y0, y1)

poit_set = x, y, z
t_set = np.mat(poit_set).T

np.random.shuffle(t_set)
np.savetxt(filename, t_set)




​ visualdata.py文件

"""
2019-3-27
点集存放在D:/pycharm/aicourse/test.txt'文件
将点集在坐标轴上显示

"""

import numpy as np
import matplotlib.pylab as plt

filename = 'D:/pycharm/aicourse/test.txt'


tol_point = 100  # 点集数量
face_x = np.arange(0, tol_point)
face_y = 2*face_x + 1
"""
函数说明:读取数据
Parameters:
    fileName - 文件名
Returns:
    dataMat - 数据矩阵
    labelMat - 数据标签
"""


def loadDataSet(fileName):
    dataMat = [];
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():  # 逐行读取,滤除空格等
        lineArr = line.strip().split(' ')
        # dataMat.append([map(float, lineArr[0]), map(float, lineArr[1])])  # 添加数据
        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.plot(face_x, face_y, color='red', linewidth=1.5, linestyle='-')
    plt.show()

if __name__ == '__main__':
    dataMat, labelMat = loadDataSet(filename)
    dataMatrix = np.mat(dataMat)
    labelMat = np.mat(labelMat).transpose()

    showDataSet(dataMat, labelMat)

​ svm.py文件

from time import sleep
import matplotlib.pylab as plt
import numpy as np
import random


filename = 'D:/pycharm/aicourse/test.txt'
"""
函数说明:读取数据

"""

def loadDataSet (fileName):
    dataMat = [];
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():  # 逐行读取,滤除空格等
        lineArr = line.strip().split(' ')
        # dataMat.append([map(float, lineArr[0]), map(float, lineArr[1])])  # 添加数据
        dataMat.append([float(lineArr[0]), float(lineArr[1])])  # 添加数据
        labelMat.append(float(lineArr[2]))  # 添加标签
    return dataMat, labelMat

"""
函数说明:随机选择alpha

Parameters:
    i:alpha
    m:alpha参数个数
"""

def selectJrand(i, m):
    j = i
    # 选择一个不等于i的j
    while (j == i):
        j = int(random.uniform(0, m))
    return j


"""
函数说明:修剪alpha
Parameters:
    aj:alpha的值
    H:alpha上限
    L:alpha下限

Returns:
    aj:alpha的值
"""


def clipAlpha(aj, H, L):
    if aj > H:
        aj = H
    if L > aj:
        aj = L
    return aj


"""
函数说明:简化版SMO算法

Parameters:
    dataMatIn:数据矩阵
    classLabels:数据标签
    C:松弛变量
    toler:容错率
    maxIter:最大迭代次数
"""


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  # y = wx+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    # LS的二阶导数,eta是阿尔法的更新步长,也叫学习率
                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 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]
    print(w)
    print(b)
    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])
    init_x = np.arange(0, 100)  # 画出真实函数
    init_y = 2*init_x + 1       # 画出真实函数
    plt.plot(init_x, init_y, color='red', linewidth=2.5, linestyle='-')      # 画出真实函数
    # 找出支持向量点
    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()

"""
函数说明:计算w
"""


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(filename)
    b, alphas = smoSimple(dataMat, labelMat, 0.6, 0.001, 40)
    w = get_w(dataMat, labelMat, alphas)
    showClassifer(dataMat, w, b)

猜你喜欢

转载自blog.csdn.net/weixin_42406148/article/details/89336915