机器学习作业4——朴素贝叶斯分类器

news2024/11/28 2:39:36

目录

一、理论

一个例子:

二、代码

对于代码的解释:

1.fit函数:

2.predict函数:

三、实验结果

原因分析:


一、理论

朴素贝叶斯分类器基于贝叶斯定理进行分类,通过后验概率来判断将新数据归为哪一类。通过利用贝叶斯定理计算后验概率,并选择具有最高后验概率的类别作为预测结果,朴素贝叶斯分类器能够在考虑了先验概率和观察到的数据特征后,做出基于统计推断的分类决策,从而实现有效的分类。

步骤:

1.计算先验概率:先验概率表示了在没有任何其他信息的情况下,一个数据点属于每个类别的概率。

2.计算条件概率:条件概率表示了在给定某一类别的情况下,某个特征取某个值的概率。

3.计算后验概率:当有一个新的数据点需要分类时,朴素贝叶斯根据该数据点的特征值,利用贝叶斯定理计算每个类别的后验概率。后验概率表示了在考虑了新的观察结果后,每个类别的概率。

4.做出分类:根据计算得到的后验概率,选择具有最高后验概率类别作为预测结果

先验概率:

P(Y=c_k) = \frac{|D_{c_k}|}{|D|}

其中:ck是类别,D是数据数量,Dck是类别为ck的数据数量。

条件概率:

P(X^{(i)}=a_{i,j}|Y=c_k) = \frac{|D_{c_k}^{(i)}(a_{i,j})|}{|D_{c_k}|}

其中:Xi代表第i个特征,ai,j代表第i个特征中第j个类型,D_{c_k}^{(i)}(a_{i,j})代表在类别为ck时,在第i个特征中的第j个类型的数量。

后验概率:

P(Y=c_k|X=x) = \frac{P(Y=c_k) \cdot \prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_k)}{\sum_{i=1}^{K} P(Y=c_i) \cdot \prod_{j=1}^{n} P(X^{(j)}=x^{(j)}|Y=c_i)}

因为我们只需要分类,因为不同后验概率分母都相同,所以可以把分母去掉只需要比较分子的大小即可,即:

P(Y=c_k|X=x) = {P(Y=c_k) \cdot \prod_{i=1}^{n} P(X^{(i)}=x^{(i)}|Y=c_k)}

其中:\prod_{i=1}^{n} P(X^{(i)}=x^{(i)}|Y=c_k)代表当前测试数据的每个类型为xi,在ck类别下的条件概率的连乘积。

可以看到公式简洁了不少。

一个例子:

假设数据集:

特征1特征2类别
11A
10A
01B
01A
10B

现在预测一个特征11特证21的测试元组是什么类别。

首先计算类别的先验概率和所有条件概率:

P(A)= 3/5,P(B)= 2/5

对于特征1:
P1(1∣A)= 2/3,P1(1 | B)= 1/2,

对于特征2:

P2(1 | A) = 2/3,P2(1 | B) = 1/2

然后计算预测为A和B的后验概率,并比较大小,得出结果

P(A|X) = P(A)*P1(1∣A)*P2(1 | A) = 4/15
P(B|X) = P(B)*P1(1 | B)*P2(1 | B) = 1/10

可以看到P(A|X)比较大,所以是A类​

 

二、代码

import numpy as np  
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score 

