贝叶斯与朴素贝叶斯分类器

来源:互联网 发布:软件质量保证措施方案 编辑:程序博客网 时间:2024/05/22 00:32

一.贝叶斯公式

概率论的一些基本知识:
条件概率:P(A|B)
联合概率:P(AB)=P(B)P(A|B)=P(A)P(B|A)
从而导出贝叶斯式:P(A|B)=P(AB)|P(B)=P(A)P(B|A)/P(B)
条件概率的链式法则:
P(A1,A2,,...,An)=P(An|A1,..,An1)P(An1|A1,A2,..,An2)...P(A2|A1)P(A1)
全概率公式:
P(B)=ni=1P(AiB)=ni=1P(Ai)P(B|Ai)
由以上公式可以导出完整的贝叶斯公式:
P(Ai|B)=P(B|Ai)P(Ai)ni=1P(Ai)P(B|Ai)

二.贝叶斯分类器

分类是机器学习和数据挖掘中最基础的一种工作。假设现在我们有一组训练样本,以及与其相对应的分类标签。每个元组都被表示为n维属性向量X=(x1,x1,...,xn)的形式,一共有k个类别C1,C2,...,Ck。分类要做的就是模型可以预测数据属于哪个类别。
对于每个类别Ci,利用贝叶斯公式来估计在给定训练元组X时的条件概率P(Ci|X)

P(Ci|X)=P(Ci)P(X|Ci)P(X)

当且仅当概率P(Ci|X)在所有类别中取值最大时,数据X属于Ci
P(Ci)是类先验概率,P(X|Ci)是样本X相对于类Ci的类条件概率,称为似然。因为P(X)是用于归一化的证据因子,其对于所有的类别都是恒定的。所以只需要基于训练数据来估计P(Ci)P(X|Ci)
对于类条件概率P(X|Ci)来说,由于它涉及关于X所有属性的联合概率,直接根据样本出现的频率来估计会遇到严重的困难,因为如果假设样本的d个属性都是二值的,则样本空间可能会有2d种可能性。在现实中,这个值往往远大于训练样本数m,也就是说很多样本的取值可能在训练样本中可能根本没有出现,由于未被观测到和没有出现是两个不同的事件,所以直接根据样本频率来估计P(X|Ci)是显然不可行的。

三.极大似然估计

估计类条件概率的一种常用策略是先假定其具有某种确定的概率分布形式,再根据训练样本对概率分布的参数进行估计。
记关于类别c的类条件概率为P(x|c),假设其具有某种概率分布形式并且被参数向量βc唯一确定,则我们就根据训练数据集来估计参数βc,我们将P(x|c)记为P(x|βc)
DC来表示训练集D中第c类样本组成的集合,假设这些样本是独立同分布的,则参数βc对于数据集DC的似然是:

P(Dc|βc)=xDcP(x|βc)

βc进行极大似然估计就是寻找能够最大化P(Dc|βc)的参数值βc。也就是在βc的所有可能的取值中,找到一个能够使数据出现的可能性最大的值。、
上式的连乘操作容易造成下溢,通常使用对数似然:
LL(βc)=logP(Dc|βc)=xDclogP(x|βc)

此时,参数βc的极大似然估计为:
βc=argβcmaxLL(βc)

需要注意的是这种参数估计的方法虽然能够使类条件概率估计变得简单,但是结果的准确性严重依赖于所假设的概率分布形式是否符合潜在的真实数据分布。在现实应用中,想要做出较好的接近潜在真实分布的假设,往往需在一定会程度上利用关于任务本身的经验知识,否则若仅凭猜测来假设概率分布形式,很可能产生误导性的结果。

三.朴素贝叶斯分类器

我们可以发现基于贝叶斯公式来计算后验概率P(C|X)的主要困难在于类条件概率P(X|C)是所有属性上的联合概率,难以从有限的训练样本中直接估计而得。
为了避开这个障碍,朴素贝叶斯分类器采用了属性条件独立性假设,即对于已知类别所有属性独立的对分类结果产生影响,也就是说所有属性都是条件独立的:

P(X|Ck)=i=1nP(Xi|Ck)=P(X1|Ck)P(X2|Ck)...P(Xn|Ck)

P(Ck|X)=P(Ck)ni=1P(Xi|Ck)P(X)

因为分母对于所有的类别都是一样的,所以我们的目标就是:
y=argminckP(Y=Ck)jP(X(j)i=xj|Y=Ck)

Xji表示样本Xi的第j个特征。朴素贝叶斯分类器的训练过程就是基于训练数据集来计算类先验概率P(Ci)和类条件概率。
需要注意的是,若某个属性值在训练集中没有与某个类同时出现过,则类条件概率会等于零,导致练乘计算出的概率值为零。为了避免这个问题,在计算概率值时要进行平滑处理,常用拉普拉斯修正,避免因训练样本不充足导致概率估值为零的问题。在训练集变大时,修正过程所引入的额先验的影响也会变得可忽略,使得估计值逐渐趋向于实际概率值。

