朴素贝叶斯算法

  • 一.朴素贝叶斯算法的概念

1.1简介

朴素贝叶斯分类器是贝叶斯算法中最常见和最简单的一种实现。它基于一个朴素的假设:即给定目标变量的值,特征之间是条件独立的。虽然这个假设在现实中往往不成立,但朴素贝叶斯分类器在很多实际应用中仍然表现出良好的性能。

1.2贝叶斯定理

提到贝叶斯定理,首先就得先讲述先验概率,后验概率以及条件概率。

先验概率:即基于统计的概率,是基于以往历史经验和分析得到的结果,不需要依赖当前发生的条件。

后验概率:则是从条件概率而来,由因推果,是基于当下发生了事件之后计算的概率,依赖于当前发生的条件。

条件概率:记事件A发生的概率为P(A),事件B发生的概率为P(B),则在B事件发生的前提下,A事件发生的概率即为条件概率,记为P(A|B)。

观看上图,以及结合高中所学的条件概率的部分知识得: P(A|B)=P(AB)/P(B);其中AB同时发生的概率即为P(AB),或写成P(AB)

而贝叶斯公式则是根据上述公式的变化得出的,即为

然而在我们的生活中,不是每次事件的相互关联都只有两个事件的,所以根据双事件的概率公式推导出了全概率公式,即为

而将全概率公式带入贝叶斯公式中便得到了一个新的公式,即为

  • 二.朴素贝叶斯算法的实现

2.1算法思路

给定一个训练数据集,我们可以计算每个类别的先验概率 P(C)(其中C是某个类别)和每个特征在每个类别下的条件概率 P(F|C)(其中F是某个特征)。

对于一个新的待分类样本,我们可以计算它属于每个类别的后验概率 P(C|F1, F2, ..., Fn)(其中F1, F2, ..., Fn是该样本的特征)。

后验概率可以表示为:

2.2代码实现

2.2.1定义加载数据集函数

定义加载数据集,其中包含训练集和待测集。

def loadDataSet():
    dataSet=[['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.697, 0.460, '好瓜'],
             ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', 0.774, 0.376, '好瓜'],
             ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.634, 0.264, '好瓜'],
             ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', 0.608, 0.318, '好瓜'],
             ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.556, 0.215, '好瓜'],            
             ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', 0.403, 0.237, '好瓜'],               
             ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', 0.481, 0.149, '好瓜'],                
             ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', 0.437, 0.211, '好瓜'],
             ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', 0.666, 0.091, '坏瓜'],
             ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', 0.243, 0.267, '坏瓜'],
             ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', 0.245, 0.057, '坏瓜'],
             ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', 0.343, 0.099, '坏瓜'],
             ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', 0.639, 0.161, '坏瓜'],  
             ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', 0.657, 0.198, '坏瓜'],
             ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', 0.360, 0.370, '坏瓜'],
             ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', 0.593, 0.042, '坏瓜'],
             ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', 0.719, 0.103, '坏瓜']]
    testSet= ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', 0.719, 0.103] # 待测集
    labels = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率'] # 特征
    return dataSet, testSet, labels

上述数据集选自于课堂上的好瓜坏瓜的数据集。

2.2.2再对先验概率进行计算

先验概率即为

现在对好瓜和坏瓜的个数分别计算,分别求出相对应的先验概率。

#计算先验概率P(c)
def prior():
    dataSet = loadDataSet()[0]  # 载入数据集
    countG = 0  # 初始化好瓜=0
    countB = 0  # 初始化坏瓜=0
    countAll = len(dataSet)
    for item in dataSet:    # 好瓜个数
        if item[-1] == "好瓜":
            countG += 1
    for item in dataSet:    # 坏瓜个数
        if item[-1] == "坏瓜":
            countB += 1  
    # 计算先验概率P(c)
    P_G = round(countG/countAll, 3)
    P_B = round(countB/countAll, 3)
    return P_G,P_B

2.2.3对于条件概率和连续属性均值和标准差

离散属性:

连续属性:

连续属性的条件概率公式:

(需先将μ和σ提前计算)

#计算(不同类别中指定连续特征的)均值、标准差

#计算(不同类别中指定连续特征的)均值、标准差
def mean_std(feature, cla):#feature:传入指定将要计算其均值的标准差的特征名称,cla:计算指定分类cla下该特征的条件概率
    dataSet, testSet, labels = loadDataSet()
    lst = [item[labels.index(feature)] for item in dataSet if item[-1]==cla]  #类别为cla中指定特征feature组成的列表
    mean = round(np.mean(lst), 3)   # 均值
    std = round(np.std(lst), 3)     # 标准差
    return mean, std

