应一位同学要求,帮忙写个基于他们书本上的例子的朴素贝叶斯分类器,然后就有此成果了。不过内容也是相当朴素,看看就好。
这里的朴素贝叶斯如下:
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)
是不是有很多注释?完全不是我的风格啊,可是没办法,你懂得。
后续:
代码简单如此,拿来用是不可能的了。其中,据我粗略的了解了一下,还可以针对连续值属性的求概率做补充,对处理零概率事件使用拉普拉斯校准,等等。而且对于代码效率等问题上,完全没做啥子。有兴趣或许后面会改改吧。
啊啊,对于文中所有的概念,仅限于和我有一致共识前提的人能看懂,我不解释(毕竟我也不是很懂)。