应一位同学要求,帮忙写个基于他们书本上的例子的朴素贝叶斯分类器,然后就有此成果了。不过内容也是相当朴素,看看就好。
这里的朴素贝叶斯如下:
P(H|X) = P(X|H)*P(H) / P(X)
而且这里针对的也只是分类属性(相当朴素)
数据变换规则如下:
age : youth==1, middle_aged=2, senior==3
income : low==1, medium==2, high==3
student : no==0, yes==1
credit_ratin : fair==0, excellent==1
buys_computer : no==0, yes==1
训练数据如下:
age income student credit_rating buys_computer
1 3 0 0 0
1 3 0 1 0
2 3 0 0 1
3 2 0 0 1
3 1 1 0 1
3 1 1 1 0
2 1 1 1 1
1 2 0 0 0
1 1 1 0 1
3 2 1 0 1
1 2 1 1 1
2 2 0 1 1
2 3 1 0 1
3 2 0 1 0
代码如下:
import re
def load_data(path):
'''
读取文件,载入训练数据
:param path: 训练数据路径
:return: 训练数据
labels是 数据属性,例如:age,income等,
training_data是 具体数据,格式像:[{'age':1,'income':1},{'age':2,'income':2}], 没有列出所有属性
'''
training_data = list()
with open(path, encoding='utf-8') as file:
data = file.read().split('\n')
labels = re.split(r'[\s]+', data[0])
for content in data[1:]:
parts = re.split(r'[\s]+', content.strip())
item = dict()
for index, part in enumerate(parts):
item[labels[index]] = part
training_data.append(item)
return labels, training_data
class Classifier:
'''
依据朴素贝叶斯分类公式P(H|X) = P(X|H)*P(H) / P(X)
做的一个分类器
'''
def __init__(self, classify_tag, decision=3, train_data_path='training_data.txt'):
'''
初始化分类器
:param decision: 计算过程中保留的小数位,应用场景:round(小数,保留的小数位个数)
:param train_data_path: 训练数据的路径
'''
self.labels, self.training_data = load_data(train_data_path)
self.labels.remove(classify_tag)
self.load_all_clazzs(classify_tag)
self.decision = decision
def load_all_clazzs(self, tag):
'''
读取所有的分类结果
:param tag: 用于分类的属性,这里是 buys_computer
:return: 所有的分类结果,例如:['0','1']
'''
self.clazz_tag = tag
self.clazz_values = set()
for item in self.training_data:
self.clazz_values.add(item[tag])
self.clazz_values = list(self.clazz_values)
def predict(self, item):
'''
预测输入数据的分类结果
:param item: 待预测对象
:param classify_tag: 分类属性,这里是'buys_computer'
:return: 分类结果, 例如:'1'
'''
PXs = list()
Ps = list()
# 统计每种类别对应的P(Ci),P(X|Ci)
for value in self.clazz_values:
rules = {self.clazz_tag: value}
p = self.get_P_on_base(rules)
Ps.append(p)
px = self.get_PX_on_base(item, rules)
PXs.append(px)
print(self.clazz_tag + ' :', self.clazz_values)
print('P(Ci) : ', Ps)
print('P(X|Ci) : ', PXs)
# 遍历所有的值,从而找到最大化的 P(Ci) * P(X|Ci)
target = [0, 0] # 格式为[位置下标,P(Ci) * P(X|Ci)的值]
for index, p in enumerate(Ps):
px = PXs[index]
p_px = round(p * px, self.decision)
if p_px > target[1]:
target[0] = index
target[1] = p_px
print('Max P(Ci)P(X|Ci) : ', target[1])
return self.clazz_values[target[0]]
def count(self, rules):
'''
计算在训练数据中,所有符合条件的元组个数
:param rules: 所有条件,数据结构为元组,格式例如:{'age':'1','buys_computer':'1'}
:return: 符合的个数
'''
# 通过遍历训练数据,再遍历条件,判断是否当前数据符合条件,符合(没有执行break)则进入else,计数+1
hit_num = 0
for item in self.training_data:
for key, value in rules.items():
if item[key] != value:
break
else:
hit_num += 1
return hit_num
def get_P_on_base(self, rules):
'''
计算先验概率,P(Ci)
:param rules:类别要求,格式例如:{'buys_computer':'1'}
:return:P(Ci)的值
'''
hit_num = self.count(rules)
P = round(hit_num / len(self.training_data), self.decision)
return P
def get_PX_on_base(self, item, rules):
'''
获取P(X|H)
:param item: 相当于X,格式例如:{'age':1,'income':1} ,此处没有列出所有属性
:param rules: 类别的要求,格式例如:{'buys_computer':'1'}
:return: P(X|H)的值
'''
base_num = self.count(rules)
PX_on_base = 1 # 后面是做P(X|Ci)的累乘,故初始化为 1
for label in self.labels:
# 获取所有的统计要求,例如:{'buys_computer':'1','age':'1'}
tmp_rule = {label: item[label]}
tmp_rule.update(rules)
hit_num = self.count(tmp_rule)
# 计算P(X|Ci)并且累乘进PX_on_base
PX_on_base = round(PX_on_base * hit_num / base_num, self.decision)
return PX_on_base
if __name__ == '__main__':
# 数据变换
# age : youth==1, middle_aged=2, senior==3
# income : low==1, medium==2, high==3
# student : no==0, yes==1
# credit_ratin : fair==0, excellent==1
# buys_computer : no==0, yes==1
# 待判断数据
item = {'age': '1', 'income': '2', 'student': '1', 'credit_rating': '0'}
# 分类的标签
clazz_tag = 'buys_computer'
classifier = Classifier(clazz_tag)
# 预测分类结果
ans = classifier.predict(item)
print('\nresult:')
print(clazz_tag + ':' + ans)
是不是有很多注释?完全不是我的风格啊,可是没办法,你懂得。
后续:
代码简单如此,拿来用是不可能的了。其中,据我粗略的了解了一下,还可以针对连续值属性的求概率做补充,对处理零概率事件使用拉普拉斯校准,等等。而且对于代码效率等问题上,完全没做啥子。有兴趣或许后面会改改吧。
啊啊,对于文中所有的概念,仅限于和我有一致共识前提的人能看懂,我不解释(毕竟我也不是很懂)。