2.2.4计算连续属性的条件概率

由于西瓜的密度和含糖率被视为连续变量,为了分析好瓜与坏瓜在这些特性上的差异,我们需要计算密度和含糖率的平均值(均值)以及它们的变化程度(标准差)。通过这些统计量,我们可以进一步估算出在给定密度和含糖率条件下,西瓜是好瓜或坏瓜的条件概率。

#计算连续属性的条件概率p(xi|c)
def p():
    dataSet, testSet, labels = loadDataSet()    # 载入数据集
    denG_mean, denG_std = mean_std("密度", "好瓜")      # 好瓜密度的均值、标准差
    denB_mean, denB_std = mean_std("密度", "坏瓜")      # 坏瓜密度的均值、标准差
    sugG_mean, sugG_std = mean_std("含糖率", "好瓜")    # 好瓜含糖率的均值、标准差
    sugB_mean, sugB_std = mean_std("含糖率", "坏瓜")    # 坏瓜含糖率的均值、标准差
    # p(密度|好瓜)
    p_density_G = (1/(math.sqrt(2*math.pi)*denG_std))*np.exp(-(((testSet[labels.index("密度")]-denG_mean)**2)/(2*(denG_std**2))))
    p_density_G = round(p_density_G, 3)
    # p(密度|坏瓜)
    p_density_B = (1/(math.sqrt(2*math.pi)*denB_std))*np.exp(-(((testSet[labels.index("密度")]-denB_mean)**2)/(2*(denB_std**2))))
    p_density_B = round(p_density_B, 3)
    # p(含糖率|好瓜)
    p_sugar_G = (1/(math.sqrt(2*math.pi)*sugG_std))*np.exp(-(((testSet[labels.index("含糖率")]-sugG_mean)**2)/(2*(sugG_std**2))))
    p_sugar_G = round(p_sugar_G, 3)
    # p(含糖率|坏瓜)
    p_sugar_B = (1/(math.sqrt(2*math.pi)*sugB_std))*np.exp(-(((testSet[labels.index("含糖率")]-sugB_mean)**2)/(2*(sugB_std**2))))
    p_sugar_B = round(p_sugar_B, 3)
    return p_density_G, p_density_B, p_sugar_G, p_sugar_B

2.2.5计算离散属性的条件概率

通过将训练集中的样本按属性归类,便于计算离散属性的条件概率,代码中通过index实现遍历待测样本特征值的索引位置对应训练样本的索引位置。

#计算离散属性的条件概率P(xi|c)
def P(index, cla):
    dataSet, testSet, labels = loadDataSet()    # 载入数据集
    countG = 0  # 初始化好瓜数量
    countB = 0  # 初始化坏瓜数量
    for item in dataSet:    # 统计好瓜个数
        if item[-1] == "好瓜":
            countG += 1
    for item in dataSet:    # 统计坏瓜个数
        if item[-1] == "坏瓜":
            countB += 1  
    lst = [item for item in dataSet if (item[-1] == cla) & (item[index] == testSet[index])] # lst为cla类中第index个属性上取值为xi的样本组成的集合
    P = round(len(lst)/(countG if cla=="好瓜" else countB), 3)  # 计算条件概率
    return P

2.2.6计算后验概率

#预测后验概率P(c|xi)
def bayes():
    #计算类先验概率
    P_G, P_B = prior()
    #计算离散属性的条件概率
    P0_G = P(0, "好瓜") # P(青绿|好瓜)
    P0_B = P(0, "坏瓜") # P(青绿|坏瓜)
    P1_G = P(1, "好瓜") # P(蜷缩|好瓜)
    P1_B = P(1, "坏瓜") # P(蜷缩|坏瓜)
    P2_G = P(2, "好瓜") # P(浊响|好瓜)
    P2_B = P(2, "坏瓜") # P(浊响|坏瓜)
    P3_G = P(3, "好瓜") # P(清晰|好瓜)
    P3_B = P(3, "坏瓜") # P(清晰|坏瓜)
    P4_G = P(4, "好瓜") # P(凹陷|好瓜)
    P4_B = P(4, "坏瓜") # P(凹陷|坏瓜)
    P5_G = P(5, "好瓜") # P(硬滑|好瓜)
    P5_B = P(5, "坏瓜") # P(硬滑|坏瓜)
    #计算连续属性的条件概率
    p_density_G, p_density_B, p_sugar_G, p_sugar_B = p()
    #计算后验概率
    isGood = P_G * P0_G * P1_G * P2_G * P3_G * P4_G * P5_G * p_density_G * p_sugar_G    # 计算是好瓜的后验概率
    isBad = P_B * P0_B * P1_B * P2_B * P3_B * P4_B * P5_B * p_density_B * p_sugar_B     # 计算是坏瓜的后验概率
    return isGood,isBad

