朴素贝叶斯和AODE算法详解

一、实验要求

(1)实验目的

  • 1、掌握朴素贝叶斯分类器(Naïve Bayes Classifier, NBC)。
  • 2、掌握AODE(Averaged One-Dependent Estimator)分类器

(2)数据集简介

在这里插入图片描述

(3)实验内容

  • 1、编写程序实现朴素贝叶斯分类器设计。
  • 2、编写程序实现AODE 分类器设计。

(4)评价指标

  • 本次实验主要利用Acc 指标对聚类结果进行评价,值越大表明分类效果越好。

二、数据集的划分

  • 将数据集8:2划分为训练集和测试集
  • dataset_parse.py
import os

import numpy as np
from scipy.io import loadmat
from sklearn.model_selection import train_test_split
np.set_printoptions(threshold=np.inf)

# 数据准备
minist_path = r".\datasets\MNIST.mat"
lung_path = r".\datasets\lung.mat"
yale_path = r".\datasets\Yale.mat"

# 存储路径
PATH_MINIST = './data/minist/'
PATH_LUNG = './data/lung/'
PATH_YALE = './data/yale/'


# 加载数据
def create_data(path):
    data = loadmat(path)
    data_x = data["X"]
    data_y = data["Y"][:, 0]
    data_y -= 1
    Data = np.array(data_x)
    Label = np.array(data_y)
    return Data, Label


def save_data(path, save_path):
    X, y = create_data(path)
    train_data, test_data, train_label, test_label = train_test_split(X, y, test_size=0.2, random_state=233)
    if not os.path.exists(save_path):
        os.makedirs(save_path)
    np.savetxt(save_path + 'train_data.txt', train_data)
    np.savetxt(save_path + 'train_label.txt', train_label)
    np.savetxt(save_path + 'test_data.txt', test_data)
    np.savetxt(save_path + 'test_label.txt', test_label)


if __name__ == '__main__':
    save_data(yale_path, PATH_YALE)
    save_data(lung_path, PATH_LUNG)
    save_data(minist_path, PATH_MINIST)
    print('完成')

三、朴素贝叶斯

(1)朴素贝叶斯原理

P ( 类 别 ∣ 特 征 1 , 特 征 2 , . . . ) = P ( 特 征 1 , 特 征 2 , . . . ∣ 类 别 ) ∗ P ( 类 别 ) P ( 特 征 1 , 特 征 2... ) = P ( 特 征 1 ∣ 类 别 ) ∗ P ( 特 征 2 ∣ 类 别 ) ∗ . . . P ( 特 征 1 ) ∗ P ( 特 征 1 ) ∗ . . . P(类别|特征1,特征2,...) = \frac{P(特征1,特征2,...|类别) * P(类别)}{P(特征1,特征2...)}=\frac{P(特征1|类别)*P(特征2|类别)*...}{P(特征1)*P(特征1)*...} P(12,...)=P(1,2...)P(1,2,...)P()=P(1)P(1)...P(1)P(2)...

  • 朴素贝叶斯的基本思想是假设所有属性都是相互独立的情况下,其中:
    1. 类别为将要识别分类的数据集的标签类别数,特征为分类数据的特征向量。
    2. P ( 特 征 n ∣ 类 别 ) = P ( 特 征 n , 类 别 ) P ( 类 别 ) P(特征n|类别) = \frac{P(特征n,类别)}{P(类别)} P(n)=P()P(n) : 在该类别发生的情况下,是所求的特征的概率 。比如类别0的情形下,类别0和特征同时满足的概率。
    3. P ( 类 别 ) P(类别) P() : 每种类别在标签集中的比例,即每种标签的概率。
    4. P ( 特 征 1 ) + P ( 特 征 2 ) + . . . P(特征1)+P(特征2)+... P(1)+P(2)+... : 即每一个特征发生的概率,求和。
  • 因为对于一个样本来说, P ( 特 征 1 ) ∗ P ( 特 征 1 ) ∗ . . . P(特征1)*P(特征1)*... P(1)P(1)...一样,分母一样,只要比较分子就好了。所以只要计算条件概率p(xk|y=j)和p(y= j)的先验概率,不需要计算 P ( 特 征 1 ) ∗ P ( 特 征 1 ) ∗ . . . P(特征1)*P(特征1)*... P(1)P(1)...
  • 由于最后要比较每个样本在m类标签各自的概率,然后取其中最大的,而且要计算 P ( 特 征 1 , 特 征 2 , . . . ∣ 类 别 ) ∗ P ( 类 别 ) P(特征1,特征2,...|类别) * P(类别) P(1,2,...)P(),不妨对其各自取对数,将其结果相加,比大小就可。

