朴素贝叶斯

引言:
朴素贝叶斯分类是机器学习中常用的一种文本分类算法。它基于贝叶斯定理和特征独立性假设,具有简单、高效和易于理解的特点。它在自然语言处理、垃圾邮件过滤、情感分析等领域具有广泛的应用。

1.简介
朴素贝叶斯是一种基于贝叶斯定理的概率生成模型,核心原则是特征条件独立假设,即给定目标值时特征之间相互独立。它通过训练样本计算各类别的先验概率及特征的条件概率,分类时依据贝叶斯公式求得后验概率并依此判断类别,算法简单高效,对小规模数据表现良好,适合文本分类、垃圾邮件过滤等多分类任务,但其“特征独立”假设在实际中往往难以完全满足。
2.贝叶斯定理
首先,让我们来了解一下贝叶斯定理,它是朴素贝叶斯分类算法的基础。贝叶斯定理描述了在已知一些条件下,另一事件发生的概率。假设有事件A和事件B,贝叶斯定理可以表示为:
在这里插入图片描述
公式解释:
P(B∣A) 是后验概率(Posterior Probability),表示在 A 发生的条件下 B 发生的概率。
P(A∣B) 是似然概率(Likelihood),表示在 B 发生的条件下 A 发生的概率。
P(B) 是先验概率(Prior Probability),表示 B 发生的初始概率(不考虑 A 的影响)。
P(A) 是边缘概率(Marginal Probability),表示 A 发生的概率(总概率)。
应用场景:
贝叶斯定理在机器学习(如朴素贝叶斯分类器)、医疗诊断、垃圾邮件过滤等领域广泛应用,用于更新对事件发生概率的估计(根据新数据调整先验概率)。
3.朴素贝叶斯分类
基本思想
基于贝叶斯定理和特征条件独立假设,用于对样本进行分类。
原理
朴素贝叶斯分类将输入文本表示为一个向量,其中每个维度对应于一个特征值。假设有 N 个特征,分类任务有 K 个类别。对于一个给定的文本样本 X,朴素贝叶斯分类通过计算以下公式来预测它所属的类别:

P(Y = c|X) = P(X|Y = c) * P(Y = c) / P(X)

其中,P(Y = c|X) 表示给定输入文本 X 情况下,它属于类别 c 的概率。P(X|Y = c) 是朴素贝叶斯分类器的核心部分,它表示在类别 c 的情况下,特征向量 X 出现的概率。P(Y = c) 表示类别 c 的先验概率,即在没有任何观察之前,样本属于类别 c 的概率。P(X) 是一个归一化因子,用于确保概率的和等于 1。
基本公式
在这里插入图片描述
公式解释:
1 .目标:
• y=f(x): 对输入样本 x 进行分类,预测其类别 y。
argmax c k
选择使后验概率最大的类别 c k作为预测类别。
2. 分子部分:
在这里插入图片描述
类别 c k的先验概率,表示在没有观测到特征时,类别 c k
​出现的概率。

在这里插入图片描述
在类别 c k 下,所有特征 X (j) 的条件概率的乘积。这里假设特征之间条件独立(朴素贝叶斯的核心假设)。
3.分母部分:

在这里插入图片描述
对所有可能的类别 c k ,计算分子部分的总和。它是一个归一化因子,确保后验概率的总和为 1。
4. 整体含义
• 公式计算了在观测到特征 X 的情况下,每个类别 c k 的后验概率
在这里插入图片描述
• 通过比较所有类别的后验概率,选择概率最大的类别作为预测结果。
4.实际应用
朴素贝叶斯是一种简单而强大的概率生成模型,广泛应用于多个领域,尤其在文本分类、情感分析、垃圾邮件过滤等自然语言处理任务中表现出色,能快速准确地对文本进行分类和情感倾向判断;在医疗领域用于疾病预测和基因分析,辅助医生进行诊断;在金融领域用于信用评分和欺诈检测,帮助金融机构降低风险;还被用于推荐系统,根据用户行为数据提供个性化推荐,以及在图像识别中对物体进行分类,尽管其假设特征独立这一条件在实际中往往不完全成立,但其简单高效的特性使其在许多场景下仍然表现出色,为各领域提供了有效的解决方案。
五、代码实现
5.1 西瓜数据集介绍
数据集中共有 17 个样本,其中前 6 个为离散特征,后 2 个为连续特征,最后一个为分类标签,每个样本包含如下特征:
在这里插入图片描述
在这里插入图片描述
其中,以训练集中编号为 1 的样本为测试样本,其他16条样本为训练集:

编号 色泽 根蒂 敲声 纹理 脐部 触感 密度 含糖率 好瓜
8 乌黑 稍蜷 浊响 清晰 稍凹 硬滑 0.437 0.211

5.2 朴素贝叶斯分类器实现步骤
步骤1:数据读取与准备
我们使用 Python 的 pandas 库读取西瓜数据集。首先将前面整理好的 17 条样本构造成 DataFrame。

其中,编号为 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.437, 0.211, ]  # 待测集
    labels = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']  # 特征
    return dataSet, testSet, labels

步骤2:计算先验概率
先验概率是指训练集中各类别(好瓜或坏瓜)的出现频率:
在这里插入图片描述

根据训练集样本统计:

好瓜(是)共有 7 个样本 ,坏瓜(否)共有 9 个样本

在这里插入图片描述
代码实现为:

# 计算先验概率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

步骤3:计算条件概率
对于测试样本的每个特征,我们统计在训练集中,类别为“好瓜”或“坏瓜”条件下,各个特征值的出现频率。

例如:特征“敲声”为“浊响”
在这里插入图片描述

依此类推,计算其他所有特征的条件概率。

注意 若某个特征值在某一类中从未出现,则条件概率为 0,会导致后验概率为 0,因此我们可使用拉普拉斯平滑避免该问题。

具体代码实现:

# 计算(不同类别中指定连续特征的)均值、标准差
def mean_std(feature, cla):  # feature:传入指定将要计算其均值的标准差的特征名称,cla:计算指定分类cla下该特征的条件概率 feature/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(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)  # 计算条件概率
    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

步骤4:分类预测
根据朴素贝叶斯公式:
在这里插入图片描述
分别计算样本属于“好瓜”和“坏瓜”的后验概率:
最终分类:
根据计算结果,如果:
在这里插入图片描述
则判定为“好瓜”,否则为“坏瓜”。

# 预测后验概率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 "预测结果 : 坏瓜")
   

整体代码为:

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.437, 0.211, ]  # 待测集
    labels = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']  # 特征
    return dataSet, testSet, labels


# 计算先验概率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


# 计算(不同类别中指定连续特征的)均值、标准差
def mean_std(feature, cla):  # feature:传入指定将要计算其均值的标准差的特征名称,cla:计算指定分类cla下该特征的条件概率 feature/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(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)  # 计算条件概率
    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 "预测结果 : 坏瓜")

运行结果:

在这里插入图片描述

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