上图即为课中所讲的测试条件概率,本文不再讲述。

2.2.7main函数

if __name__=='__main__':
    dataSet, testSet, labels = loadDataSet()
    testSet = [testSet]
    df = pd.DataFrame(testSet, columns=labels, index=[1])
    print(f"待测集:\n{df}")
    isGood, isBad = bayes()
    print("后验概率:")
    print(f"P(好瓜|xi) = {isGood}")
    print(f"P(坏瓜|xi) = {isBad}")
    print("预测结果 : 好瓜" if (isGood > isBad) else "预测结果 : 坏瓜")

2.2.8整体代码

import numpy as np
import math
import pandas as pd
 
#加载数据集函数
#dataSet:训练集  testSet:待测集  labels:样本所具有的特征的名称
def loadDataSet():
    dataSet=[['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.697, 0.460, '好瓜'],
             ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', 0.774, 0.376, '好瓜'],
             ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.634, 0.264, '好瓜'],
             ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', 0.608, 0.318, '好瓜'],
             ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.556, 0.215, '好瓜'],            
             ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', 0.403, 0.237, '好瓜'],               
             ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', 0.481, 0.149, '好瓜'],                
             ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', 0.437, 0.211, '好瓜'],
             ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', 0.666, 0.091, '坏瓜'],
             ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', 0.243, 0.267, '坏瓜'],
             ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', 0.245, 0.057, '坏瓜'],
             ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', 0.343, 0.099, '坏瓜'],
             ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', 0.639, 0.161, '坏瓜'],  
             ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', 0.657, 0.198, '坏瓜'],
             ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', 0.360, 0.370, '坏瓜'],
             ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', 0.593, 0.042, '坏瓜'],
             ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', 0.719, 0.103, '坏瓜']]
    testSet= ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', 0.719, 0.103] # 待测集
    labels = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率'] # 特征
    return dataSet, testSet, labels
 
#计算(不同类别中指定连续特征的)均值、标准差
def mean_std(feature, cla):#feature:传入指定将要计算其均值的标准差的特征名称,cla:计算指定分类cla下该特征的条件概率
    dataSet, testSet, labels = loadDataSet()
    lst = [item[labels.index(feature)] for item in dataSet if item[-1]==cla]  #类别为cla中指定特征feature组成的列表
    mean = round(np.mean(lst), 3)   # 均值
    std = round(np.std(lst), 3)     # 标准差
    return mean, std
 
#计算先验概率P(c)
def prior():
    dataSet = loadDataSet()[0]  # 载入数据集
    countG = 0  # 初始化好瓜=0
    countB = 0  # 初始化坏瓜=0
    countAll = len(dataSet)
    for item in dataSet:    # 好瓜个数
        if item[-1] == "好瓜":
            countG += 1
    for item in dataSet:    # 坏瓜个数
        if item[-1] == "坏瓜":
            countB += 1  
    # 计算先验概率P(c)
    P_G = round(countG/countAll, 3)
    P_B = round(countB/countAll, 3)
    return P_G,P_B
 
#计算离散属性的条件概率P(xi|c)
def P(index, cla):
    dataSet, testSet, labels = loadDataSet()    # 载入数据集
    countG = 0  # 初始化好瓜数量
    countB = 0  # 初始化坏瓜数量
    for item in dataSet:    # 统计好瓜个数
        if item[-1] == "好瓜":
            countG += 1
    for item in dataSet:    # 统计坏瓜个数
        if item[-1] == "坏瓜":
            countB += 1  
    lst = [item for item in dataSet if (item[-1] == cla) & (item[index] == testSet[index])] # lst为cla类中第index个属性上取值为xi的样本组成的集合
    P = round(len(lst)/(countG if cla=="好瓜" else countB), 3)  # 计算条件概率
    return P
 
