样本是10位马军头领, 10位步兵头领,现在设定如下:
已知有两个分类: A1=马军头领 A2=步军头领 两个用来分类的特征: F1=纹身 F2=闹事 特征可以如下取值: f11 = 有纹身 f12 = 无纹身 f21 = 爱闹事 f22 = 不爱闹事有了分类器模型和预制条件,下面就看如何推导出分类器模型参数了。
3. 训练过程和数据以下是根据已知数据统计得来。就是由实际数值训练出来的 分类器参数。
假定 马军头领中,2位有纹身,1位爱闹事,步兵头领中,7位有纹身,6位爱闹事。所以得到统计数据如下: P(有纹身) = P(f11) = (7+2)/20 = 9/20 = 0.45 P(无纹身) = P(f12) = 11/20 = 0.55 P(爱闹事) = P(f21) = 7/20 = 0.35 P(不爱闹事) = P(f22) = 13/20 = 0.65 P(F1=f11|A=A1) = P(有纹身|马军头领) = 2/20 = 0.1 P(F1=f12|A=A1) = P(无纹身|马军头领) = 8/20 = 0.4 P(F1=f11|A=A2) = P(有纹身|步兵头领) = 7/20 = 0.35 P(F1=f12|A=A2) = P(无纹身|步兵头领) = 3/20 = 0.15 P(F2=f21|A=A1) = P(爱闹事|马军头领) = 1/20 = 0.05 P(F2=f22|A=A1) = P(不爱闹事|马军头领) = 9/20 = 0.45 P(F2=f21|A=A2) = P(爱闹事|步兵头领) = 6/20 = 0.3 P(F2=f22|A=A2) = P(不爱闹事|步兵头领) = 4/20 = 0.2这样就训练(统计)出来了一个分类器模型的参数。
可以结合之前的分类器
P(A|F1,F2) = P(A) [P(F1|A)P(F2|A)] / P(F1,F2) = P(A) [P(F1|A)P(F2|A)] / [P(F1) P(F2)]来对 "待分类数据" 做处理了。
4. 如何分类如果有某位头领 x:不纹身,不闹事。进行针对两个分类(马军头领,步兵头领)进行两次运算,得出两个数值。
(不纹身,不闹事)是马军头领的可能性
P(马军头领|不纹身,不闹事) = P(马军头领) [P(无纹身|马军头领) P(不闹事|马军头领) ] / [P(无纹身)P(不闹事)] P(A=A1|x) = p(A=A1) P(F1=f12|A=A1)p(F2=f22|A=A1) / [P(f12)P(f22)] = 0.5 * 0.4 * 0.45 / [0.55 * 0.65] = 0.18 / [0.55 * 0.65] = 0.25(不纹身,不闹事)是步兵头领的可能性
P(步兵头领|不纹身,不闹事) = P(步兵头领) [P(无纹身|步兵头领) P(不闹事|步兵头领) ] / [P(无纹身)P(不闹事)] P(A=A2|x) = p(A=A2) P(F1=f12|A=A2)p(F2=f22|A=A2) / [P(f12)P(f22)] = 0.5 * 0.15 * 0.2 / [0.55 * 0.65] = 0.03 / [0.55 * 0.65] = 0.04所以x是马军的可能性更大。
贝叶斯定理最大的好处是可以用已知的频率去计算未知的概率,我们 简单地将频率当成了概率。
0X03 参考snowNLP的源码我们可以通过snowNLP的源码来对朴素贝叶斯模型再进一步理解。
在bayes对象中,有两个属性d和total,d是一个数据字典,total存储所有分类的总词数,经过train方法训练数据集后,d中存储的是每个分类标签的数据key为分类标签,value是一个AddOneProb对象。
这里的代码就是简单地将频率当成了概率。训练就是统计各个分类标签(key)所对应的个数。
1. 源码 #训练数据集 def train(self, data): #遍历数据集,data 中既包含正样本,也包含负样本 for d in data: # data中是list # d[0]:分词的结果,list # d[1]:标签-->分类类别,正/负样本的标记 c = d[1] #判断数据字典中是否有当前的标签 if c not in self.d: #如果没有该标签,加入标签,值是一个AddOneProb对象 self.d[c] = AddOneProb() # 类的初始化 #d[0]是评论的分词list,遍历分词list for word in d[0]: #调用AddOneProb中的add方法,添加单词 self.d[c].add(word, 1) #计算总词数,是正类和负类之和 self.total = sum(map(lambda x: self.d[x].getsum(), self.d.keys())) # # 取得所有的d中的sum之和 class AddOneProb(BaseProb): def __init__(self): self.d = {} self.total = 0.0 self.none = 1 #添加单词 def add(self, key, value): #更新该类别下的单词总数 self.total += value #如果单词未出现过,需新建key if not self.exists(key): #将单词加入对应标签的数据字典中,value设为1 self.d[key] = 1 #更新总词数 self.total += 1 #如果单词出现过,对该单词的value值加1 self.d[key] += value具体分类则是计算各个分类标签的概率
#贝叶斯分类 def classify(self, x): tmp = {} #遍历每个分类标签 for k in self.d: # 正类和负类 #获取每个分类标签下的总词数和所有标签总词数,求对数差相当于log(某标签下的总词数/所有标签总词数) tmp[k] = log(self.d[k].getsum()) - log(self.total) # 正类/负类的和的log函数-所有之和的log函数 for word in x: #获取每个单词出现的频率,log[(某标签下的总词数/所有标签总词数)*单词出现频率] tmp[k] += log(self.d[k].freq(word)) #计算概率 ret, prob = 0, 0 for k in self.d: now = 0 try: for otherk in self.d: now += exp(tmp[otherk]-tmp[k]) now = 1/now except OverflowError: now = 0 if now > prob: ret, prob = k, now return (ret, prob) 2. 源码推导公式