2022年 11月 7日

朴素贝叶斯算法原理与Python实现

1 算法介绍

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。

最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBM)。和决策树模型相比,朴素贝叶斯分类器(Naive Bayes Classifier 或 NBC)发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率。同时,NBC模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。理论上,NBC模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为NBC模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,这给NBC模型的正确分类带来了一定影响。

朴素贝叶斯公式如下:

P(A_{i}|B) = \frac{P(B|A_{i})P(A_{i})}{\sum_{j}^{}P(B|A_{j})P(A_{j})}

这个是什么意思呢?该如何用呢?相信大家有一定的概率知识基础的同学,对先验概率及条件概率都比较清楚,在这里简单的说一下这条公式的含义:

1、首先P(A_{i}|B)代表的意思就是在B条件发生的情况下,Ai发生的概率

2、\sum{}_{j}^{}P(B|A_{j})P(A_{j})这公式代表的意思就是,在所有A类的结果中,B发生的概率之和,也就是B的概率

3、所以在这个公式,表达的意思就是(B条件发生的情况下,Ai发生的概率=Ai与B同时发生的概率占整个B发生的概率的多少)

 于是这个公式就可以转化为P(A_{i}|B) = \frac{P(B|A_{i})P(A_{i})}{P(B)} = \frac{P(A_{i}B)}{P(B)},(加上后面这个公式,是为了让我们更好的理解,实际上的使用,一般只用第一个等式的公式)

4、因此,由于P(B)是固定得,所以P(A_{i}|B)发生得概率与分子P(B|A_{i})P(A_{i})相关,也就是分子越大,概率越高

5、推导过程如下:

P(A_{i}|B) = \frac{P(A_{i}B) }{P(B)} = \frac{P(A_{i}B) }{\sum_{j}^{}P(A_{j}B)} = \frac{P(B|A_{i})P(A_{i})}{\sum_{j}^{}P(B|A_{j})P(A_{j})}

仔细品了一下这个公式,也确实发现其很适合用在分类的算法中;对于这个贝叶斯公式来说,就像是求B属于Ai的概率,运用在分类中,就得先求出B属于每一类中的可能,找出最大的,这就是朴素贝叶斯的算法思路。

由此得到朴素贝叶斯分类算法流程:

  1. 利用贝叶斯公式,求出预测得结果属于每一种类别得可能
  2. 找出最大值

2 案例介绍

有房 婚姻状况 拖欠贷款
单身 no
已婚 no
单身 no
已婚 no
离婚 yes
已婚 no
离婚 no
单身 yes
已婚 no
单身 yes
已婚 no
已婚 no
已婚 yes

基于上面的数据,希望预测没房、单身的人,会不会拖欠贷款??? 

代码流程:

  1. 先将样本数据处理成字典,并统计每一类发生得个数(第一个函数)
  2. 将待预测得样本写入,并根据之前得样本数据,对分类结果进行求概率,得出最大概率得分类结果