四.朴素贝叶斯分类器的特点

  • 朴素贝叶斯分类器非常容易建立,并且能够很快作出决策。当有新增样本数据时,仅对新样本属性值所涉及的概率估值进行修正即可实现增量学习。
  • 在许多领域工作的很好。
  • 决策结果很容易解释
  • 在进行其他复杂的分类技术前可以先应用朴素贝叶斯分类。

五.使用朴素贝叶斯进行文档分类

python代码示例:

#导入数据集与其所对应的类别def loadDataSet():    postingList=[['my','dog','has','flea','problems','help','please'],                 ['maybe','not','take','him','to','dog','park','stupid'],                 ['my','dalmation','is','so','cute','I','love','him'],                 ['stop','posting','stupid','worthless','garbage'],                 ['mr','licks','ate','my','steak','how','to','stop','him'],                 ['quit','buying','worthless','dog','food','stupid']]    classVec=[0,1,0,1,0,1]    return postingList,classVec#根据数据集来创建一个包含在所有文档中出现的不重复词的列表def createVocabList(dataSet):    vocabSet=set([])    for document in dataSet:        vocabSet=vocabSet|set(document)    return list(vocabSet)#输出文档向量,向量的每一元素为0/1,表示词汇表中的单词在输入文档中是否出现def setofWords2Vec(vocabSet,inputSet):    returnVec=[0]*len(vocabSet)    for word in inputSet:        if word in vocabSet:            returnVec[vocabSet.index(word)]=1        else:            print "thw word %s is not in vocabSet" % word    return returnVec#词袋模型:与上述代码不同的是每当遇到一个单词时,会增加词向量中的对应值,而不是将对应的数值设为1def bagofWords2Vec(vocabSet,inputSet):    returnVec=[0]*len(vocabSet)    for word in inputSet:        if word in vocabSet:            returnVec[vocabSet.index(word)]+=1    return returnVec#构造分类器def trainNB0(trainMatrix,trainCategory):    N=len(trainMatrix)    M=len(trainMatrix[0])    pAbusive=sum(trainCategory)/float(N);    p0Num=ones(M); p1Num=ones(M)    p0Denom=2.0  ; p1Denom=2.0;    for i in range(N):        if trainCategory[i]==0:            p0Num+=trainMatrix[i]            p0Denom+=sum(trainMatrix[i])        if trainCategory[i]==1:            p1Num+=trainMatrix[i]            p1Denom+=sum(trainMatrix[i])    p0Vect=p0Num/p0Denom    p1Vect=p1Num/p1Denom    return p0Vect,p1Vect,pAbusive#对输入数据进行分类def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):    p1=sum(vec2Classify*p1Vec)+log(pClass1);    p0=sum(vec2Classify*p0Vec)+log(1-pClass1);    if p1>p0:        return 1;    else:        return 0;#遍历函数:封装所有的操作def testingNB():    listPosts,listClasses=loadDataSet()    myVocabList=createVocabList(listPosts)    trainMat=[]    for postinDoc in listPosts:        trainMat.append(setofWords2Vec(myVocabList,postinDoc))    p0V,p1V,pAb=trainNB0(trainMat,listClasses)    testEntry=['love','my','dalmation']    thisDoc=setofWords2Vec(myVocabList,testEntry)    print testEntry,' classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)    testEntry=['stupid','garbage']    thisDoc=array(setofWords2Vec(myVocabList,testEntry))    print testEntry,' classified as: ',classifyNB(thisDoc,p0V,p1V,pAb)#切分文本def textParse(bigString):    import re    regEx=re.compile('\\W*')    listOfTokens=regEx.split(bigString)    return [tok.lower() for tok in listOfTokens if len(tok)>2]#完整的垃圾邮件测试函数,输出测试错误率def spamTest():    docList=[]; classList=[];    for i in range(1,26):        wordList=textParse(open('email/spam/%d.txt' % i).read())        docList.append(wordList)        classList.append(1)        wordList=textParse(open('email/ham/%d.txt' % i).read())        docList.append(wordList)        classList.append(0)    myVocabList=createVocabList(docList)    trainingSet=range(50); testSet=[]    for i in range(10):        randomIndex=int(random.uniform(0,len(trainingSet)))        testSet.append(trainingSet[randomIndex])        del(trainingSet[randomIndex])    trainMat=[] trainClass=[]    for docIndex in trainingSet:        trainMat.append(bagofWords2Vec(myVocabList,docList[docIndex]))        trainClass.append(classList[docIndex])    p0,p1,pA=trainNB0(trainMat,trainClass)    errorCount=0;    for docIndex in testSet        wordVector=bagofWords2Vec(myVocabList,docList(docIndex))        if classifyNB(wordVector,p0,p1,pA)!=classList[docIndex]:            errorCount+=1    print 'the error rate is: ', float(errorCount)/len(testSet)