(2)拉普拉斯修正

  • 根据训练集,算条件概率P(xk|y=j) 和先验概率 P(y=j),由于这两种概率可能会为0,后面无法计算,因此一定要进行Laplace平滑,代码就一句话,分子+1,分母+种类数。
    P ( 类 别 m ) = D ( 类 别 m ) + 1 D + N P(类别m) = \frac{ D(类别m) + 1}{D+N} P(m)=D+ND(m)+1
  • 其中D(类别m)为类别为m的数目,D为标签总数,N为标签类别数
    P ( 特 征 n ∣ 类 别 m ) = D ( 特 征 n , 类 别 m ) + 1 D ( 类 别 m ) + N n P(特征n|类别m) = \frac{ D(特征n,类别m) + 1}{D(类别m)+N_{n}} P(nm)=D(m)+NnD(n,m)+1
  • 其中D(类别m)为类别为m的数目,D(特征n,类别m)为特征n且为类别m的数目维数为 m ∗ n m*n mn N n N_{n} Nn
    为第n个特征数量,即一共有多上个不同值的特征,如果是minist数据集,数据集有784维,则有必要对其二值化,变为0和1,这样 N n N_{n} Nn的值就为2,便于简化计算。

(3)函数变量解析

  • P ( y = j ) : P ( 类 别 ) P(y=j):P(类别) P(y=j)P()
  • P ( x k ∣ y = j ) : P ( 类 别 ∣ 特 征 ) P(x_{k}|y=j):P(类别|特征) P(xky=j)P()
  • P ( y = j ∣ x k ) : P ( 特 征 ∣ 类 别 ) P(y=j|x_{k}) : P(特征|类别) P(y=jxk)P()
  • num::2400,特征的数目
  • dimsnum:784,特征的维数
  • labelnum:10,标签类别数
  • pyj:1x10的零向量,代表 P ( y = j ) : P ( 类 别 ) P(y=j):P(类别) P(y=j)P()
  • pyjk1:10x784的零向量,代表 P ( y = j ∣ x k ) : P ( 特 征 ∣ 类 别 ) P(y=j|x_{k}) : P(特征|类别) P(y=jxk)P()
  • b=np.argmax(a)#取出a中元素最大值所对应的索引(索引值默认从0开始)
  • b=np.argmax(a, axis=0)#对二维矩阵来讲a[0][1]会有两个索引方向,第一个方向为a[0],默认按列方向搜索最大值
  • xk=0时,概率为1 - pyjxk1 ,xk=1时,概率为pyjxk1
  • 坑:由于yale数据集只有203个,但是有15维,在程序中使用for j in range(1+np.max(test_label)):来做循环,而不是for j in range(labelnum),由于样本量太少,导致在测试集里面没有某一类的样本,循环次数少了一次,导致精确度一直只有0.12。

(4)代码如下

  • naive_bayes.py
import numpy as np

from data_handle import load_data, PATH_MINIST, PATH_LUNG, PATH_YALE

np.set_printoptions(threshold=np.inf)


# p(类别|特征) = p(特征|类别)*p(类别)/p(特征)


def normalize(data):  ##将图片像素二值化
    m, n = np.array(data).shape
    h = (np.max(data) - np.min(data)) / 2
    for i in range(m):
        for j in range(n):
            if data[i, j] > h:
                data[i, j] = 1
            else:
                data[i, j] = 0
    return data


