python 高斯烟羽模型_支持向量机(SVM)模型python复现 - SMO算法;核技巧;高斯核函数...

800d9a2a5ef4e0c1df729ca6bab48103.png

本文包括:

  1. 支持向量机 - SMO
  2. 其它有关数据分析,机器学习的文章及社群

支持向量机理论知识:

舟晓南:统计学习方法 - 线性可分支持向量机解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 线性支持向量机解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 非线性支持向量机解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 序列最小最优化算法(SMO)解析 | 数据分析,机器学习,学习历程全记录

数据集mnist下载地址(csv格式),以及完整代码:

Zhouxiaonnan/machine-learning-notesandcode

如果觉得对您有帮助,还烦请点击上面的链接,帮忙github点个star~谢谢~


Talk is cheap, let's check the code ^_^

支持向量机 - SMO算法 - 核技巧 - 高斯核函数

支持向量机中涉及到较多的算法,比如最优化拉格朗日向量的SMO算法,将非线性可分的数据集映射到高维空间的核函数等,建议先熟悉理论,然后自己写一下算法的大致流程,再开始写代码。

'''
支持向量机
-------------------------------------
数据集:Mnist
训练集数量:10000
测试集数量:1000
---------------------------------
训练结果:
初始化时间:327s
训练时间:285s
正确率:0.99
'''

import numpy as np
import time
from itertools import islice
import math

'''
readlines方法读取训练数据
--------
输入:
file:数据地址
--------
输出:
data:转换后的数据
label:转换后的标记
'''
def LoadData(file):

    # 打开数据文件
    fr = open(file, 'r')
       
    # 准备两个list存储data和label
    data = []
    label = []
    
    # 逐行读取数据,使用islice可以直接跳过第一行表头进行逐行读取
    for line in islice(fr,1,None):
        
        # 对每一行消去空格,并根据','进行分割
        splited = line.strip().split(',')
        
        # 分割后的第一个元素是Label,跳过label遍历所有特征值
        # 归一化
        int_line = [int(num) / 255 for num in splited[1:]]
        
        # 逐行存储数据
        data.append(int_line)
        
        # 将问题转换为二分类问题
        if int(splited[0]) == 0:
            label.append(1)
        else:
            label.append(-1)
    
    # 转换成ndarray形式方便后续计算
    data = np.array(data)
    label = np.array(label)
        
    # 返回数据的特征部分和标记部分
    return data, label

