vlambda博客
学习文章列表

Python手工实现朴素贝叶斯分类及预测

  • 朴素贝叶斯的简单介绍


朴素贝叶斯是基于1、最大后验概率和 2、特征条件独立假设 的分类方法,其分类原理是根据某对象的先验概率和类概率计算出其后验概率,然后选择具有最大后验概率的类作为该对象所属的类别。
朴素贝叶斯分类器的公式如下:
假设某样本X具有n项特征,分别F1,F2,...,Fn。有m个类别,分别为C1,C2,...Cm。贝叶斯分类器就是计算出样本X地后验概率最大的分类,即求下面这个公式的最大值。

由于等式右边的分母部分对于每个类别C1,C2,C3...都相同,可以省略。 所以贝叶斯分类的最大后验估计为求下面公公式的最大值:

为了模型简单理解,朴素贝叶斯假设所有的特征都彼此独立,因此才被称之为朴素(naive)。

等号右边P(Ci)表示该类的先验概率,P(F|Ci)是类条件概率,即似然概率,表面在类别Ci中特征F的可能性。
  • 例题及代码实现

下面例题为《人工智能数学基础》第八章的第一道习题

数据包含三种类别,分别是{感冒、过敏、脑震荡},训练数据如下表所示。预测一个打喷嚏的建筑工人诊断结果如何?
职业
症状
类别
护士
打喷嚏 感冒
农夫
打喷嚏 过敏
建筑工人
头痛
脑震荡
建筑工人 头痛 感冒
教师 打喷嚏 感冒
教师
头痛 脑震荡
首先来手算一下,根据病人是否感冒可将表格分为以下两部分
感冒的病人:
职业
症状
类别
护士
打喷嚏 感冒
建筑工人 头痛 感冒
教师 打喷嚏 感冒
非感冒的病人
职业
症状
类别
农夫
打喷嚏 过敏
建筑工人
头痛
脑震荡
教师
头痛 脑震荡

首先来手算一部分来加深对公式的理解:
(1)设 为感冒, 为过敏, 为脑震荡
则不知道任何信息情况下的先验概率为


(2)设特征 为"职业为建筑工人",则患感冒的人为建筑工人的似然概率
类似的,设特征 为"症状为打喷嚏",则感冒的人打喷嚏的似然概率
(3)此时,打喷嚏且为建筑工人的人患感冒的概率为 。类似的,可以算出脑震荡和过敏的后验概率。

python代码实现如下:

import pandas as pdimport numpy as np

def get_data(): '''获取数据,此处为直接输入,也可读取csv文件''' df = pd.DataFrame( [['护士', '打喷嚏', '感冒'], ['农夫', '打喷嚏', '过敏'], ['建筑工人', '头痛', '脑震荡'], ['建筑工人', '头痛', '感冒'], ['教师', '打喷嚏', '感冒'], ['教师', '头痛', '脑震荡']], columns=['职业', '症状', '类别']) return (df)

def test_data(): #获取测试数据,也可读取csv文件 df = pd.DataFrame([['建筑工人', '打喷嚏']], columns=['职业', '症状']) return df

class NBClassify(object): '''创建一个实现朴素贝斯模型的类''' def __init__(self): _tagProbablity = None #使用字典tagProbablity记录各类别的先验概率 _featuresProbablity = None #字典featureProbablity记录类别下各特征取值的条件概率
def train(self, df): '''传入训练数据df,计算条件概率''' # 计算每种类别的先验概率并输出 self._tagProbablity = dict(df['类别'].value_counts(normalize=True)) print('各类别的先验概率:\n', self._tagProbablity, '\n')
# 计算各特征及取值出现的次数,例如{'职业'{'护士':1, '农夫':1, ...} dictFeaturesBase = {}.fromkeys(df.columns) for column in df.columns: SeriesFeature = dict(df[column].value_counts()) dictFeaturesBase[column] = SeriesFeature del dictFeaturesBase['类别'] #删除类别信息 # print('各特征取值出现次数:\n', dictFeaturesBase)
# 初始化字典dictFeatures # 格式{'感冒':{'职业':{'建筑工人':0, '教师':0...},{'症状':...}}'脑震荡':...} dictFeatures = {}.fromkeys(df['类别']) #第一层字典{感冒:None,脑震荡:None...} for key in dictFeatures.keys(): dictFeatures[key] = {}.fromkeys( dictFeaturesBase) #第二层字典{'感冒': {'职业': None, '症状': None}...} for key, value in dictFeatures.items(): for subkey in value.keys(): value[subkey] = {}.fromkeys(dictFeaturesBase[subkey], 0) # 第三层字典,初始值设为0# print('dictFeature:\n', dictFeatures) #编写代码时方便理解和调试,可转换成代码执行
# 计算各类别,特征值及出现次数,存入字典dictFeatures中 for i in range(0, len(df)): label = df.iloc[i]['类别'] for feature in df.columns[:-1]: fvalue = df.iloc[i][feature] dictFeatures[label][feature][fvalue] += 1# print(dictFeatures)
#计算似然概率 for tag, featuresDict in dictFeatures.items(): for featureName, featureValueDict in featuresDict.items(): totalCount = sum(featureValueDict.values()) for featureKey, featureValues in featureValueDict.items(): featureValueDict[featureKey] = featureValues / totalCount self._featuresProbablity = dictFeatures print('每种特征的似然概率\n:', dictFeatures, '\n')
def predict(self, test_data): for i in range(0, len(test_data)): # 遍历测试数据 probability = self._tagProbablity # 先验概率 for key in probability: for column in test_data.columns: # 先验概率 * Π似然概率 probability[key] = probability[ key] * self._featuresProbablity[key][column][ test_data[column][i]] print(f'第{i+1}位患者的后验概率:', probability)
if __name__ == '__main__': '''执行主程序''' data = get_data() #获取训练数据 test_data = test_data() model = NBClassify() #定义朴素贝叶斯模型 model.train(data) model.predict(test_data)

执行结果为

各类别的先验概率: {'感冒': 0.5, '脑震荡': 0.3333333333333333, '过敏': 0.16666666666666666} 
每种特征的似然概率: {'感冒': {'职业': {'建筑工人': 0.3333333333333333, '教师': 0.3333333333333333, '护士': 0.3333333333333333, '农夫': 0.0}, '症状': {'打喷嚏': 0.6666666666666666, '头痛': 0.3333333333333333}}, '过敏': {'职业': {'建筑工人': 0.0, '教师': 0.0, '护士': 0.0, '农夫': 1.0}, '症状': {'打喷嚏': 1.0, '头痛': 0.0}}, '脑震荡': {'职业': {'建筑工人': 0.5, '教师': 0.5, '护士': 0.0, '农夫': 0.0}, '症状': {'打喷嚏': 0.0, '头痛': 1.0}}}
1位患者的后验概率: {'感冒': 0.1111111111111111, '脑震荡': 0.0, '过敏': 0.0}