欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

机器学习实战(python3.7也可跑)-支持向量机

程序员文章站 2024-02-03 21:09:10
...

如果觉得本篇文章对您的学习起到帮助作用,请 点赞 + 关注 + 评论 ,留下您的足迹????????????

本篇文章为我对机器学习实战-支持向量机的理解与我在学习时所做笔记,一是为了日后查找方便并加深对代码的理解,二是希望能帮助到使用这本书遇到困难的人。

代码可在python3.7跑通
因此代码相对原书做了一些修改,增加了可读性,同时也解决了一些问题。

代码svm.py及详细注释如下:

import numpy as np
import matplotlib as mpl
import  matplotlib.pyplot as plt

def loadDataset(filename):
    '''
    # 加载数据,第一、二列以列表形式保存到 dataMat 中,第三列标签保存到 labelMat 中
    :param filename: 数据为文本文件;由三列组成,其中第三列为标签
    :return:
    '''
    dataMat = list(); labelMat = list()
    with open(filename) as fr:
        for line in fr.readlines():
            # .split('\t')以制表符为分隔符将字符串分拆成多个部分,并存储到列表中
            # .strip()该方法只能删除开头或是结尾的字符,不能删除中间部分的字符
            lineArr = line.strip().split('\t')
            # 数据需要由字符串转化为浮点型
            dataMat.append([float(lineArr[0]), float(lineArr[1])])
            labelMat.append(float(lineArr[2]))
    dataMat = np.array(dataMat)
    # 转化为(-1, 1)的数据,可进行后面乘法计算相乘
    labelMat = np.array(labelMat).reshape((-1, 1))
    return dataMat, labelMat

def selectJrand(i, m):
    '''
    # SMO 算法的辅助函数
    # 只要函数值不等于输入值i,函数就会进行随机选择
    # 用于随机选择一个与 i 不同的值,用于内层循环的alpha2
    :param i: 第一个alpha的下标
    :param m: 所有alpha的数目为m
    :return:
    '''
    j = i
    while(j==i):
        j = int(np.random.uniform(0, m))
    return j

def clipAlpha(aj, H, L):
    '''
    # SMO 算法的辅助函数
    # 裁剪函数;SMO算法所选择的两个alpha,是有范围的,L< alpha <H
    :param aj: 计算得到第j个alpha的值
    :param H: alpha的上界
    :param L: alpha的下界
    :return:
    '''
    if aj > H:
        aj = H
    if L > aj:
        aj = L
    return aj

def draw(inX, wStar, bStar):
    '''
    # 画数据点,以及分割超平面
    :param inX: 输入数据点
    :param wStar: 参数w
    :param bStar: 参数b
    :return:
    '''
    # 显示汉字
    mpl.rcParams['font.sans-serif'] = [u'SimHei']
    # 解决负号错误问题
    mpl.rcParams['axes.unicode_minus'] = False

    inX = np.array(inX)
    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(111)

    ax.plot(inX[:,0], inX[:,1], 'go', linewidth=1)
    x = np.arange(-3.0, 10.0, 0.1)
    y = (-bStar[0] - wStar[0, 0] * x) / wStar[0, 1]
    ax.plot(x, y, 'r-', linewidth=2)
    plt.title('支持向量机', fontsize=18)
    plt.xlabel('X1', fontsize=15)
    plt.ylabel('X2', fontsize=15)
    plt.show()


