本文介绍如何使用Python实现一个简易的朴素贝叶斯分类器(Naive Baves classifier)。
贝叶斯公式
我们先简单回顾一下贝叶斯公式:
其中,我们称P(A)和P(B)为先验概率,P(A|B)和P(B|A)为后验概率。
上诉公式可以直接从条件概率公式推导出。根据条件概率的定义,在事件A发生的条件下事件B发生的概率是:
其中,两边同乘P(A),可得:
同理,在事件B发生的条件下事件A发生的概率是:
将用替换,即可得到贝叶斯公式。
朴素贝叶斯分类器的公式
假设某个体有n项特征,分别为。现有m个分类,分别为。贝叶斯分类器就是计算出概率最大的那个分类,也就是求下面这个算式的最大值:
其中,。
由于对于所有的类别都是相同的,可以省略,问题就变成了求,
朴素贝叶斯假定所有的特征值相互独立,因此:
上式等号右边的每一项,都可以从统计资料中得到,由此就可以计算出每个类别对应的概率,从而找出最大概率的那个类。
虽然"所有特征彼此独立"这个假设,在现实中不太可能成立,但是它可以大大简化计算,而且有研究表明对分类结果的准确性影响不大。
水果分类小例子
在这个例子中,我们将对水果进行分类,我们考虑水果的如下三个特征:
[('较长','不长'),('甜','不甜'),('黄色','不是黄色')]
现在我们有1000个水果的上诉特征数据,如下:
类别 | 较长 | 不长 | 甜 | 不甜 | 黄色 | 不是黄色 | 总数 |
---|---|---|---|---|---|---|---|
香蕉 | 400 | 100 | 350 | 150 | 450 | 50 | 500 |
橘子 | 0 | 300 | 150 | 150 | 300 | 0 | 300 |
其他水果 | 100 | 100 | 150 | 50 | 50 | 150 | 200 |
总数 | 500 | 500 | 650 | 350 | 800 | 200 | 1000 |
根据贝叶斯分类器公式,我们需要做的是,在知道上诉特征的情况下,判断这个水果是香蕉、橘子还是其他,写成公式,如下:
根据上诉推导,我们只需求如下等式的最大值,
其中,F->水果, L->长度, S->甜度,C->颜色
根据样本数据,有:
假设,我们有一个水果,它有如下特征,较长,不甜,黄色,请问它最有可能是什么水果?
可见,该水果最有可能是香蕉。
Python 实现
import random
import operator
datasets = {
'banala':{'long':400,'not_long':100,'sweet':350,'not_sweet':150,'yellow':450,'not_yellow':50},
'orange':{'long':0,'not_long':300,'sweet':150,'not_sweet':150,'yellow':300,'not_yellow':0},
'other_fruit':{'long':100,'not_long':100,'sweet':150,'not_sweet':50,'yellow':50,'not_yellow':150}
}
def count_total(data:dict):
# return ({'banala': 500, 'orange': 300, 'other_fruit':200},1000)
count= dict()
total = 0
for fruit in data:
count[fruit] = data[fruit]['sweet'] + data[fruit]['not_sweet']
total += count[fruit]
return count, total
def cal_base_rates(data:dict):
# 计算 P(香蕉) P(橘子) P(其他)
categroies,total = count_total(data)
cal_base_rates = dict()
for label in categroies:
priori_prob=categroies[label]/total
cal_base_rates[label] = priori_prob
return cal_base_rates
def likelihold_prod(data:dict):
# 计算 P(较长|香蕉) P(甜|香蕉) P(黄色|香蕉)...
count, _ = count_total(data)
likelihold = dict()
for fruit in data:
attr_prob={}
for attr in data[fruit]:
attr_prob[attr] = data[fruit][attr]/count[fruit]
likelihold[fruit] = attr_prob
return likelihold
class navie_bayes_classifier:
def __init__(self, data = datasets):
self._data = data
self._labels = [key for key in self._data.keys()]
self._priori_prob = cal_base_rates(self._data)
self._likelihold_prob = likelihold_prod(self._data)
def get_label(self, length, sweetness, color):
self._attrs = [length, sweetness, color]
res = dict()
# 计算 P(较长∣香蕉)P(甜∣香蕉)P(黄色∣香蕉)P(香蕉)...
for label in self._labels:
prob = self._priori_prob[label]
for attr in self._attrs:
prob*=self._likelihold_prob[label][attr]
res[label] = prob
return res
def random_attr(pair):
return pair[random.randint(0,1)]
def gen_attrs():
# 生成测试数据集
sets= [('long','not_long'),('sweet','not_sweet'),('yellow','not_yellow')]
test_datasets = []
for _ in range(20):
test_datasets.append(list(map(random_attr,sets)))
return test_datasets
def main():
test_datasets = gen_attrs()
classfier = navie_bayes_classifier()
for data in test_datasets:
print('特征值: ', end='\t')
print(data)
print('预测结果: ' ,end= '\t')
res= classfier.get_label(*data)
print(res)
print(sorted(res.items(),key=operator.itemgetter(1),reverse=True)[0][0])
if __name__ == "__main__":
main()
参考文献
网友评论