def CalProb(train_data, train_label):
    # p(y=j):p(类别)  P(xk|y=j):p(特征|类别)
    # 根据训练集 算条件概率P(xk|y=j) 和先验概率 P(y=j) 注意这两种概率可能会为0 后面无法计算 因此一定要进行Laplace平滑 参见李航P51
    # num: 2400, dimsnum: 784
    num, dimsnum = train_data.shape
    # labelnum: 标签类别数
    labelnum = len(set(train_label))

    # pyj:1x10的零向量
    pyj = np.zeros(labelnum)
    # pyjk1:10x784的零向量
    pyjk1 = np.zeros((labelnum, dimsnum))
    # num: 2400
    for i in range(num):
        # 计算出每种标签的个数 ---> p(y=j):p(类别)
        label = train_label[i]
        # 需要laplace平滑 这里是真实个数
        pyj[label] = pyj[label] + 1
        # dimsnum: 784
        for j in range(dimsnum):
            # 因为会出现条件概率为0的情况 log无法计算 需要laplace平滑  ##算 Pj k = 1
            # 此处计算的是所有label为1的数的个数
            pyjk1[label][j] += train_data[i][j]
    # print('pyj个数:', pyj)
    # 条件概率 需要Laplace平滑 分母要加上xk的种类数 这里只能取0 1像素
    # P y = j && xk = 1的概率  经验主义用频率去估计概率
    # 此时pyj为10种类别各自的数目
    # ni 为特征n的标签数
    pyjk1 = (pyjk1.T + 1) / (pyj + 2)
    # P y = j 的概率 先验概率 需要 Laplace平滑 分母要加上y的标签种类数
    pyj = (pyj + 1) / (num + labelnum)
    # pk1, #, pyjk1
    return pyj, pyjk1


def CalTestProb_xk_yj(xk, pyjxk1):  # 计算条件概率 P(xk|y=j)的概率的log
    # xk=0时,概率为1 - pyjxk1 ,xk=1时,概率为pyjxk1
    return xk * np.log(pyjxk1) + (1 - xk) * np.log(1 - pyjxk1)


# test  这块计算 应该可以优化
def test(test_data, test_label, pyjk1, pyj):  # 测试
    # num : 600 , dimsnum : 784
    num, dimsnum = test_data.shape
    labelnum = len(set(test_label))
    acc = 0
    for i in range(num):
        testdata = test_data[i]
        # 第i个样本属于j类的概率
        p_yj_xi = np.log(pyj)
        # 计算xi 属于 第j个类别的概率
        for j in range(1+np.max(test_label)):
            for k in range(dimsnum):
                xk = testdata[k]  # x^i的第j个像素 或者说是 维度
                # print(pyjk1[j][k])
                p_yj_xi[j] += CalTestProb_xk_yj(xk, pyjk1[j][k])
        # p_yj_xi
        # np.argmax : 取出a中元素最大值所对应的索引,此时最大值位6,其对应的位置索引值为4,(索引值默认从0开始)
        p_y_xi = np.argmax(p_yj_xi)
        acc += (p_y_xi == test_label[i])

        # print('real is: ', test_label[i], '  predict is: ', p_y_xi)

    print('Test accuracy is: ', acc / num)


def main(path):
    train_data, test_data, train_label, test_label = load_data(path)
    train_data = normalize(train_data)
    test_data = normalize(test_data)
    pyj, pyjk1 = CalProb(train_data, train_label)
    test(test_data, test_label, pyjk1.T, pyj)


if __name__ == "__main__":
    print('minist:')
    main(PATH_MINIST)
    print('---------')
    print('lung')
    main(PATH_LUNG)
    print('---------')
    print('yale')
    main(PATH_YALE)

(5)结果

minist:
Test accuracy is:  0.8266666666666667
---------
lung
Test accuracy is:  0.926829268292683
---------
yale
Test accuracy is:  0.42424242424242425

四、AODE算法