#计算连续属性的条件概率p(xi|c)
def p():
    dataSet, testSet, labels = loadDataSet()    # 载入数据集
    denG_mean, denG_std = mean_std("密度", "好瓜")      # 好瓜密度的均值、标准差
    denB_mean, denB_std = mean_std("密度", "坏瓜")      # 坏瓜密度的均值、标准差
    sugG_mean, sugG_std = mean_std("含糖率", "好瓜")    # 好瓜含糖率的均值、标准差
    sugB_mean, sugB_std = mean_std("含糖率", "坏瓜")    # 坏瓜含糖率的均值、标准差
    # p(密度|好瓜)
    p_density_G = (1/(math.sqrt(2*math.pi)*denG_std))*np.exp(-(((testSet[labels.index("密度")]-denG_mean)**2)/(2*(denG_std**2))))
    p_density_G = round(p_density_G, 3)
    # p(密度|坏瓜)
    p_density_B = (1/(math.sqrt(2*math.pi)*denB_std))*np.exp(-(((testSet[labels.index("密度")]-denB_mean)**2)/(2*(denB_std**2))))
    p_density_B = round(p_density_B, 3)
    # p(含糖率|好瓜)
    p_sugar_G = (1/(math.sqrt(2*math.pi)*sugG_std))*np.exp(-(((testSet[labels.index("含糖率")]-sugG_mean)**2)/(2*(sugG_std**2))))
    p_sugar_G = round(p_sugar_G, 3)
    # p(含糖率|坏瓜)
    p_sugar_B = (1/(math.sqrt(2*math.pi)*sugB_std))*np.exp(-(((testSet[labels.index("含糖率")]-sugB_mean)**2)/(2*(sugB_std**2))))
    p_sugar_B = round(p_sugar_B, 3)
    return p_density_G, p_density_B, p_sugar_G, p_sugar_B
 
#预测后验概率P(c|xi)
def bayes():
    #计算类先验概率
    P_G, P_B = prior()
    #计算离散属性的条件概率
    P0_G = P(0, "好瓜") # P(青绿|好瓜)
    P0_B = P(0, "坏瓜") # P(青绿|坏瓜)
    P1_G = P(1, "好瓜") # P(蜷缩|好瓜)
    P1_B = P(1, "坏瓜") # P(蜷缩|坏瓜)
    P2_G = P(2, "好瓜") # P(浊响|好瓜)
    P2_B = P(2, "坏瓜") # P(浊响|坏瓜)
    P3_G = P(3, "好瓜") # P(清晰|好瓜)
    P3_B = P(3, "坏瓜") # P(清晰|坏瓜)
    P4_G = P(4, "好瓜") # P(凹陷|好瓜)
    P4_B = P(4, "坏瓜") # P(凹陷|坏瓜)
    P5_G = P(5, "好瓜") # P(硬滑|好瓜)
    P5_B = P(5, "坏瓜") # P(硬滑|坏瓜)
    #计算连续属性的条件概率
    p_density_G, p_density_B, p_sugar_G, p_sugar_B = p()
    #计算后验概率
    isGood = P_G * P0_G * P1_G * P2_G * P3_G * P4_G * P5_G * p_density_G * p_sugar_G    # 计算是好瓜的后验概率
    isBad = P_B * P0_B * P1_B * P2_B * P3_B * P4_B * P5_B * p_density_B * p_sugar_B     # 计算是坏瓜的后验概率
    return isGood,isBad
 
if __name__=='__main__':
    dataSet, testSet, labels = loadDataSet()
    testSet = [testSet]
    df = pd.DataFrame(testSet, columns=labels, index=[1])
    print(f"待测集:\n{df}")
    isGood, isBad = bayes()
    print("后验概率:")
    print(f"P(好瓜|xi) = {isGood}")
    print(f"P(坏瓜|xi) = {isBad}")
    print("预测结果 : 好瓜" if (isGood > isBad) else "预测结果 : 坏瓜")

2.3测试结果

  • 三.实验结果

本次实验我们学习了朴素贝叶斯,并学习了它的算法,优化及代码实现。朴素贝叶斯算法对大数量训练和查询时具有较高的速度。即使使用超大规模的训练集,针对每个项目通常也只会有相对较少的特征数,并且对项目的训练和分类也仅仅是特征概率的数学运算而已;对小规模的数据表现很好,能个处理多分类任务,适合增量式训练(即可以实时的对新增的样本进行训练;对缺失数据不太敏感,算法也比较简单,常用于文本分类;此外朴素贝叶斯对结果解释容易理解。但对于输入数据的准备方式较为敏感,因此对数据的准备方式要符合条件。总的来说是一次收获满满的实验。

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值