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

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

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

代码可在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)

分类结果如下:
在这里插入图片描述

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值