(1)AODE算法原理

  • 由于属性之间是相互独立的假设太强在现实生活中很难满足,故不妨对独立性条件进行放松—半朴素贝叶斯分类器,适当考虑一部分属性间的相互依赖信息,最常用的是独依赖,即每个属性最多依赖一个其他属性。
  • AODE是一种基于集成学习机制、更为强大的独依赖分类器,其相关的计算公式如下:
    P ( 类 别 m , 特 征 i ) = D ( 类 别 m , 特 征 i ) + 1 D + N ∗ N i P(类别m,特征i) = \frac{ D(类别m,特征i) + 1}{D+N*N_{i}} P(mi)=D+NNiD(mi)+1
  • 其中D(类别m,特征i)为类别为m且在第i个特征上取值为 特征i 的数目,D为标签总数,N为标签类别数, N i N_{i} Ni为第i个特征可能的取值数

P ( 特 征 j ∣ 类 别 m , 特 征 i ) = D ( 特 征 j , 特 征 i , 类 别 m ) + 1 D ( 类 别 m , 特 征 i ) + N j P(特征j|类别m,特征i) = \frac{ D(特征j,特征i,类别m) + 1}{D(类别m,特征i)+N_{j}} P(jmi)=D(mi)+NjD(jim)+1

  • 其中D(特征j,特征i,类别m)为类别为m且在第i和第j个属性上取值分别为特征i和特征j的数目,D(类别m,特征i)为在类别m上,且取值为特征i的数目, N j N_{j} Nj为第j个特征可能取值的个数。

五、结果

(1)acc结果对比

数据/方法ministlungyale
bayes0.8260.9260.424

(2)结果分析

  • 通过实验可以发现由于lung数据集的数据量最大,标签类别只有5,因此每一类标签的训练数据集较大,因此结果较高,可以到百分之九十,yale数据量小,标签类别为15,因此每一类标签的训练数据集较小,结果也相对较差。可以通过优化模型,或者增加数据记得方式来提高精确度。
  • 在本次实验中,贝叶斯方法在lung数据集上效果最好,虽然比cnn要差了一些,但是在没有涉及神经网络的领域,精度已经较高了。

参考

神奇的拉普拉斯平滑(Laplacian Smoothing)及其在正则化上的应用~
谈谈自己对正则化的一些理解~
拉普拉斯修正的朴素贝叶斯分类器及AODE分类器
Python 机器学习_基于朴素贝叶斯分类的MNIST手写数字识别 - 本文的代码原型来源
详解朴素贝叶斯分类算法 - 用男生的特征来决定女生嫁不嫁,讲的生动有趣
贝叶斯分类器(一):朴素贝叶斯分类器与半朴素贝叶斯分类器 - 讲了很多贝叶斯相关原理

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
朴素贝叶斯算法是一种基于贝叶斯定理的分类算法,它假设特征之间相互独立。这个假设使得算法具有较快的训练和预测速度,并且在处理大规模数据集时表现良好。 下面我将用图解的方式详细介绍朴素贝叶斯算法的原理和步骤。 首先,我们需要准备一个分类任务的数据集。假设我们要构建一个垃圾邮件分类器,数据集包含了一些已经标记好的邮件样本,每个邮件样本都有一些特征(如邮件内容、发件人等)和对应的标签(垃圾邮件/非垃圾邮件)。 第一步是计算先验概率。先验概率指的是在没有任何特征信息的情况下,某个样本属于某个类别的概率。在我们的例子中,就是计算垃圾邮件和非垃圾邮件出现的概率。 第二步是计算条件概率。条件概率指的是在已知某个特征条件下,某个样本属于某个类别的概率。对于朴素贝叶斯算法来说,我们假设所有特征之间相互独立,因此可以将条件概率拆分为各个特征的概率乘积。我们需要计算每个特征在每个类别下的概率。 第三步是应用贝叶斯定理。贝叶斯定理可以根据已知的特征计算某个样本属于某个类别的后验概率。后验概率越大,就说明该样本属于该类别的可能性越大。 最后,我们可以根据后验概率进行分类预测。选择具有最大后验概率的类别作为预测结果。 总结一下,朴素贝叶斯算法通过计算先验概率、条件概率和应用贝叶斯定理,实现了对样本的分类预测。它的优势在于简单、快速,并且在一些特定的数据集上表现出色。然而,它的假设可能不符合实际情况,所以在实际应用中需要考虑和验证数据的特性和假设的合理性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值