统计学习方法第四章朴素贝叶斯算法

# -*- coding: utf-8 -*-
# @FileName: 统计学习方法第四章朴素贝叶斯算法
# @Software: PyCharm
# @Author  : li Xu
# @Time    :2021//03//03

import time

import numpy as np


def loaddata(fileName):
    '''
    加载数据集文件
    :param fileName:要加载得文件得路径
    :return:数据集和标签
    '''
    print('start to read data')
    # 存放数据及标记
    dataArr = []
    labelArr = []
    # 读取数据
    fr = open(fileName, 'r')
    # 遍历文件的每一行
    for line in fr.readlines():
        # 获取当前行,并按','切割成字段放入列表中
        curline = line.strip().split(',')
        # 将每行中除标记外的数据放入数据集中(curLine[0]为标记信息)在放入的同时将原先字符串形式的数据转换为整型
        # 此外将数据进行了二值化处理,大于128的转换成1,小于的转换成0,方便后续计算
        dataArr.append([int(int(num) > 128) for num in curline[1:]])
        # 将标记数据放入标记数据集里
        labelArr.append(int(curline[0]))
    # 返回数据集和标记
    return dataArr, labelArr


def NaiveBayes(Py, Px_y, x):
    '''
    通过朴素贝叶斯计算进行概率估计
    :param Py:先验概率分布
    :param Px_y:条件概率分布
    :param x:需要估计的样本
    :return:返回所有label的估计概率
    '''
    # 设置特征数目
    featureNum = 784
    # 设置类别数目
    classNum = 10
    # 建立存放所有标记的估计概率数组
    P = [0] * classNum
    # 对于每一个类别,单独估计其概率
    for i in range(classNum):
        # 初始化sum为0,sum为求和项。在训练过程中对概率进行了log处理,所以这里原先应当是连乘所有概率,最后比较哪个概率最大
        # 但是当使用log处理时,连乘变成了累加,所以使用sum
        sum = 0
        # 获取每一个条件概率值,进行累加
        for j in range(featureNum):
            sum += Px_y[i][j][x[j]]
        # 最后再和先验概率相加(也就是式4.7中的先验概率乘以后头那些东西,乘法因为log全变成了加法)
        P[i] = sum + Py[i]
    # P.index(max(P)):找到该概率最大值对应的所有(索引值和标签值相等)
    return P.index(max(P))


def model_test(Py, Px_y, testDataArr, testLabelArr):
    '''
    对测试集进行测试
    :param Py:先验概率分布
    :param Px_y:条件概率分布
    :param testDataArr:测试集数据
    :param testLabelArr:测试集数据对应的标签
    :return:准确率
    '''
    # 错误计数
    errorCnt = 0
    # 循环遍历测试集中的每一个样本
    for i in range(len(testDataArr)):
        # 获取预测值
        predict = NaiveBayes(Py, Px_y, testDataArr[i])
        # 与答案进行比较
        if predict != testLabelArr[i]:
            errorCnt += 1
    # 返回准确率
    return 1 - (errorCnt / len(testDataArr))


def getAllProbability(trainDataArr, trainLabelArr):
    '''
    通过训练集计算先验概率分布和条件概率分布
    :param trainDataArr:训练集数据
    :param trainLabelArr:训练集数据对应的标签
    :return:先验概率分布和条件概率分布
    '''
    # 设置特征数目
    featureNum = 784
    # 设置类别数目
    classNum = 10
    # 初始化先验概率分布存放数组,后续计算得到的P(Y = 0)放在Py[0]中,以此类推数据长度为10行1列
    Py = np.zeros((classNum, 1))
    # 对每个类别进行一次循环,分别计算它们的先验概率分布,计算公式为书中"4.2节 朴素贝叶斯法的参数估计公式4.11"
    for i in range(classNum):
        Py[i] = ((np.sum(np.mat(trainLabelArr) == i)) + 1) / (len(trainLabelArr) + 10)
    # 转换为log对数形式
    Py = np.log(Py)
    # 计算条件概率 Px_y=P(X=x|Y = y),初始化为全0矩阵,用于存放所有情况下的条件概率
    Px_y = np.zeros((classNum, featureNum, 2))
    # 对标记集进行遍历
    for i in range(len(trainLabelArr)):
        # 获取当前循环所使用的标记
        label = trainLabelArr[i]
        # 获取当前要处理的样本
        x = trainDataArr[i]
        # 对该样本的每一维特征进行遍历
        for j in range(featureNum):
            # 在矩阵中对应位置加1,这里还没有计算条件概率,先把所有数累加,全加完以后,在后续步骤中再求对应的条件概率
            Px_y[label][j][x[j]] += 1
    for label in range(classNum):
        # 循环每一个标记对应的每一个特征
        for j in range(featureNum):
            # 获取y=label,第j个特征为0的个数
            Px_y0 = Px_y[label][j][0]
            # 获取y=label,第j个特征为1的个数
            Px_y1 = Px_y[label][j][1]
            # 分别计算对于y= label,x第j个特征为0和1的条件概率分布
            Px_y[label][j][0] = np.log((Px_y0 + 1) / (Px_y0 + Px_y1 + 2))
            Px_y[label][j][1] = np.log((Px_y1 + 1) / (Px_y0 + Px_y1 + 2))
    # 返回先验概率分布和条件概率分布
    return Py, Px_y


if __name__ == "__main__":
    start = time.time()
    # 获取训练集
    print('start read transSet')
    trainDataArr, trainLabelArr = loaddata('Mnist/mnist_train.csv')
    # 获取测试集
    print('start read testSet')
    testDataArr, testLabelArr = loaddata('Mnist/mnist_test.csv')
    # 开始训练,学习先验概率分布和条件概率分布
    print('start to train')
    Py, Px_y = getAllProbability(trainDataArr, trainLabelArr)
    # 使用习得的先验概率分布和条件概率分布对测试集进行测试
    print('start to test')
    accuracy = model_test(Py, Px_y, testDataArr, testLabelArr)
    # 打印准确率
    print('the accuracy is:', accuracy)
    # 打印时间
    print('time span:', time.time() - start)


start read transSet
start to read data
start read testSet
start to read data
start to train
start to test
the accuracy is: 0.8432999999999999
time span: 156.16428136825562
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值