'''
定义SVM类
'''
class SVM:
    
    '''初始化参数'''
    def __init__(self, train_data, train_label, sigma, C, toler, itertime):
        
        self.train_data = train_data                       # 训练集数据
        self.train_label = train_label                     # 训练集标记
        self.m, self.n = np.shape(train_data)              # self.m为训练集样本容量,self.n为特征数量
        self.sigma = sigma                                 # 高斯核分母上的超参数
        self.KernalMatrix = self.CalKernalMatrix()         # 高斯核矩阵
        self.alpha = np.zeros(self.m)                      # 初始化拉格朗日向量,长度为训练集样本容量
        self.b = 0                                         # 初始化参数b
        self.C = C                                         # 惩罚参数
        self.toler = toler                                 # 松弛变量
        self.itertime = itertime                           # 迭代次数
        self.E = [float(-1 * y) for y in self.train_label] # 初始化Elist,因为alpha和b初始值为0,因此E的初始值为训练集标记的值
        
    '''计算高斯核矩阵'''
    def CalKernalMatrix(self):
        
        # 初始化高斯核矩阵,矩阵为m*m大小
        KernalMatrix = [[0 for i in range(self.m)] for j in range(self.m)]
        
        # 遍历每一个样本
        for i in range(self.m):
            
            # 首先得到一个样本的数据
            X = self.train_data[i]
            
            # 仅遍历从i到self.m的样本,因为Kij和Kji的值相同,所以只需要计算一次
            for j in range(i, self.m):
                
                # 得到另一个样本的数据
                Z = self.train_data[j]
                
                # 计算核函数
                K = np.exp(-1 * np.dot(X - Z, X - Z) / (2 * np.square(self.sigma)))
                
                # 存储在核矩阵中对称的位置
                KernalMatrix[i][j] = K
                KernalMatrix[j][i] = K
        
        KernalMatrix = np.array(KernalMatrix)
                
        return KernalMatrix
        
    '''计算g(xi)'''
    def Calgxi(self, i):
        
        Index = [index for index, value in enumerate(self.alpha) if value != 0]
        gxi = 0
        for index in Index:
            gxi += self.alpha[index] * self.train_label[index] * self.KernalMatrix[i][index]
        gxi = gxi + self.b

        return gxi
        
    '''判断是否符合KKT条件'''
    def isSatisfyKKT(self, i):
        
        # 获得alpha[i]的值
        alpha_i = self.alpha[i]
 
        # 计算yi * g(xi)
        gxi = self.Calgxi(i)
        yi = self.train_label[i]
        yi_gxi = yi * gxi
        
        # 判断是否符合KKT条件
        if -1 * self.toler < alpha_i < self.toler and yi_gxi >= 1:
            return True
        elif -1 * self.toler < alpha_i < self.C + self.toler and math.fabs(yi_gxi - 1) < self.toler:
            return True
        elif self.C - self.toler < alpha_i < self.C + self.toler and yi_gxi <= 1:
            return True
        return False
    
    '''SMO算法'''
    def SMO(self):
        
        # 迭代
        t = 0
        parameterchanged = 1
        while t < self.itertime and parameterchanged > 0:
        
            t += 1
            parameterchanged = 0
            '''选择两个alpha'''

            # 外层循环,选择第一个alpha
            for i in range(self.m):

                # 判断是否符合KKT条件,如果不满足,则选择该alpha为alpha1
                # 如果满足,则继续外层循环
                TorF = self.isSatisfyKKT(i)
                if TorF == False:
                    alpha1 = self.alpha[i]
                    
                    # 从Earray得到alpha1对应的E1
                    E1 = self.E[i]

                    # 复制一个EMatrix,并令E1的位置为nan
                    # 这样在接下来找最大值和最小值时将不会考虑E1
                    # 这里需要使用copy,如果不用copy,改变EM_temp也会同时改变EMatrix
                    EM_temp = np.copy(self.E)
                    EM_temp[i] = np.nan

                    # 我们需要使|E1-E2|的值最大,由此选择E2
                    # 首先初始化maxE1_E2和E2及E2的下标j
                    maxE1_E2 = -1
                    E2 = np.nan
                    j = -1

                    # 内层循环
                    # 遍历EM_temp中的每一个Ei,得到使|E1-E2|最大的E和它的下标
                    for j_temp, Ej in enumerate(EM_temp):
                        if math.fabs(E1 - Ej) > maxE1_E2:
                            maxE1_E2 = math.fabs(E1 - Ej)
                            E2 = Ej
                            j = j_temp

                    # alpha2为E2对应的alpha
                    alpha2 = self.alpha[j]

                    '''求最优alpha1和alpha2'''

                    y1 = self.train_label[i]
                    y2 = self.train_label[j]

                    # 计算η
                    K11 = self.KernalMatrix[i][i]
                    K22 = self.KernalMatrix[j][j]
                    K12 = self.KernalMatrix[i][j]
                    eta = K11 + K22 - 2 * K12

                    # 计算alpha2_new
                    alpha2_new = alpha2 + y2 * (E1 - E2) / eta

                    # 计算上限H和下限L
                    if y1 != y2:
                        L = max([0, alpha2 - alpha1])
                        H = min([self.C, self.C + alpha2 - alpha1])
                    else:
                        L = max([0, alpha2 + alpha1 - self.C])
                        H = min([self.C, alpha2 + alpha1])

                    # 剪切alpha2_new
                    if alpha2_new > H:
                        alpha2_new = H
                    elif alpha2_new < L:
                        alpha2_new = L

                    # 得到alpha1_new
                    alpha1_new = alpha1 + y1 * y2 * (alpha2 - alpha2_new)

                    '''更新b'''
                    # 计算b1_new和b2_new
                    b1_new = -1 * E1 - y1 * K11 * (alpha1_new - alpha1) 
                            - y2 * K12 * (alpha2_new - alpha2) + self.b
                    b2_new = -1 * E2 - y1 * K12 * (alpha1_new - alpha1) 
                            - y2 * K22 * (alpha2_new - alpha2) + self.b

                    # 根据alpha1和alpha2的范围确定b_new
                    if 0 < alpha1_new < self.C and 0 < alpha2_new < self.C:
                        b_new = b1_new
                    else:
                        b_new = (b1_new + b2_new) / 2

                    '''更新E'''
                    # 首先需要更新两个alpha和b
                    self.alpha[i] = alpha1_new
                    self.alpha[j] = alpha2_new
                    self.b = b_new

                    # 计算Ei_new和Ej_new
                    E1_new = self.Calgxi(i) - y1
                    E2_new = self.Calgxi(j) - y2

                    # 更新E
                    self.E[i] = E1_new
                    self.E[j] = E2_new

                    if math.fabs(alpha2_new - alpha2) >= 0.000001:
                        parameterchanged += 1
            
            print('itertime: %a   parameterchanged: %a' % (t,parameterchanged))
        
        # 最后遍历一遍alpha,大于0的下标即对应支持向量
        VecIndex = [index for index, value in enumerate(self.alpha) if value > 0]
        
        # 返回支持向量的下标,之后在预测时还需要用到
        return VecIndex
    
    '''计算b'''
    def OptimizeB(self):
        
        for j, a in enumerate(self.alpha):
            if 0 < a < self.C:
                break
        
        yj = self.train_label[j]
        summary = 0
        for i in range(self.alpha):
            summary += self.alpha[i] * self.train_label[i] * self.KernalMatrix[i][j]
            
        optimiezedB = yj - summary
        self.b = optimiezedB
    
    '''计算单个核函数'''
    def CalSingleKernal(self, x, z):
        
        SingleKernal = np.exp(-1 * np.dot(x - z, x - z) / (2 * np.square(self.sigma)))
        return SingleKernal
    
    '''单个新输入实例的预测'''
    def predict(self, x, VecIndex):
        
        # 决策函数计算
        # 求和项初始化
        summary = 0
        
        # Index中存储着不为0的alpha的下标
        for i in VecIndex:
            
            alphai = self.alpha[i]
            yi = self.train_label[i]
            Kernali = self.CalSingleKernal(x, self.train_data[i])
            
            summary += alphai * yi * Kernali
        
        # 最后+b
        # np.sign得到符号
        result = np.sign(summary + self.b)
            
        return result
    
    '''测试模型'''
    def test(self, test_data, test_label, VecIndex):
        
        # 测试集实例数量
        TestNum = len(test_label)
        
        errorCnt = 0
        
        # 对每一个实例进行预测
        for i in range(TestNum):
            
            result = self.predict(test_data[i], VecIndex)
            if result != test_label[i]:
                errorCnt += 1
        
        Acc = 1 - errorCnt / TestNum
        
        return Acc