class NaiveBayesClassifier:
    def __init__(self):
        self.class_prior = {}  # 存储类别的先验概率
        self.feature_prob = {}  # 存储特征在各个类别下的条件概率

    def fit(self, X, y):
        n_samples, n_features = X.shape  # 获取样本数和特征数
        self.classes = np.unique(y)  # 获取目标标签中的唯一类别值
        
        print("计算先验概率:")
        for c in self.classes:
            # 统计每个类别在目标标签中的出现次数,并除以样本总数,得到先验概率
            self.class_prior[c] = np.sum(y == c) / n_samples
            print("先验概率: 类别, 概率 "+repr(c)+" "+repr(self.class_prior[c]))

        # 外层循坏遍历类别,内层循环遍历每个特征,内层循环每次要计算出每个特征下不同取值的在当前类别下的条件概率,存放到[c][feature_index]下
        print("计算条件概率:")
        for c in self.classes:
            self.feature_prob[c] = {}  # 初始化存储特征条件概率的字典
            for feature_index in range(n_features):
                # 对于每个特征,统计在当前类别下的取值及其出现次数,并除以总次数,得到条件概率
                # values是feature_index列中不同的取值,counts是这些不同取值的个数(在类别为C下的)
                values, counts = np.unique(X[y == c, feature_index], return_counts=True)
                self.feature_prob[c][feature_index] = dict(zip(values, counts / np.sum(counts)))#dict是变为以values和counts/np.sum的字典,zip就是一个方便的操作
                for i in range(len(counts)):
                    print("条件概率:P(" + repr(values[i]) + "|" + repr(c) + ") = " + repr(self.feature_prob[c][feature_index][values[i]]))

    def predict(self, X):
        predictions = []  # 存储预测结果的列表
        #对于测试集中的每个元素
        cnt = 0
        for x in X:
            print("\n对于第" + repr(cnt) + "个测试集:")
            cnt+=1
            max_posterior_prob = -1  # 最大后验概率初始化为-1
            predicted_class = None  # 预测类别初始化为空
            #对于每个类别
            for c in self.classes:
                # 计算后验概率,先验概率乘以各个特征的条件概率
                posterior_prob = self.class_prior[c]
                print("对于类别"+repr(c)+" = "+"[先验"+repr(self.class_prior[c])+"]", end = '')
                #对于每个特征
                for feature_index, feature_value in enumerate(x):
                    if feature_value in self.feature_prob[c][feature_index]:
                        print(" * [P("+ repr(feature_value) + "|" + repr(c) + ") = " + repr(self.feature_prob[c][feature_index][feature_value]) + "]", end = '')
                        posterior_prob *= self.feature_prob[c][feature_index][feature_value]
                    else:
                        print(" * 1")
                # 更新预测值
                print(" = " + repr(posterior_prob))
                if posterior_prob > max_posterior_prob:
                    max_posterior_prob = posterior_prob
                    predicted_class = c
            predictions.append(predicted_class)  # 将预测结果添加到列表中
        return predictions  # 返回预测结果列表


data = {
    'outlook': ['sunny', 'sunny', 'overcast', 'rain', 'rain', 'rain', 'overcast', 'sunny', 'sunny', 'rain', 'rain', 'overcast', 'overcast', 'rain'],
    'temperature': ['hot', 'hot', 'hot', 'mild', 'cool', 'cool', 'cool', 'mild', 'cool', 'mild', 'mild', 'mild', 'hot', 'mid'],
    'humidity': ['high', 'high', 'high', 'high', 'normal', 'normal', 'normal', 'high', 'normal', 'normal', 'normal', 'high', 'normal', 'high'],
    'wind': ['weak', 'strong', 'weak', 'weak', 'weak', 'strong', 'strong', 'weak', 'weak', 'weak', 'strong', 'strong', 'weak', 'strong'],
    'playtennis': ['no', 'no', 'yes', 'yes', 'yes', 'no', 'yes', 'no', 'yes', 'yes', 'yes', 'yes', 'yes', 'no']
}
#创建DataFrame
df = pd.DataFrame(data)
X = df.drop('playtennis', axis=1)#axis=1删除列,=0删除行
y = df['playtennis']

# 将数据集分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=3)
X_train = X_train.values#化为二维数组,便于遍历
X_test = X_test.values
y_train = y_train.values
y_test = y_test.values

print("训练集:")
for i in range(len(y_train)):
    print(repr(i)+": "+repr(X_train[i])+"  "+repr(y_train[i]))
print("测试集:")
for i in range(len(y_test)):
    print(repr(i)+": "+repr(X_test[i])+"  "+repr(y_test[i]))


nb_classifier = NaiveBayesClassifier()
nb_classifier.fit(X_train, y_train)


