当前位置: 首页 > article >正文

机器学习:朴素贝叶斯分类器

贝叶斯决策论是概率框架下实施决策的基本方法,对分类任务来说,在所有相关概率都已知的理想情形下,贝叶斯决策论考虑如何基于这些概率和误判损失来选择最优的类别标记。

贝叶斯定理是贝叶斯决策论的基础,描述了如何根据新的证据更新先验概率,贝叶斯定理
P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A|B) = \frac {P(B|A) P(A)} {P(B)} P(AB)=P(B)P(BA)P(A)

后验概率P(A|B)在观测B的条件下A的概率指在观测到数据之后,对某个假设的概率估计。它是通过贝叶斯定理计算得到的,结合了先验概率和似然概率。(事情已经发生,求这件事情发生的原因是由某个因素引起的可能性的大小。
似然概率P(B|A)在A发生的条件下B的概率指在某个假设下,观测数据的概率。它描述了在某个假设下,数据出现的可能性
先验概率P(A)没有观测到B的条件下A的概率指在没有观测到任何数据之前,对某个事件的概率估计。它通常基于先验知识或经验。(根据以往经验或经过数据统计得到的概率。
边际概率P(B)B的总概率

贝叶斯决策论通过结合先验知识和观测数据,使用贝叶斯定理计算后验概率,从而做出最优决策。即:
P ( c ∣ x ) = P ( c ) P ( x ∣ c ) P ( x ) P(c|x) = \frac {P(c) P(x|c)} {P(x)} P(cx)=P(x)P(c)P(xc)
P©是样本空间中各类样本所占的比例,根据大数定理,当训练集包含充足的独立同分布样本时,P©可以通过各类样本所占的比例来进行估计。

但是对于类条件概率P(x|c)来说,涉及了关于x所有属性的联合概率,因此很难进行估计。

例如:每个样本具有d个属性,每个属性都有10个属性值,那么样本空间将有 1 0 d 10^d 10d种可能值,导致组合爆炸

朴素贝叶斯对条件概率分布做了条件独立性假设

即,现在有 10 × d 10 \times d 10×d种可能值。

P ( x ∣ c ) = ∏ j = 1 n P ( x j ∣ c ) P(x|c) = \prod_{j=1}^{n}P(x^j|c) P(xc)=j=1nP(xjc)

先验概率:
P ( c k ) = ∑ i N I ( y i = c k ) N P(c_k) = \frac {\sum_i^NI(y_i = c_k)} {N} P(ck)=NiNI(yi=ck)
属性值是离散情况下,条件概率为:
P ( x j = a j l ∣ y = c k ) = ∑ i = 1 N I ( x i ( j ) = a j l , y i = c k ) ∑ i = 1 N I ( y i = c k ) P(x^j = a_{jl}|y = c_k) = \frac {\sum_{i=1}^NI(x_i^{(j)} = a_{jl}, y_i=c_k)} {\sum_{i=1}^{N}I(y_i=c_k)} P(xj=ajly=ck)=i=1NI(yi=ck)i=1NI(xi(j)=ajl,yi=ck)
属性值是连续情况下,条件概率为:

假设概率密度P(x|c)服从正太分布 N ( μ , σ 2 ) N(\mu, \sigma^2) N(μ,σ2),通过极大似然估计得到的正态分布均值就是样本均值,方差就是 ( x − μ c ^ ) ( x − μ c ^ ) T (x - \hat{\mu_c})(x - \hat{\mu_c})^T (xμc^)(xμc^)T

这样假设使其问题变得简单,但是估计准确性严重依赖所假设的概率分布形式是否符合潜在的真实数据分布。不过准确性却较高。

例如:

在这里插入图片描述

代码示例:

数据处理:将DataFrame转为numpy.array类型,并自定义分出训练集和测试集用来检验正确性。

import pandas as pd
import numpy as np
from io import StringIO

data = '编号,色泽,根蒂,敲声,纹理,脐部,触感,密度,含糖率,好瓜\n\
1,青绿,蜷缩,浊响,清晰,凹陷,硬滑,0.697,0.46,是\n\
2,乌黑,蜷缩,沉闷,清晰,凹陷,硬滑,0.774,0.376,是\n\
3,乌黑,蜷缩,浊响,清晰,凹陷,硬滑,0.634,0.264,是\n\
4,青绿,蜷缩,沉闷,清晰,凹陷,硬滑,0.608,0.318,是\n\
5,浅白,蜷缩,浊响,清晰,凹陷,硬滑,0.556,0.215,是\n\
6,青绿,稍蜷,浊响,清晰,稍凹,软粘,0.403,0.237,是\n\
7,乌黑,稍蜷,浊响,稍糊,稍凹,软粘,0.481,0.149,是\n\
8,乌黑,稍蜷,浊响,清晰,稍凹,硬滑,0.437,0.211,是\n\
9,乌黑,稍蜷,沉闷,稍糊,稍凹,硬滑,0.666,0.091,否\n\
10,青绿,硬挺,清脆,清晰,平坦,软粘,0.243,0.267,否\n\
11,浅白,硬挺,清脆,模糊,平坦,硬滑,0.245,0.057,否\n\
12,浅白,蜷缩,浊响,模糊,平坦,软粘,0.343,0.099,否\n\
13,青绿,稍蜷,浊响,稍糊,凹陷,硬滑,0.639,0.161,否\n\
14,浅白,稍蜷,沉闷,稍糊,凹陷,硬滑,0.657,0.198,否\n\
15,乌黑,稍蜷,浊响,清晰,稍凹,软粘,0.36,0.37,否\n\
16,浅白,蜷缩,浊响,模糊,平坦,硬滑,0.593,0.042,否\n\
17,青绿,蜷缩,沉闷,稍糊,稍凹,硬滑,0.719,0.103,否'

df = pd.read_csv(StringIO(data))
# print(df.info())

def obj_to_int(series: pd.Series):
    return pd.Categorical(series).codes

def label_encoder(df: pd.DataFrame):
    for col in df.columns:
        if df[col].dtype == 'object':
            df[col] = obj_to_int(df[col])
    return df

# 已完成:2025年2月9日 14点27分
# # 保存处理后的数据
# df = label_encoder(df)
# df.to_csv('data.csv', index=False)

# # 已完成:2025年2月9日 14点30分
# # 随机选择三行作为测试集
# test_set = df.sample(n=3, random_state=42)
# # 获取剩余的行作为训练集
# train_set = df.drop(test_set.index)
# # 保存训练集和测试集
# train_set.to_csv('train.csv', index=False)
# test_set.to_csv('test.csv', index=False)

def get_train_data():
    df = pd.read_csv('train.csv')
    return np.array(df.iloc[:, :])

def get_test_data():
    # 2025年2月9日 16点19分
    df = pd.read_csv('test.csv')
    return np.array(df.iloc[0:, :])

    # 随机选择一半的数据作为训练集
    # df = pd.read_csv('data.csv')
    # bool_array = np.random.choice([True, False], size=len(df), p=[0.5, 0.5])
    # return np.array(df[bool_array].iloc[:, :])
# print(get_train_data())
# print(get_test_data())

朴素贝叶斯

  • 计算先验概率

    def get_prior_prob(train_data: np.array):
        # 先验概率
        prior_prob = {}
        # 计算先验概率
        for i in range(len(train_data)):
            if train_data[i][-1] not in prior_prob:
                prior_prob[int(train_data[i][-1])] = 1
            else:
                prior_prob[int(train_data[i][-1])] += 1
        for key in prior_prob:
            prior_prob[key] /= len(train_data)
        return prior_prob
    
  • 计算条件概率

    • 离散值

      n = len(train_data[0]) - 1
          cond_prob = [ {} for i in range(n) ]
          # 计算条件概率
          # (特征值, 类别) -> 出现次数
          fea_res_cnt = [[0,0] for i in range(n)]
          for i in range(len(train_data)):
              for j in range(1, n):
                  if int(train_data[i][-1]) == 0:
                      fea_res_cnt[j][0] += 1
                  else:  
                      fea_res_cnt[j][1] += 1
          
          for i in range(len(train_data)):
              # 仅计算特征值为离散值的条件概率
              for j in range(1, n - 2):
                  fea = int(train_data[i][j])
                  res = int(train_data[i][-1])
                  if (fea, res) not in cond_prob[j]:
                      cond_prob[j][(fea, res)] = 1
                  else:
                      cond_prob[j][(fea, res)] += 1
      
    • 连续值:使用极大似然法球的均值、标准差,进行正太分布

      # 计算特征值为连续值的条件概率
          reslist = [ [ [] for i in range(2)] for j in range(2)]
          for i in range(len(train_data)):
              for j in range(n - 2, n):
                  res = int(train_data[i][-1])
                  reslist[j - n + 2][res].append(float(train_data[i][j]))
          for i in range(2):
              for j in range(2):
                  mean,std = np.mean(reslist[i][j]),np.std(reslist[i][j])
                  cond_prob[i + n - 2][(j, mean, std)] = 0
      
      

    总的这个函数代码:

    def get_cond_prob(train_data: np.array):
        n = len(train_data[0]) - 1
        cond_prob = [ {} for i in range(n) ]
        # 计算条件概率
        # (特征值, 类别) -> 出现次数
        fea_res_cnt = [[0,0] for i in range(n)]
        for i in range(len(train_data)):
            for j in range(1, n):
                if int(train_data[i][-1]) == 0:
                    fea_res_cnt[j][0] += 1
                else:  
                    fea_res_cnt[j][1] += 1
        
        for i in range(len(train_data)):
            # 仅计算特征值为离散值的条件概率
            for j in range(1, n - 2):
                fea = int(train_data[i][j])
                res = int(train_data[i][-1])
                if (fea, res) not in cond_prob[j]:
                    cond_prob[j][(fea, res)] = 1
                else:
                    cond_prob[j][(fea, res)] += 1
    
        # 计算特征值为连续值的条件概率
        reslist = [ [ [] for i in range(2)] for j in range(2)]
        for i in range(len(train_data)):
            for j in range(n - 2, n):
                res = int(train_data[i][-1])
                reslist[j - n + 2][res].append(float(train_data[i][j]))
        for i in range(2):
            for j in range(2):
                mean,std = np.mean(reslist[i][j]),np.std(reslist[i][j])
                cond_prob[i + n - 2][(j, mean, std)] = 0
    
        # 计算条件概率
        for i in range(1, n - 2):
            for key in cond_prob[i]:
                cond_prob[i][key] /= fea_res_cnt[i][key[1]]
        return cond_prob
    
    

在这里插入图片描述

  • 进行测试

    # 正太分布密度概率
    def probability_density_function(mean:float, std:float, var:float) -> float:
        return (1/(std * np.sqrt(2 * np.pi))) * np.
    
    
    
exp(-0.5 * ((var - mean)/std)**2)
  
  def test():
      cond_prob = get_cond_prob(get_train_data())
      prior_prob = get_prior_prob(get_train_data())
      test_data = get_test_data()
      # 预测
      right_cnt = 0
      for i in range(len(test_data)):
          good = bad = 1
          good = prior_prob[1]
          bad = prior_prob[0]
          for j in range(len(cond_prob)):
              for key in cond_prob[j]:
                  if len(key) == 2:
                      if key[1] == 0:
                          bad *= cond_prob[j][(int(test_data[i][j]), key[1])]
                      else:
                          good *= cond_prob[j].get((int(test_data[i][j]), key[1]), 0) # 有可能出现未知的特征值
                  elif len(key) == 3:
                      if key[0] == 0:
                          bad *= probability_density_function(key[1], key[2], float(test_data[i][j]))
                      else:
                          good *= probability_density_function(key[1], key[2], float(test_data[i][j]))
          if good > bad:
              print('good')
              if int(test_data[i][-1]) == 1:
                  print('right')
                  right_cnt += 1
          else:
              print('bad')
              if int(test_data[i][-1]) == 0:
                  print('right')
                  right_cnt += 1
          
      print(f'accuracy: {right_cnt / len(test_data)}')
  

在这里插入图片描述

朴素贝叶斯(Naive Bayes)算法理论与实践 - 简书 (jianshu.com)


http://www.kler.cn/a/539076.html

相关文章:

  • 云计算——AWS Solutions Architect – Associate(saa)4.安全组和NACL
  • 深入探究 Rust 测试:灵活控制测试的执行方式
  • Vue全流程--Vue2路由
  • 微信小程序案例2——天气微信小程序(学会绑定数据)
  • ffmpeg -formats
  • Linux中设置开机运行指令
  • BMS应用软件开发 — 11 CAN通讯
  • MongoDB开发规范
  • 青少年编程与数学 02-009 Django 5 Web 编程 03课题、项目结构
  • Puck.js,一款基于React的开源可视化编辑器
  • 内存的RANK具体指什么?
  • ML.NET库学习004:ML.NET基础知识复盘
  • 202406 青少年软件编程等级考试C/C++ 三级真题答案及解析(电子学会)
  • 测试文章内容1
  • 如何写出优秀的单元测试?
  • 跟着李沐老师学习深度学习(二)
  • Jetbrains IDE http客户端使用教程
  • Unet 改进:引入残差模块ResidualBlock
  • 《qt easy3d中添加Delaunay-2d重建》
  • 使用Python进行数据采集与解析!
  • 小结:NAT
  • YouBIP 项目
  • C# 比较两个List集合内容是否相同
  • 国产编辑器EverEdit - 查找下一个单词与查找上一个单词
  • 软件工程-分析建模
  • DeepSeek 提示工程:解锁 AI 对话的六维超能力