代码如下: 

  1. import pandas as pd
  2. def handle_data(data):
  3. """
  4. 将数据处理成字典,用于保存样本数据中的类别数据存储情况
  5. :param data: dataframe 数据源
  6. :return:样本数据中的类别数据字典,分类结果字典
  7. """
  8. # 初始化类别数据字典
  9. cate_dict = {}
  10. # 数据集表头列表(各个条件及分类结果)
  11. header_list = data.columns.tolist()
  12. # 条件列表
  13. factor_list = header_list[:-1]
  14. # 分类结果所在位置
  15. k = len(header_list) - 1
  16. # result_dict 为分类的结果类型字典
  17. result_dict = dict(data.iloc[:, k].value_counts())
  18. # 或使用如下语句:
  19. # result_dict = dict(data.iloc[:, -1].value_counts())
  20. result_dict_key = result_dict.keys()
  21. # 将每个分类结果写入 cate_dict
  22. # 循环各个分类结果
  23. for result_key in result_dict_key:
  24. # 如果类别数据字典不存在该分类结果,默认设置空字典
  25. if result_key not in cate_dict:
  26. # dict.setdefault(key, default=None) 键不存在于字典中,将会添加键并将值设为默认值
  27. cate_dict.setdefault(result_key, {})
  28. # 在该分类结果下,循环各个条件(因素)
  29. for factor in factor_list:
  30. # 如果该分类结果字典不存在该条件(因素),默认设置空字典
  31. if factor not in cate_dict[result_key]:
  32. cate_dict[result_key].setdefault(factor, {})
  33. # 获取该条件的分类列表
  34. factor_key_list = data[factor].value_counts().index.tolist()
  35. # 循环获取该条件的各个分类数量
  36. for key in factor_key_list:
  37. # 获取该分类结果下,该因素中某个分类的数量
  38. number = data[(data[header_list[k]] == result_key) & (data[factor] == key)].shape[0]
  39. if key not in cate_dict[result_key][factor]:
  40. cate_dict[result_key][factor].setdefault(key, number)
  41. return cate_dict, result_dict
  42. def calculate(cate_dict, result_dict, new_data):
  43. """
  44. 对每个待预测得结果进行贝叶斯公式得计算,并得出预测类别与概率
  45. :param cate_dict: 样本数据中的类别数据字典
  46. :param result_dict: 分类结果字典
  47. :param new_data: 待预测的数据集
  48. :return: 预测结果列表
  49. """
  50. # 获取数据集的各个条件(因素)列表
  51. factor_list = new_data.columns.tolist()
  52. # 初始化预测结果列表
  53. result_list = []
  54. # 分类结果列表
  55. result_key_list = cate_dict.keys()
  56. # 循环预测新数据
  57. for i in range(len(new_data)):
  58. new_result_dict = {}
  59. # 循环计算各个分类指标的概率
  60. for result_key in result_key_list:
  61. # 该分类结果在所有分类结果中的占比
  62. all_ratio = result_dict[result_key] / sum(list(result_dict.values()))
  63. # 循环获取该分类结果下,该因素中各个 分类 在 该分类结果 中的占比
  64. for factor in factor_list:
  65. ratio = cate_dict[result_key][factor][new_data.iloc[i, factor_list.index(factor)]] / result_dict[result_key]
  66. # 总占比 乘以 该因素下的各个分类占比
  67. all_ratio *= ratio
  68. new_result_dict.setdefault(result_key, all_ratio)
  69. print(new_result_dict)
  70. # 获取占比最大的分类结果
  71. max_result_key = max(new_result_dict, key=new_result_dict.get)
  72. # 获取占比最大的分类结果的占比
  73. max_value = new_result_dict[max_result_key]
  74. result_list.append([max_result_key, max_value])
  75. return result_list
  76. if __name__ == '__main__':
  77. file_path = "./朴素贝叶斯数据集.xlsx"
  78. data = pd.read_excel(file_path)
  79. print("数据源\n", data)
  80. # 待预测数据
  81. new_data = pd.DataFrame({"有房": "是", "婚姻状况": "已婚"}, index=[0])
  82. cate_dict, result_dict = handle_data(data)
  83. print(cate_dict)
  84. print(result_dict)
  85. result = calculate(cate_dict, result_dict, new_data)
  86. print(result)

运行结果:

  1. 数据源
  2. 有房 婚姻状况 拖欠贷款
  3. 0 是 单身 no
  4. 1 否 已婚 no
  5. 2 否 单身 no
  6. 3 是 已婚 no
  7. 4 否 离婚 yes
  8. 5 否 已婚 no
  9. 6 是 离婚 no
  10. 7 否 单身 yes
  11. 8 否 已婚 no
  12. 9 否 单身 yes
  13. 10 是 已婚 no
  14. 11 是 已婚 no
  15. 12 是 已婚 yes
  16. {'no': {'有房': {'否': 4, '是': 5}, '婚姻状况': {'已婚': 6, '单身': 2, '离婚': 1}}, 'yes': {'有房': {'否': 3, '是': 1}, '婚姻状况': {'已婚': 1, '单身': 2, '离婚': 1}}}
  17. {'no': 9, 'yes': 4}
  18. {'no': 0.2564102564102564, 'yes': 0.019230769230769232}
  19. [['no', 0.2564102564102564]]

所以得出结果为NO!