y_pred = nb_classifier.predict(X_test)
print("预测结果:")
for i in range(len(y_pred)):
    print(repr(i)+"正确,预测: "+repr(y_test[i])+", "+repr(y_pred[i]))

accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred, pos_label='yes')
recall = recall_score(y_test, y_pred, pos_label='yes')
f1 = f1_score(y_test, y_pred, pos_label='yes')
print("准确率:", accuracy)  
print("精确率:", precision)  
print("召回率:", recall)  
print("f1:", f1)  

对于重要代码的解释:

1.fit函数:

      训练测试集的主要代码,先把训练集中所有的条件概率和先验概率算出来,在后面的预测中会使用到。

先验概率:

print("计算先验概率:")
        for c in self.classes:
            # 统计每个类别在目标标签中的出现次数,并除以样本总数,得到先验概率
            self.class_prior[c] = np.sum(y == c) / n_samples
            print("先验概率: 类别, 概率 "+repr(c)+" "+repr(self.class_prior[c]))

所有条件概率:

print("计算条件概率:")
        for c in self.classes:
            self.feature_prob[c] = {}  # 初始化存储特征条件概率的字典
            for feature_index in range(n_features):
                # 对于每个特征,统计在当前类别下的取值及其出现次数,并除以总次数,得到条件概率
                # values是feature_index列中不同的取值,counts是这些不同取值的个数(在类别为C下的)
                values, counts = np.unique(X[y == c, feature_index], return_counts=True)
                self.feature_prob[c][feature_index] = dict(zip(values, counts / np.sum(counts)))
                for i in range(len(counts)):
                    print("条件概率:P(" + repr(values[i]) + "|" + repr(c) + ") = " + repr(self.feature_prob[c][feature_index][values[i]]))

 结果:

2.predict函数:

这个函数用于测试集的预测,对于测试集中的每个元组,都要对于所有类别,计算一次该类别下的后验概率,然后选择所有类别中后验概率最大的,作为预测结果,而每个后验概率,通过该元组下的每个特征下的类型对应的条件概率的累乘和该类别的先验概率的乘积来确定。

对于每个元组:

for x in X:
            print("\n对于第" + repr(cnt) + "个测试集:")
            cnt+=1
            max_posterior_prob = -1  # 最大后验概率初始化为-1
            predicted_class = None  # 预测类别初始化为空

对于每个类别:

for c in self.classes:
                posterior_prob = self.class_prior[c]

对于每个特征:

for feature_index, feature_value in enumerate(x):
                    if feature_value in self.feature_prob[c][feature_index]:
                        print(" * [P("+ repr(feature_value) + "|" + repr(c) + ") = " + repr(self.feature_prob[c][feature_index][feature_value]) + "]", end = '')
                        posterior_prob *= self.feature_prob[c][feature_index][feature_value]
                    else:
                        print(" * 1")

每次计算完后,维护最大值以及类型:

if posterior_prob > max_posterior_prob:
                    max_posterior_prob = posterior_prob
                    predicted_class = c

每个元组计算完后,添加到预测数组中,最后返回预测数组即可。

三、实验结果

上图是最好的一个实验结果,所有指标为1,其他的实验结果准确率较低

原因分析:

        这个训练集数据过小,但是特征和每个特征中的类型又比较多,再加上测试集一共就13条,数据使用不同的随机种子会使得一些测试集中的特征在不同类别下的条件概率没有出现过,因为没有出现过,直接就不处理这个特征了,就容易出现误差。

例如:

当随机种子为1时:

当随机种子为4时:

朴素贝叶斯分类器优缺点分析:

优点:

  1. 适用广:朴素贝叶斯算法在处理大规模数据时表现良好,适用于许多实际问题。

  2. 对小规模数据表现良好:即使在小规模数据集上,朴素贝叶斯分类器也能有好的结果。

  3. 对缺失数据不敏感:朴素贝叶斯算法对缺失数据不敏感,即使有部分特征缺失或者未出现,仍然可以有效地进行分类。

