集体智慧编程_第六章文档过滤

本文档介绍了如何使用朴素贝叶斯方法进行文档分类,包括计算概率、训练分类器、贝叶斯定理的应用以及过滤博客订阅源。通过对单词特征的处理,建立分类器并使用概率模型来判断文档类别,同时还讨论了算法的优化和持久化存储。
摘要由CSDN通过智能技术生成

文档和单词

将单词作为特征。将任何非字母类字符为分隔符对文本进行划分,将文本拆分为一个个单词,并转化成小写形式。

import re
import math
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

fc将记录位于各分类中不同特征的数量:

{‘python’:{‘bad’:0,‘good’:6},‘the’:{‘bad’:3,‘good’:3}}

变量cc表示记录各分类被使用次数的字典。getfeatures为从即将被归类的内容项中提取出特征,在本例中就是我们定义过的getwords函数。

#增加对特征f、分类cat组合的计数值
def incf(self,f,cat):
    self.fc.setdefault(f,{})
    self.fc[f].setdefault(cat,0)
    self.fc[f][cat]+=1
#增加对某一分类的计数值
def incc(self,cat):
    self.cc.setdefault(cat,0)
    self.cc[cat]+=1
#某一特征出现在某一分类中的次数
def fcount(self,f,cat):
    if f in self.fc and cat in self.fc[f]:
        return float(self.fc[f][cat])
    return 0
#属于某一分类的内容项
def catcount(self,cat):
    if cat in self.cc:
        return float(self.cc[cat])
    return 0
#所有内容项
def totalcount(self):
    return sum(self.cc.values())
#所有分类的列表
def categories(self):
    return self.cc.keys()
    

Train方法接受一个内容项和一个分类作为参数。利用getfeatures函数,将内容项拆分为彼此独立的各个特征。利用incf函数,针对该分类为每个特征增加计数值。最后,函数会增加针对该分类的总计数值。

def train(self,item,cat):
    features=self.getfeatures(item)
    #features为拆分出来的单词
    for f in features:
        self.incf(f,cat)
    self.incc(cat)

之前的代码放置在docclass.py中。

import docclass
c1=docclass.classifier(docclass.getwords)
c1.train('the quick brown fox jumps over the lazy dog','good')
#代表上述单词分到good类
c1.train('quick','good')

计算概率

可以用一个单词在一篇属于某个分类的文档中出现的次数,除以该分类的文档总数,计算出单词在分类中出现的概率。

def fprob(self,f,cat):
    if self.catcount(cat)==0: return 0
    return self.fcount(f,cat)/self.catcount(cat)

fprob概率为条件概率,pr(word|classification),即对于给定的分类某个单词出现的概率。

c1.fprob('quick','good')

表示一篇good分类的文档中包含该单词的概率为,pr(quick|good)=0.666

从一个合理的推测开始

上述的算法,对那些极少出现的单词异常敏感。对上述算法进行改进。

def weightedprob(self,f,cat,prf,weight=1.0,ap=0.5):
    #计算当前概率值
    basicprob=prf(f,cat)
    #统计特征在所有分类中出现的次数
    totals=sum([self.fcount(f,c) for f in self.categories()])
    #计算加权平均
    bp=((weight*ap)+(totals*basicprob))/(weight+totals)
    return bp

朴素分类器

朴素表示将要被组合的概率是彼此独立的。

整片文档的概率

pr(python|bad)=0.2

pr(casino|bad)=0.8

pr(python&casino|bad)=0.16

若计算整片文档,曾江文档中出现的特征概率相乘即可。如下算法。

#新建一个classifier的子类
class naviebayes(classifier):
      def docprob(self,item,cat):
          features=self.getfeatures(item)
          #将所有特征概率相乘
          p=1
          for f in features:p*=self.weightedprob(f,cat,self,fprob)
          return p

目前我们已经知道如何计算pr(document|category),但我们需要给定一篇文档,它属于某个分类的概率是多少?
即pr(category|document),为此我们采用贝叶斯定理。

贝叶斯定理

pr(category|document)=pr(document|category)*pr(category)/pr(document)

def prob(self,item,cat):
    catprob=self.catcount(cat)/self.totalcount()
    docprob=self.docprob(item,cat)
    return catprob*docprob
c1.prob('quick rabbit','good')
c1.prob('quick rabbit','bad')

那个概率大,说明该短语更倾向于那个类。

选择分类

避免将普通邮件当做垃圾邮件。设定阈值。
假设过滤到bad的分类阈值为3,则针对bad分类的概率就必须至少3倍于针对good的概率才行。

def __init__(self,getfeatures):
    classifier.__init__(self,getfeatures)
    self.thresholds={}
def setthreshold(self,cat,t):
    self.thresholds[cat]=t
def getthreshold(self,cat):
    if cat not in self.thresholds: return 1
    return self.thresholds[cat]

将计算每个分类的概率,从中得到最大值,并将其余次大概率值进行对比,确定是否超过了阈值。

def classify(self,item,default=None):
    probs={}
    #寻找概率最大的分类
    max=0
    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.getthreshold(best)>probs[best]:return default
    return best

我们已经建立起一个完整的文档分类系统。

c1.setthreshold('bad',3.0)
c1.classify('quick money',default='unknown')

费舍尔方法

是前面介绍的朴素贝叶斯的一种替代方案

将经过训练的分类器持久化

本节中将classifier所用的字典,都替换成一个持久化的数据存储结构。请在classifier中添加一个方法,为该分类器打开数据库,执行建表操作。

from pysqlite2 import dbapi2 as sqlite
def setdb(self,dbfile):
    self.con=sqlite.connect(dbfile)
    self.con.execute('create table if not exists fc(feature,category,count)')
    self.con.execute('create table if not exists cc(category,count)')

如果我们打算将分类器移植到另个数据库上,需要修改:

#增加对特征f、分类cat组合的计数值
def incf(self,f,cat):
    count=self.fcount(f.cat)
    if count==0:
       self.con.execute("insert into fc values('%s','%s',1)" %(f,cat))
    else:
       self.con.execute("update fc set count='%d' where feature='%s' and category='%s'"   %(count+1,f,cat))
....

获取所用分类的列表与文档总数的方法也被替换掉。
最后添加一条提交语句:

self.con.commit()
c1.setdb('test1.db')

过滤博客订阅源

我们只希望阅读某个分类的文章。

import feedparser
import re
def read(feed,classifier):
    f=feedparser.parse(feed)
    for entry in f['entries']:
       print...
       print 'guess: '+str(classifier.classify(fulltext))
       #请求用户给出正确分类,并据此进行训练
       c1=raw_input('enter category: ')
       classifier.train(fulltext,c1)

对特征检测的改进

def entryfeatures(entry):
    splitter.re.compile('\\W*')
    f={}
    titlewords=[s.lower() for s in splitter.split(entry['title']) if len(s)>2 and len(s)<20]
    for w in titlewords:f['title :'+w]=1
    summarywords=[s.lower() for s in splitter.split(entry['summary']) if len(s)>2 and len(s)<20]
    #统计大写单词
    uc=0
    for i in range(len(summarywords)):
        w=summarywords[i]
        f[w]=1
        if w.isupper():uc+=1
        if i<len(summarywords)-1:
           twowords=' '.join(summarywords[i:i+1])
           f[twowords]=1
           ...

贝叶斯分类器经常被用于文档分类的原因是,它所要求的计算资源更少。但是神经网络和支持向量机有个很大的优势,可以捕捉输入特征之间更为复杂的关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值