一、分析贝叶斯算法
贝叶斯定理是概率论中的一个定理,描述在已知一些条件下,某事件的发生概率。比如,如果已知某种健康问题与寿命有关,使用贝叶斯定理则可以通过得知某人年龄,来更加准确地计算出某人有某种健康问题的概率。
通常,事件A在事件B已发生的条件下发生的概率,与事件B在事件A已发生的条件下发生的概率是不一样的。然而,这两者是有确定的关系的,贝叶斯定理就是这种关系的陈述。贝叶斯公式的一个用途,即透过已知的三个概率而推出第四个概率。贝叶斯定理跟随机变量的条件概率以及边际概率分布有关。
整体来说,贝叶斯定理是概率论中的一个定理,描述在已知一些条件下,某事件的发生概率。即描述的是条件概率。
贝叶斯算法是一种基于概率统计的分类算法,通过对先验概率和条件概率的计算,来判断新数据所属的类别。它的基本思想是:对于给定的数据集,计算每个类别的先验概率,然后计算每个属性在每个类别中的条件概率,最后根据贝叶斯公式计算后验概率,选择概率最大的类别作为预测结果。
二、计算条件概率
在贝叶斯算法中,需要计算每个属性在每个类别中的条件概率。假设有n个属性和m个类别,那么每个属性在每个类别中的条件概率可以表示为一个n*m的矩阵P,其中P(i,j)表示第i个属性在第j个类别中的条件概率。其中,条件概率表示在某个事件发生的情况下,另外一个事件发生的概率。假设事件A和B相互独立,则条件概率P(B|A)等于事件A和B同时发生的概率除以事件A发生的概率,即:P(B|A) = P(A ∩ B) / P(A)
。
三、预测精度的计算与评估
我们可以用准确率(Accuracy
)、召回率(Recall
)、F1值
等指标来评估贝叶斯分类算法的预测精度。
- 准确率是所有预测正确的样本数占总样本数的比例;
- 召回率是真正例(预测为正例且实际为正例)占所有实际正例的比例;
- F1值是准确率和召回率的调和平均数。
四、编程实现贝叶斯分类算法,并对简单应用样本数据实现预测分类(1)
实现了贝叶斯分类算法的应用,主要包括三部分:
- 实现朴素贝叶斯分类器(在bayes_classfier.py中定义)
bayes_classfier.py 定义了一个类 navie_bayes_classifier,包含了一些方法来实现朴素贝叶斯分类器。其中 __init __ () 方法用于初始化模型的一些参数,例如先验概率、条件概率和特征概率等;get_label() 方法则用于对输入的特征值进行分类,并返回每种水果属于该类别的概率。 - 生成训练数据集和测试数据集(在generate_attires.py 中定义)
generate_attires.py 实现了一个函数 gen_attrs(),用于生成一些随机的属性值来作为测试数据集,这些属性值包括水果的长度、甜度和颜色。 - 在主函数中调用贝叶斯分类器对测试数据集进行分类,并输出分类结果
在 main() 函数中,我们使用 gen_attrs() 函数生成了一些随机的属性值,然后通过 navie_bayes_classifier() 函数创建了一个朴素贝叶斯分类器对象。接着,我们遍历测试数据集中的每组属性值,将其传入 get_label() 方法中,得到每种水果属于该类别的概率,并通过排序选出概率最大的标签,并打印出预测结果和属于哪种水果的概率。
bayes_classfier.py(贝叶斯算法)
# python # @author ZShiJ # 贝叶斯算法 ####训练数据集---->合适参数 # 定义了一个训练数据集 datasets,其中包含若干个水果,每个水果有三个特征:长度、甜度和颜色。每个特征都有两个取值:长或短、甜或不甜、黄或不黄。 datasets = {'banala': {'long': 400, 'not_long': 100, 'sweet': 350, 'not_sweet': 150, 'yellow': 450, 'not_yellow': 50}, 'orange': {'long': 0, 'not_long': 300, 'sweet': 150, 'not_sweet': 150, 'yellow': 300, 'not_yellow': 0}, 'other_fruit': {'long': 100, 'not_long': 100, 'sweet': 150, 'not_sweet': 50, 'yellow': 50, 'not_yellow': 150} } # 定义count_total函数计算各种水果的总数,返回一个字典,包括每种水果的总数。 def count_total(data): """计算各种水果的总数return {‘banala’:500 ...}""" count = {} total = 0 for fruit in data: ''' 因为水果要么甜要么不甜,可以用这两种特征来统计总数 ''' count[fruit] = data[fruit]['sweet'] + data[fruit]['not_sweet'] total += count[fruit] return count, total # 对训练数据集进行计数,得到每种水果的数量和所有水果的数量 categories,simpleTotal = count_total(datasets) print(categories,simpleTotal) ########################################################### # 定义cal_base_rates函数计算各种水果的先验概率,即每种水果出现的概率。 def cal_base_rates(data): '''计算各种水果的先验概率 return {‘banala’:0.5 ...}''' categories, total = count_total(data) cal_base_rates = {} for label in categories: priori_prob = categories[label] / total cal_base_rates[label] = priori_prob return cal_base_rates # 计算各类水果的先验概率 Prio = cal_base_rates(datasets) print(Prio) ############################################################ # 定义likelihold_prob函数计算各个特征值在已知水果下的概率(likelihood probabilities),即对于每种水果,分别计算其每个特征取值的概率。 def likelihold_prob(data): '''计算各个特征值在已知水果下的概率(likelihood probabilities) 返回一个字典,格式为{'banala':{'long':0.8}...}''' count, _ = count_total(data) likelihold = {} for fruit in data: '''创建一个临时的字典,临时存储各个特征值的概率''' attr_prob = {} for attr in data[fruit]: # 计算各个特征值在已知水果下的概率 attr_prob[attr] = data[fruit][attr] / count[fruit] likelihold[fruit] = attr_prob return likelihold # 计算各个特征值在已知水果下的条件概率 LikeHold = likelihold_prob(datasets) print(LikeHold) ############################################################ # 定义evidence_prob函数计算特征的概率对分类结果的影响,即对于每个特征,计算所有水果中该特征取值的概率,并返回一个字典,格式为 {'long':50%...} def evidence_prob(data): '''计算特征的概率对分类结果的影响 return {'long':50%...}''' # 获取数据集中所有水果的特征 attrs = list(data['banala'].keys()) count, total = count_total(data) evidence_prob = {} # 计算各种特征的概率 for attr in attrs: attr_total = 0 for fruit in data: attr_total += data[fruit][attr] evidence_prob[attr] = attr_total / total return evidence_prob # 计算特征的概率对分类结果的影响 Evidence_prob = evidence_prob(datasets) print(Evidence_prob) ########################################################## # 以上是训练数据用到的函数,即将数据转化为代码计算概率 ########################################################## # 定义navie_bayes_classifier类实现朴素贝叶斯分类器。初始化时调用上述四个函数计算相关概率值,并把结果保存为类的成员变量。 class navie_bayes_classifier: '''初始化贝叶斯分类器,实例化时会调用__init__函数''' def __init__(self, data=datasets): self._data = datasets self._labels = [key for key in self._data.keys()] self._priori_prob = cal_base_rates(self._data) self._likelihold_prob = likelihold_prob(self._data) self._evidence_prob = evidence_prob(self._data) # get_label方法用于根据输入的特征值获取某一组特征值的类别。具体实现是先根据贝叶斯公式计算出每种水果的后验概率,然后找到其中最大值作为分类结果。在计算后验概率时,先取某水果占比率,然后分别计算每个特征取值的概率除以总的该特征取值概率,最后把这些概率乘起来即可。 def get_label(self, length, sweetness, color): # 可以直接调用上面类中定义的变量 '''获取某一组特征值的类别''' self._attrs = [length, sweetness, color] res = {} for label in self._labels: prob = self._priori_prob[label] # 取某水果占比率 # print("各个水果的占比率:",prob) for attr in self._attrs: # 单个水果的某个特征概率除以总的某个特征概率 再乘以某水果占比率 prob *= self._likelihold_prob[label][attr] / self._evidence_prob[attr] # print(prob) res[label] = prob # print(res) return res """ 这是一个朴素贝叶斯分类器的 Python 代码,下面是代码的具体解释: 1. 首先定义了一个训练数据集 datasets,其中包含若干个水果,每个水果有三个特征:长度、甜度和颜色。每个特征都有两个取值:长或短、甜或不甜、黄或不黄。 2. count_total函数计算各种水果的总数,返回一个字典,包括每种水果的总数。 3. cal_base_rates函数计算各种水果的先验概率,即每种水果出现的概率。 4. likelihold_prob函数计算各个特征值在已知水果下的概率(likelihood probabilities),即对于每种水果,分别计算其每个特征取值的概率。 5. evidence_prob函数计算特征的概率对分类结果的影响,即对于每个特征,计算所有水果中该特征取值的概率。 6. navie_bayes_classifier类实现朴素贝叶斯分类器。初始化时调用上述四个函数计算相关概率值,并把结果保存为类的成员变量。 7. get_label方法用于根据输入的特征值获取某一组特征值的类别。具体实现是先根据贝叶斯公式计算出每种水果的后验概率,然后找到其中最大值作为分类结果。在计算后验概率时,先取某水果占比率,然后分别计算每个特征取值的概率除以总的该特征取值概率,最后把这些概率乘起来即可。 """
运行结果:
generate_attires.py(生成测试/训练集合)
# python # @author ZShiJ # 生成测试/训练集合 import random # random_attr函数用于从一对特征值中随机选取一个。 def random_attr(pair): # 生成0-1之间的随机数 return pair[random.randint(0, 1)] # gen_attrs函数用于生成一组特征值数据集。首先定义了三个特征取值集合,即长度(长/短)、甜度(甜/不甜)和颜色(黄/不黄)。然后使用map函数和random_attr函数,从每个特征取值集合中随机选取一个特征值,生成一组随机的特征值。重复执行该过程20次,生成20组随机的特征值数据集,返回包含这些特征值数据集的列表。 def gen_attrs(): # 特征值的取值集合 sets = [('long', 'not_long'), ('sweet', 'not_sweet'), ('yellow', 'not_yellow')] test_datasets = [] for i in range(20):# 从sets中随机选择一个元组,然后用random_attr函数来生成该元组对应的特征值 # 使用map函数来生成一组特征值 test_datasets.append(list(map(random_attr, sets))) return test_datasets # 调用gen_attrs函数,并打印输出 print(gen_attrs()) """ 这是一个 Python 代码,用于生成随机的特征值数据集。具体解释如下: 1. random_attr函数用于从一对特征值中随机选取一个。 2. gen_attrs函数用于生成一组特征值数据集。首先定义了三个特征取值集合,即长度(长/短)、甜度(甜/不甜)和颜色(黄/不黄)。然后使用map函数和random_attr函数,从每个特征取值集合中随机选取一个特征值,生成一组随机的特征值。重复执行该过程20次,生成20组随机的特征值数据集,返回包含这些特征值数据集的列表。 """
运行结果:
classfication.py(分类(最终结果))
# python # @author ZShiJ # 分类(最终结果) import operator # 运算符模块,用于排序操作 import bayes_classfier # 自定义朴素贝叶斯分类器模块 import generate_attires # 自动生成测试数据集模块 # main函数首先使用generate_attires模块中的gen_attrs函数生成一组特征值数据集,然后实例化一个navie_bayes_classifier对象classfier。 def main(): # 生成测试数据集(在 generate_attires.py 中定义) test_datasets = generate_attires.gen_attrs() # 创建朴素贝叶斯分类器对象(在 bayes_classfier.py 中定义) classfier = bayes_classfier.navie_bayes_classifier() for data in test_datasets: print("特征值:", end='\t') print(data) print("预测结果:", end='\t') res = classfier.get_label(*data) # 表示多参传入 print(res) # 预测属于哪种水果的概率 print('水果类别:', end='\t') # 对后验概率排序,输出概率最大的标签 print(sorted(res.items(), key=operator.itemgetter(1), reverse=True)[0][0]) if __name__ == '__main__': # 当该模块自身被运行时,执行 main() 函数 main() """ 这是一个 Python 代码,用于测试生成的随机特征值数据集的分类结果。具体解释如下: 1. 导入operator模块和bayes_classfier模块还有generate_attires模块。 2. main函数首先使用generate_attires模块中的gen_attrs函数生成一组特征值数据集,然后实例化一个navie_bayes_classifier对象classfier。 3. 对于每个特征值数据集,调用classfier的get_label方法获取属于哪种水果的概率,然后对后验概率排序,输出概率最大的标签,并打印出预测结果和属于哪种水果的概率。 4. 最后,在if __name__ == '__main__'条件下执行main函数。 """
运行结果:
五、编程实现贝叶斯分类算法,并对简单应用样本数据实现预测分类(2)
算法分析
实现了一个简单的朴素贝叶斯分类器,根据数据集进行训练和测试,最终输出分类结果和准确率。下面是对算法过程的分析:
- 输入训练数据:
- 使用NumPy数组表示的数据集包含了西瓜的各种属性信息,最后一列是类别标签。
features
:字典,包含了各个特征的可能取值。labels
:一个包含了标签的列表,这里是[“好瓜”, “坏瓜”]。
- 设定训练数据集与测试数据集大小:
- 在
if __name__ == "__main__":
部分,通过切片操作X = dataset[:, :6]
和Y = dataset[:, 6]
分别将数据集中的特征和标签提取出来,用于后续的训练和测试。
- 计算训练数据集数据中各属性在各类中的概率分布情况:
- 朴素贝叶斯类定义(
Bayes
类):首先初始化了先验概率和条件概率的字典,并为每个类别、每个属性、每个属性值设定了初始概率。
__init__
方法:初始化贝叶斯分类器的属性,包括特征、标签、条件概率、先验概率等。bayes_prob
方法:计算贝叶斯概率。train
方法:训练分类器,计算先验概率和条件概率。predic
方法:进行预测。show_params
方法:展示参数,主要用于调试。
- 利用测试数据计算贝叶斯算法的分类精度:
- 使用
predic()
方法对测试数据进行分类预测,该方法计算了每个类别的后验概率,并选择概率最大的类别作为预测结果。 - 输出了预测结果并计算了分类器的准确率。
- 输出分类结果:
- 输出了先验概率和条件概率的情况,以及最终的分类预测结果和准确率。
代码
from pprint import pprint # 用于漂亮地打印数据结构 import numpy as np # 用于处理数组和矩阵 # 原始数据集 dataset = np.array([ # 色泽,根蒂,敲击,纹理,脐部,触感, 好坏 ['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'], ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'], ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '好瓜'], ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', '好瓜'], ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', '好瓜'], ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜'], ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '坏瓜'], ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', '坏瓜'], ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', '坏瓜'], ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '坏瓜'], ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '坏瓜'], ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '坏瓜'], ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '坏瓜'], ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜'] ]) # 特征及其可能取值 features = { '色泽': ['青绿', '乌黑', '浅白'], '根蒂': ['蜷缩', '稍蜷', '硬挺'], '敲击': ['浊响', '沉闷', '清脆'], '纹理': ['清晰', '稍糊', '模糊'], '脐部': ['凹陷', '稍凹', '平坦'], '触感': ['硬滑', '软粘'] } # 类别标签 labels = ["好瓜", "坏瓜"] # 贝叶斯分类器类 class Bayes: def __init__(self, features: dict, labels: list) -> None: # 初始化特征、标签、特征类别等 self.features = features # 特征字典 self.labels = labels # 标签列表 self.feature_classes = list(self.features.keys()) # 特征类别列表 self.condi_prob = {} # 条件概率字典 self.prior_prob = {} # 先验概率字典 self._statistic = {} # 统计信息字典 self._samples = 0 # 样本数量 # 初始化先验概率、条件概率及统计信息 for i in self.labels: # 遍历每个标签 self.prior_prob[i] = 1 / len(self.labels) # 初始化先验概率为均匀分布 self.condi_prob[i] = {} # 初始化条件概率字典 self._statistic[i] = 0 # 初始化统计信息为0 for j in self.feature_classes: # 遍历每个特征类别 self.condi_prob[i][j] = {} # 初始化条件概率字典 for k in self.features[j]: # 遍历特征的可能取值 self.condi_prob[i][j][k] = 1 / len(self.features[j]) # 初始化条件概率为均匀分布 self._statistic['.'.join([i, j, k])] = 0 # 初始化统计信息为0 # 计算朴素贝叶斯概率 def bayes_prob(self, arr): plist = [] for i in self.labels: # 遍历所有标签 idx = 0 p = self.prior_prob[i] # 获取标签的先验概率 for j in self.feature_classes: # 遍历所有特征 p *= self.condi_prob[i][j][arr[idx]] # 计算条件概率 idx += 1 plist.append(p) # 将计算得到的朴素贝叶斯概率添加到列表中 return plist # 训练朴素贝叶斯模型 def train(self, X, Y): rows, cols = X.shape # 获取数据集的行数和列数 for i in range(rows): # 遍历每个样本 self._statistic[Y[i]] += 1 # 统计每个类别出现的次数 for j in range(cols): # 遍历每个特征 self._statistic['.'.join([Y[i], self.feature_classes[j], X[i, j]])] += 1 # 统计每个类别下每个特征取值出现的次数 self._samples += 1 # 统计样本总数 for i in self.labels: # 遍历所有标签 self.prior_prob[i] = (self._statistic[i] + 1) / (self._samples+len(self.labels)) # 计算每个类别的先验概率 for j in self.feature_classes: # 遍历所有特征 for k in self.features[j]: # 遍历特征的可能取值 self.condi_prob[i][j][k] = (self._statistic['.'.join([i, j, k])] + 1) / (self._statistic[i] + len(self.features[j])) # 计算每个类别下每个特征取值的条件概率 # 预测 def predic(self, X): rows, cols = X.shape # 获取测试数据集的行数和列数 res = [] for i in range(rows): # 遍历每个测试样本 y = self.bayes_prob(X[i]) # 计算每个样本的朴素贝叶斯概率 res.append(y) # 将计算得到的朴素贝叶斯概率添加到结果列表中 res = np.argmax(res, axis=1) # 找到每个样本中最大概率对应的标签索引 res = [self.labels[x] for x in res] # 将标签索引转换为标签值 return np.array(res) # 返回预测结果数组 # 显示模型参数 def show_params(self) -> str: pprint(self.prior_prob) # 打印先验概率 pprint(self.condi_prob , sort_dicts=False) # 打印条件概率 if __name__ == "__main__": X = dataset[:, :6] # 特征 Y = dataset[:, 6] # 标签 print(X) print(Y) clf = Bayes(features, labels) # 初始化分类器 clf.show_params() # 显示参数 clf.train(X, Y) # 训练模型 clf.show_params() # 显示参数 Yp = clf.predic(X) # 预测 print(f"原始:{Y}\n预测:{Yp}\n准确率:{np.sum(Y==Yp)/len(Y)}") # 输出准确率
结果
计算训练数据集数据中各属性在各类中的概率分布情况
结果显示了训练数据集中各属性在各类别中的概率分布情况。在结果中,每个类别(“好瓜"和"坏瓜”)都有一个先验概率。在每个类别下,特征属性(如色泽、根蒂、敲击等)都有不同取值(如青绿、乌黑、浅白等),并且对应着该取值在该类别下的条件概率。
对于"好瓜"类别:
- 在属性"色泽"下,青绿、乌黑和浅白的概率分别是约0.36、0.45和0.18。
- 在属性"根蒂"下,蜷缩、稍蜷和硬挺的概率分别是约0.55、0.36和0.09。
- 在属性"敲击"下,浊响、沉闷和清脆的概率分别是约0.64、0.27和0.09。
- 在属性"纹理"下,清晰、稍糊和模糊的概率分别是约0.73、0.18和0.09。
- 在属性"脐部"下,凹陷、稍凹和平坦的概率分别是约0.55、0.36和0.09。
- 在属性"触感"下,硬滑和软粘的概率分别是0.7和0.3。
对于"坏瓜"类别:
- 在属性"色泽"下,青绿、乌黑和浅白的概率分别是约0.33、0.25和0.42。
- 在属性"根蒂"下,蜷缩、稍蜷和硬挺的概率分别是约0.33、0.42和0.25。
- 在属性"敲击"下,浊响、沉闷和清脆的概率分别是约0.42、0.33和0.25。
- 在属性"纹理"下,清晰、稍糊和模糊的概率分别是约0.25、0.42和0.33。
- 在属性"脐部"下,凹陷、稍凹和平坦的概率分别是约0.25、0.33和0.42。
- 在属性"触感"下,硬滑和软粘的概率分别是0.64和0.36。
这些概率分布可以帮助我们在给定一个新样本时,根据其特征属性的取值,利用贝叶斯算法来预测其属于"好瓜"还是"坏瓜"类别。
利用测试数据计算贝叶斯算法的分类精度
分类精度的结果显示了贝叶斯算法在测试数据集上的表现。根据预测结果和实际标签的比较,算法的准确率为约82.35%。
具体来说:
- 原始的标签包括了17个样本,其中有8个属于"好瓜"类别,9个属于"坏瓜"类别。
- 预测的结果与原始标签进行了比较,其中有14个样本的预测结果与原始标签一致,3个样本的预测结果与原始标签不一致。
- 根据比较结果计算得到的准确率为0.8235,即82.35%。
这个准确率表明,贝叶斯算法在这个测试数据集上的分类表现较为良好,但仍有改进的空间。