缺点:

  1. 假设过于简化:朴素贝叶斯假设特征之间相互独立,在现实中,数据一般难以是真正独立的,因此会导致结果不准确。

  2. 处理连续性特征较差:朴素贝叶斯算法通常假设特征是离散的,对于连续性特征的处理不够灵活,可能会影响分类性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1668602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式——结构型模式——代理模式(静态代理、动态代理:JDK、CGLIB)

目录 代理模式 代理模式简介 代理模式的分类 代理模式组成 代理模式的优缺点 静态代理 背景前置 编写代码 JDK动态代理 编写代码 使用Arthas分析JDK动态代理底层原理 CGLIB动态代理 编写代码 三种代理的对比 代理模式使用场景 代理模式 代理模式简介 代理模式属…

测试萌新三天速通python基础(二)列表,字符串,元组,字典,遍历,容器,集合,函数

python基础 字符串下标(索引)切片字符串的替换 replace()字符串拆分 split()字符串的连接 join列表 list列表的增删改查列表的反转 reverse()排序列表嵌套元组 tuple 排序 升序降序交换变量字典 dict查询遍历容器集合函数参数函数的嵌套调⽤函数的返回值模块导⼊的⽅法____name…

重发布和路由策略实验(课堂练习)

需求: 将1.1.1.0/24网段(不在OSPF中)重发布到网络中,不允许出现次优路径,实现全网可达。 需求分析: 1、在R1上重发布1.1.1.0/24网段,但是需要过滤192.168.12.0/24和192.168.13.0/24 2、在R2和R3…

【论文阅读笔记】MapReduce: Simplified Data Processing on Large Clusters

文章目录 1 概念2 编程模型3 实现3.1 MapReduce执行流程3.2 master数据结构3.3 容错机制3.3.1 worker故障3.3.2 master故障3.3.3 出现故障时的语义 3.4 存储位置3.5 任务粒度3.6 备用任务 4 扩展技巧4.1 分区函数4.2 顺序保证4.3 Combiner函数4.4 输入和输出的类型4.5 副作用4.…

【超详细】跑通YOLOv8之深度学习环境配置2

环境配置2下载安装内容如下: CUDA:https://developer.nvidia.com/cuda-toolkit-archive cudnn:https://developer.nvidia.com/rdp/cudnn-archive 版本:CUDA11.3 cudnn8.9.7 CUDA安装 简介 CUDA(Compute Unified De…

【superset】基于MySQL的BI数据分析可视化实战案例(已更新)

1.熟悉、梳理、总结下superset可视化分析实战案例知识体系,一直想探索有效可用的可视化分析方案,大多收费或不好用,这里,借此机会总结、更新下。 2.复杂度高,遇到并解决的问题较多,尝试了很多次。 3.欢迎批评指正,跪谢一键三连! 基于MySQL的BI数据分析可视化实战案例文…

免费剪辑的素材资源网站,超高清、可商用、不限速、无版权,迅速有效的解决您的视频剪辑难题!

在数字媒体时代,高质量的剪辑素材已成为视频制作的核心资源。下面为您推荐的优质视频剪辑素材网站,都提供超高清、无限速、无版权、可商用的素材,这些网站将大大提升您的视频制作效率和质量 01. 蛙学府 实用性:★★★★☆ 丰富性&…

容器化Jenkins远程发布java应用(方式一:pipline+ssh)

1.创建pipline工程 2.准备工程Jenkinsfile文件(java目录) 1.文件脚本内容 env.fileName "planetflix-app.jar" env.configName "planetflix_prod" env.remoteDirectory "/data/project/java" env.sourceFile "/…

百度云防护如何开启CC攻击防护

百度云防护的最重要的功能是可以CC攻击防护,针对CC攻击,百度云防护有被动的CC攻击拦截规则,也有主动自定义访问策略拦截。 今天百度云来教大家如何开启百度云防护的CC攻击防御功能。 1.进入防护模板功能-创建模板 2.开启CC攻击防御功能&…

Windows单机部署RocketMQ5.X并与SpringBoot集成