'''测试模型'''
if __name__ == "__main__":
    
    print('start loading')
    # 输入训练集
    train_data, train_label = LoadData('E:/我的学习笔记/统计学习方法/实战/训练数据/mnist_train.csv')

    # 输入测试集
    test_data, test_label = LoadData('E:/我的学习笔记/统计学习方法/实战/训练数据/mnist_test.csv')
    print('end loading')

    # 初始化
    print('start initiating')
    start = time.time()
    svm = SVM(train_data[0:10000], train_label[0:10000], 10, 200, 0.001, 10)
    end = time.time()
    print('end initiating')
    print('initiating time: ', end - start)

    # 开始SMO算法
    print('start training')
    start = time.time()
    VecIndex = svm.SMO()
    end = time.time()
    print('end training')
    print('training time: ', end - start)
    
    # 开始测试模型
    Acc = svm.test(test_data[0:1000], test_label[0:1000], VecIndex)
    print('Accurate: ', Acc)

github:

如果觉得对您有帮助,还烦请点击下面的链接,帮忙github点个star~谢谢~

Zhouxiaonnan/machine-learning-notesandcode

欢迎关注专栏:

学习笔记:数据分析,机器学习,深度学习​zhuanlan.zhihu.com
ea145b7c52bb43682cb1e63e7e357f0d.png

机器学习模型Python复现:

舟晓南:感知机模型python复现 - 随机梯度下降法;梯度下降法;adagrad;对偶形式

舟晓南:k近邻(KNN)模型python复现 - 线性扫描;带权值的近邻点优化方法

舟晓南:朴素贝叶斯(Bayes)模型python复现 - 贝叶斯估计;下溢出问题

舟晓南:决策树(Decision Tree)模型python复现 - ID3;C4.5;CART;剪枝

舟晓南:逻辑斯蒂回归(LR)模型python复现 - 随机梯度下降法;SGDM;RMSProp;Adam

机器学习模型理论部分:

舟晓南:统计学习方法 - 感知机模型解读 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 感知机模型的收敛性解读 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - k近邻模型解读 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 朴素贝叶斯模型解读 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 朴素贝叶斯之后验概率最大化的含义 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 决策树,CART决策树解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 逻辑斯蒂回归解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 最大熵模型解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 改进的迭代尺度法IIS解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 线性可分支持向量机解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 线性支持向量机解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 非线性支持向量机解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 序列最小最优化算法(SMO)解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 提升算法Adaboost解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - 提升树算法解析 | 数据分析,机器学习,学习历程全记录

舟晓南:统计学习方法 - EM算法解析 | 数据分析,机器学习,学习历程全记录

MySQL:

舟晓南:转行数据分析师如何开始学习SQL | 工科生三个月转行数据分析学习心得

舟晓南:MySQL的执行逻辑和表的结构 | 数据分析学习历程全记录

舟晓南:MySQL优化查询1:索引及其使用技巧 | 数据分析学习历程全记录

舟晓南:MySQL优化查询2:如何查找低效率语句 | 数据分析学习历程全记录

舟晓南:MySQL优化查询3:explain和show profiles | 数据分析学习历程全记录

舟晓南:MySQL优化查询4:24种查询优化的具体方法 | 数据分析学习历程全记录

转行数据分析相关:

舟晓南:如何转行和学习数据分析 | 工科生三个月成功转行数据分析心得浅谈

舟晓南:求职数据分析师岗位,简历应该如何写?|工科生三个月成功转行数据分析心得浅谈


数据分析,机器学习学习社群正式启动~

需要学习资料,想要加入社群均可私信~

在这里会分享各种数据分析相关资源,技能学习技巧和经验等等~

详情私信,一起进步吧!

写于成都 2020-10-4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值