朴素贝叶斯公式及其推导回来补上,今天只上代码
一、提取文档特征
#提取文档特征,此处为对文档进行分词处理,每个单词即为文档的特征
def getwords(doc):
splitter=re.compile('\W*')
words=[s.lower() for s in splitter.split(doc) if len(s)>2 and len(s)<20]
return dict([(w,1) for w in words])
二、 基本分类器设计
class classifier:
def __init__(self,getfeatures,filename=None):
self.fc={}#各个特征所属不同分类的频度字典
self.cc={}#各个分类的频度字典
self.getfeatures=getfeatures#获得提取特征函数
def incf(self,f,cat):
self.fc.setdefault(f,{})
self.fc[f].setdefault(cat,0)
self.fc[f][cat]+=1#特征f所属分类cat的频度加1
def incc(self,cat):
self.cc.setdefault(cat,0)
self.cc[cat]+=1#分类cat的频度加1
def fcount(self,f,cat):
if f in self.fc and cat in self.fc[f]:
return float(self.fc[f][cat])#返回特征f所属分类cat的频度
return 0.0
def catcount(self,cat):
if cat in self.cc:
return float(self.cc[cat])#返回分类cat的频度
return 0.0
def totalcount(self):
return sum(self.cc.values())#返回所有分类的频度总和
def categories(self):
return self.cc.keys()#用列表返回所有分类
def train(self,item,cat):
features=self.getfeatures(item)#分词提取特征
for f in features:
self.incf(f,cat)#对每个特征属于cat分类的频度加1
self.incc(cat)#cat分类的频度加1
def fprob(self,f,cat):
if self.catcount(cat)==0:
return 0
return self.fcount(f,cat)/self.catcount(cat)#计算在cat分类的条件下含有特征f的条件概率
#相当于对特殊情况(比如特征f属于分类cat的频度为0)计算条件概率进行的平滑处理
def weightedprob(self,f,cat,prf,weight=1.0,ap=0.5):
basicprob=prf(f,cat)
totals=sum(self.fc[f].values())
#totals=sum([self.fcount(f,c) for c in self.categories()])
bp=(totals*basicprob+weight*ap)/(weight+totals)
return bp
三、朴素贝叶斯分类器设计
class naivebayes(classifier):
def __init__(self,getfeatures):
classifier.__init__(self,getfeatures)
self.thresholds={}
#阈值,即最佳预测分类必须满足特定条件才能作为预测结果,不同的设定可影响预测的准确率
#比如在分类垃圾邮件的应用中就可提高分类为垃圾邮件的门槛,避免误将重要邮件分类为垃圾邮件造成损失
def setthresholds(self,cat,t):
self.thresholds[cat]=t#设定阈值
def getthresholds(self,cat):
if cat not in self.thresholds:
return 1.0
return self.thresholds[cat]#得到阈值
def classfy(self,item,default=None):
probs={}
max=0.0
best=default
for cat in self.categories():
probs[cat]=self.prob(item,cat)
if probs[cat]>max:#选择朴素贝叶斯分类器值最大的分类
max=probs[cat]
best=cat
for cat in probs:
if cat==best:
continue
#拿最佳分类和其他分类相比,如果前者的值除以后者小于最佳分类设定的阈值则返回为默认分类
if probs[cat]*self.getthresholds(best)>probs[best]:
return default
return best#满足阈值设定才返回作为预测结果
def docprob(self,item,cat):
features=self.getfeatures(item)
p=1
for f in features:
p*=self.weightedprob(f,cat,self.fprob)#基于朴素贝叶斯假设将特征概率全部乘起来
return p
def prob(self,item,cat):
catprob=self.catcount(cat)/self.totalcount()#分类cat的概率
docprob=self.docprob(item,cat)#在分类为cat的条件下含有item所有特征的概率
return docprob*catprob#返回朴素贝叶斯分类器值
附加:费舍尔方法
class fisherclassifier(classifier):
def __init__(self,getfeatures):
classifier.__init__(self,getfeatures)
self.minimums={}#阈值
def setminimum(self,cat,min):
self.minimums[cat]=min#设定阈值
def getminimum(self,cat):
if cat not in self.minimums:
return 0
return self.minimums[cat]#得到阈值
def classfy(self,item,default=None):
best=default
max=0.0
for c in self.categories():
p=self.fisherprob(item,c)
if p>self.getminimum(c) and p>max:
best=c#选择大于阈值设定的最佳分类
max=p
return best
def cprob(self,f,cat):
clf=self.fprob(f,cat)#f所属为cat分类的概率
if clf==0:
return 0
freqsum=sum([self.fprob(f,c) for c in self.categories()])#f所属为各个分类的概率总和
p=clf/freqsum#直接计算给定特征f属于分类cat的条件概率
return p
def fisherprob(self,item,cat):
p=1
features=self.getfeatures(item)
for f in features:
p*=self.weightedprob(f,cat,self.cprob)#item每个特征属于cat分类条件概率的乘积
fscore=-2*math.log(p)#取自然对数,并乘以-2
return self.invchi2(fscore,len(features)*2)#利用倒置对数卡方函数求得概率
#倒置对数卡方函数
#好吧,我知道你看到这里很困惑,我和你一样困惑,上网查了一些资料也没推导出个所以然
#但据说这种方法的预测准确率更高,就记录下来作为备用
#所以,现在跟我一起后悔没有本科选数学专业吧......
def invchi2(self,chi,df):
m=chi/2.0
sum=term=math.exp(-m)
for i in range(1,df//2):
term*=m/i
sum+=term
return min(sum,1.0)
四、测试
def sampletrain(cl):
cl.train('Nobody owns the water.','good')
cl.train('the quick rabbit jumps fences','good')
cl.train('buy pharmaceuticals now','bad')
cl.train('make quick money at the online casino','bad')
cl.train('the quick brown fox jumps','good')
cl_bayes=naivebayes(getwords)
sampletrain(cl_bayes)
print(cl_bayes.classfy('quick rabbit'))
print(cl_bayes.classfy('quick money'))
cl_bayes.setthresholds('bad',3.0)
for i in range(5):
sampletrain(cl_bayes)
print(i,cl_bayes.classfy('quick money',default='unknown'))
print(' ')
cl_fisher=fisherclassifier(getwords)
sampletrain(cl_fisher)
print(cl_fisher.classfy('quick rabbit'))
print(cl_fisher.classfy('quick money'))
cl_fisher.setminimum('bad',0.75)
for i in range(5):
sampletrain(cl_fisher)
print(i,cl_fisher.classfy('quick money',default='unknown'))
结果输出
good
bad
0 unknown
1 unknown
2 unknown
3 bad
4 bad
good
bad
0 good
1 good
2 bad
3 bad
4 bad
从结果可以看出,设定阈值可以提升分类的门槛,在一些重要场合下可以降低误分类的风险。同时,随着训练的不断加深,超过阈值设定后的分类显得“更有底气”。