RocketMQ 5.X下载 《RocketMQ 5.X下载》 下载后,解压缩 修改初始内存 修改runserver.cmd(Linux则为runserver.sh) 将下面的-Xms2g -Xmx2g -Xmn1g调小 if %JAVA_MAJOR_VERSION% lss 17 (set "JAVA_OPT%JAVA_OPT% -server -Xms2g -X…

Apache反代理Tomcat项目,分离应用服务器和WEB服务器

项目的原理是使用单独的机器做应用服务器,再用单独的机器做WEB服务器,从网络需要访问我们的应用的话,就会先经过我们的WEB服务器,再到达应用程序,这样子的好处是我们可以保护应用程序的机器位置,同时还可以…

长安汽车:基于云器 Lakehouse 的车联网大数据平台建设

近年来随着智能汽车行业的迅速发展,数据也在呈爆炸式增长。长安大数据平台承接了长安在生产上大部分流量应用及日常生产业务应用。本文将分享长安汽车在车联网场景下大数据平台建设面临的一些挑战和具体落地的实践。 主要内容如下: 1. 背景介绍 2. 长…

渲染农场多少钱一个小时?

​很多第一次准备使用渲染农场的小伙伴不知道渲染农场多少钱一个小时,今天就给大家介绍一下渲染农场多少钱一小时。 现在渲染农场基本都有CPU渲染和GPU渲染,各渲染农场的服务器配置不同,收费也各不相同,不过都是按渲染时长收费&…

C语言学习(八)typedef 虚拟内存 malloc/free

目录 一、typedef 类型重定义(一)使用(二)define和typedef的区别1. 编译处理的阶段不同2. 功能不同 二、虚拟内存(一)虚拟内存分布(二)内存分布1. 静态分配2. 动态分配 三、malloc/f…

如何使用canvas在图片上进行标注,以下代码不起作用,着实被坑到了(文末附完整代码)

今天发现一个有意思的问题&#xff1a; 如何使用canvas在图片上进行如下的标注&#xff0c;以下代码不起作用,如何修改 原始代码如下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name&quo…

理解进程的基本概念

1. 什么是进程 进程&#xff0c;即执行中的程序 进程 程序执行 在计算机中&#xff0c;每一个运行的exe程序&#xff0c;就是一个进程 2.为什么会有进程 早期&#xff0c;操作系统只有一个程序&#xff0c;这样效率是很低的。为了提高CPU的利用率&#xff0c;人们将多个程…

时间复杂度的简单讲解

小伙伴们大家好&#xff0c;我们又见面了&#xff0c;这次我们直接进入正题 时间复杂度的概念 时间复杂度的定义&#xff1a;在计算机科学中&#xff0c; 算法的时间复杂度是一个函数 &#xff0c;它定量描述了该算法的运行时间。一 个算法执行所耗费的时间&#xff0c;从理论…

Hadoop-未授权访问-内置配合命令执行RCE

一、Hadoop常见端口及配置文件 Hadoop中有多种端口&#xff0c;这些端口用于不同的服务和通信。以下是Hadoop中常见的端口以及它们的用途&#xff1a; NameNode Web界面端口 (默认: 9870)NameNode 对客户端服务端口 (默认: 8020)Secondary NameNode Web界面端口 (默认: 9868)…

每日OJ题_贪心算法四⑤_力扣354. 俄罗斯套娃信封问题

目录 力扣354. 俄罗斯套娃信封问题 解析代码1_动态规划&#xff08;超时&#xff09; 解析代码2_重写排序贪心二分 力扣354. 俄罗斯套娃信封问题 354. 俄罗斯套娃信封问题 难度 困难 给你一个二维整数数组 envelopes &#xff0c;其中 envelopes[i] [wi, hi] &#xff0…

物联网杀虫灯—新型的环保杀虫设备

型号推荐&#xff1a;云境天合TH-FD2S】物联网杀虫灯是一种新型环保杀虫设备&#xff0c;其中风吸式太阳能杀虫灯作为其一种特殊类型&#xff0c;展现了独特的工作原理和优势。 风吸式太阳能杀虫灯以太阳能电池板为电源&#xff0c;白天储存电源&#xff0c;晚上为杀虫灯提供电…