def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
    '''
    # 简单版本SMO,第一个参数选择违反KKT最严重的点,第二个参数随机选择
    # 如果数据集遍历maxIter次都没有参数改变,则退出循环,程序运行结束
    :param dataMatIn: 数据集
    :param classLabels: 数据标签
    :param C: 超参数C
    :param toler: 容忍度,软间隔里面的kesei
    :param maxIter: 最大循环次数
    :return:
    '''

    m, n = np.shape(dataMatIn)   # m为训练集个数,n为训练集维度
    alphas = np.zeros((m, 1))   # alpha初始化为0
    b = 0.0
    iter = 0    # 在alpha没有任何改变时的情况下遍历数据集次数,当iter大于最大循环次数maxIter时,函数运行结束
    while(iter < maxIter):
        alphaPairsChanged  = 0  # 无参数改变为0,有参数改变为1.标志位
        for i in range(m):
            # 计算预测值g(xi)
            fXi = float(np.dot((alphas * classLabels).T, np.dot(dataMatIn, dataMatIn[i,:].T))) + b
            Ei = fXi - float(classLabels[i, 0])

            # 外层循环,寻找违反KKT条件最严重的点
            if ((classLabels[i, 0]*Ei < -toler) and (alphas[i] < C)) or ((classLabels[i, 0]*Ei > toler) and (alphas[i] > 0)):
                # 随机选择一个不同于 i 的 alpha 索引作为 j
                j = selectJrand(i, m)
                fXj = float(np.dot((alphas * classLabels).T, np.dot(dataMatIn, dataMatIn[j,:].T))) + b
                Ej = fXj - float(classLabels[j, 0])
                alphaIold = alphas[i].copy()    # 复制一个值用于存储更新前的alpha值
                alphaJold = alphas[j].copy()

                # 裁剪alpha到合法范围
                if (classLabels[i, 0] != classLabels[j, 0]):
                    L = max(0, (alphaJold - alphaIold))
                    H = min(C, (C + alphaJold - alphaIold))
                else:
                    L = max(0, (alphaJold + alphaIold - C))
                    H = min(C, (alphaJold + alphaIold))

                if H == L: print('H=L');continue # 如果L和H相等,不做任何改变,继续下一次循环
                # E11+E22-2E12
                eta = np.dot(dataMatIn[i,:].T, dataMatIn[i,:]) + np.dot(dataMatIn[j,:].T, dataMatIn[j,:]) \
                      - 2 * np.dot(dataMatIn[i,:].T, dataMatIn[j,:])
                if eta <= 0: continue   # 理论上eta不可能出现负数
                alphas[j] += classLabels[j, 0] * (Ei - Ej) / eta  # 更新内循环参数alpha(未裁剪)
                alphas[j] = clipAlpha(alphas[j], H, L)  # 进行裁剪

                if (np.abs(alphas[j] - alphaJold) < 0.00001): continue  # 新旧参数相差极小,则不更新

                alphas[i] += classLabels[i, 0] * classLabels[j, 0] * (alphaJold - alphas[j])    # 更新内循环alpha参数

                # 根据所求得alpha计算对应的b
                b1 = b - Ei - classLabels[i, 0] * (alphas[i] - alphaIold) * np.dot(dataMatIn[i,:].T, dataMatIn[i,:]) \
                     - classLabels[j, 0] * (alphas[j] - alphaJold) * np.dot(dataMatIn[j,:].T, dataMatIn[i,:])
                b2 = b - Ej - classLabels[i, 0] * (alphas[i] - alphaIold) * np.dot(dataMatIn[i,:].T, dataMatIn[j,:]) \
                     - classLabels[j, 0] * (alphas[j] - alphaJold) * np.dot(dataMatIn[j,:].T, dataMatIn[j,:])

                if (alphas[i] > 0) and (alphas[i] < C): b = b1  # 如果外循环alpha为[0, C]范围内,则 b = b1
                elif (alphas[j] > 0) and (alphas[j] < C): b = b2    # 如果内循环alpha为[0, C]范围内,则 b = b2
                else: b = (b1 + b2) / 2.0   # 此时内循环alpha,外循环alpha为0或者C,则取平均值

                alphaPairsChanged = 1
                print('iter:%d i:%d, num of change:%d' % (iter, i, alphaPairsChanged))
        if (alphaPairsChanged == 0):    # 标志alphaPairsChanged为0则证明未更新参数,iter加1
            iter += 1
        else:
            iter = 0
        print('迭代次数:%d' % iter)
    # 此处b即为最优参数b
    return b, alphas

def calculateOptiPara(dataArr, labelArr, alphas):
    '''
    # 计算最优的 w 参数,用于构造分割超平面
    :param dataArr: 输入数据
    :param labelArr: 输入标签
    :param alphas: alphas数据列表
    :return:
    '''
    wstar = np.dot((alphas * labelArr).T, dataArr)
    return wstar

if __name__ == '__main__':
    dataArr, labelArr = loadDataset('dataset//testSet.txt')
    b, alphas = smoSimple(dataArr, labelArr, 0.6, 0.001, 40)
    wStar = calculateOptiPara(dataArr, labelArr, alphas)
    draw(dataArr, wStar, b)

分类结果如下:
机器学习实战(python3.7也可跑)-支持向量机

希望文章内容可以帮助到你,快来动手敲代码吧!!