朴素贝叶斯分类应用——文档过滤

朴素贝叶斯公式及其推导回来补上,今天只上代码

一、提取文档特征
#提取文档特征,此处为对文档进行分词处理,每个单词即为文档的特征
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

从结果可以看出,设定阈值可以提升分类的门槛,在一些重要场合下可以降低误分类的风险。同时,随着训练的不断加深,超过阈值设定后的分类显得“更有底气”。

五、资源链接
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值