4.1基于贝叶斯决策理论的分类方法

优点:在数据较少的情况下任然有效,可以处理多类别问题。
缺点:对于输入数据的准备方式较为敏感 。
适用数据类型:标称型数据。

    贝叶斯决策理论的核心思想:选择具有最高概率的决策。

4.2条件概率

    贝叶斯准备告诉我们如何交换条件概率中的条件与结果,即如果已知$p(x\mid c)$,要求$p(c\mid x)$,那么可以使用下面的计算方法:

$$ p(c\mid x)=\frac{p(x\mid c)p(c)}{p(x)} $$

4.3使用条件概率来分类

    贝叶斯分类准则:
    如果$p(c_{1}\mid x,y)>p(c_{2}\mid x,y)$,那么属于类别$c_{1}$。
    如果$p(c_{1}\mid x,y)<p(c_{2}\mid x,y)$,那么属于类别$c_{2}$。

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

    朴素贝叶斯的一般过程:
    1、收集数据:可以使用任何方法。本章使用RSS源。
    2、准备数据:需要数值型或者布尔型数据。
    3、分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好。
    4、训练算法:计算不同的独立特征的条件概率。
    5、测试算法:计算错误率。
    6、使用算法:一个常见的朴素贝叶斯应用是文档分类。可以在任何的分类场景中使用朴素贝叶斯分类器,不一定非要是文本。

4.5使用Python进行文本分类

4.5.1准备数据:从文本中构建词向量

    我们将把文本看成单词向量或者词条向量,也就是说将句子转换为向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。
创建一个叫bayes.py的新文件,添加代码:

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]  # 1 is abusive, 0 not
    return postingList, classVec


def createVocabList(dataSet):
    vocabSet = set([])  # create empty set
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # union of the two sets
    return list(vocabSet)


def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else:
            print("the word: %s is not in my Vocabulary!" % word)
    return returnVec

测试代码:

import bayes

listOPosts,listClasses = bayes.loadDataSet()
myVocabList = bayes.createVocabList(listOPosts)
print(myVocabList)
vec = bayes.setOfWords2Vec(myVocabList,listOPosts[0])#返回的是词表list的第i个小list里面有没有前面的这些词
print(vec)

结果:
在这里插入图片描述

4.5.2训练算法:从词向量计算概率

    使用公式 $p(c\mid w)=\frac{p(x\mid c)p(c)}{p(w)}$对每个类计算该值,然后比较这两个概率值的大小($w$表示一个向量)。
    首先可以通过类别 $i$ (侮辱性留言或非侮辱性留言)中文档数除以总的文档数来计算概率$p(c_{i})$。接下来计算$p(w\mid c_{i})$,这里就要用到朴素贝叶斯假设。这里假设所有词都相互独立,该假设也称作条件独立性假设。
在bayes.py中添加代码:

def trainNB0(trainMatrix,trainCategory):
    numTrainDocs=len(trainMatrix)                   #训练文档数目
    numWords=len(trainMatrix[0])                    #每篇文档词条数
    pAbusive=sum(trainCategory)/float(numTrainDocs) #属于侮辱类文档的概率
    p0Num=np.zeros(numWords)                        #词条出现向量初始化为0
    p1Num=np.zeros(numWords)
    p0Denom=0.0                                     #分母
    p1Denom=0.0
    for i in range(numTrainDocs):
        if trainCategory[i]==1:
            p1Num+=trainMatrix[i]                   #向量相加
            p1Denom+=sum(trainMatrix[i])            #统计侮辱类条件概率p(w1|c1),p(w2|c1)……
        else:
            p0Num+=trainMatrix[i]                   #统计非侮辱类条件概率p(w1|c0),p(w2|c0)……
            p0Denom+=sum(trainMatrix[i])
    p1Vect=p1Num/p1Denom
    p0Vect=p0Num/p0Denom
    return p0Vect,p1Vect,pAbusive                   #返回属于非侮辱类条件概率数组,属于侮辱类条件概率数组,文档属于侮辱类概率

测试代码:

listOposts,listClasses = bayes.loadDataSet()
myVocabList = bayes.createVocabList(listOposts)#创建不重复的词表
trainMat = []
for postinDoc in listOposts:#按小list计数
    trainMat.append(bayes.setOfWords2Vec(myVocabList,postinDoc))#在trainmat中不断加入训练数据,加入的是词表list里面每个小list在总词表中的有无。
     #append加入也不是直接加入,而是把每次加入的对象单独作为一个新的list元素
p0V,p1V,pAb = bayes.trainNB0(trainMat,listClasses)
print(pAb)
print(p0V)
print(p1V)

结果:
在这里插入图片描述

4.5.3测试算法:根据现实情况修改分类器

    在利用贝叶斯分类器对文档进行分类时,会遇到两个问题:
    1、需要计算多个概率的乘积,即计算$p(w_{1}\mid c_{1})p(w_{2}\mid c_{1})……$,如果其中一个概率为0,则结果为0,为了降低这种影响,我们需要对概率值进行“平滑”处理,即分子加1,分母增加Ni表示第i个属性可能的取值数,这种方法称为拉普拉斯平滑,在本例中每个词可能取值数为2,即所有分母加2,分子加1。
    2、许多小数相乘会造成下溢,为了解决这个问题通常采取乘积取对数。
在这里我们对上面的分类器进行修改:

p0Num=np.ones(numWords)
p1Num=np.ones(numWords)
p0Denom=2.0
p1Denom=2.0
p1Vect=log(p1Num/p1Denom)
p0Vect=log(p0Num/p0Denom)

得到的结果为:
在这里插入图片描述
    现在已经准备好构建完整的分类器了。
在bayes.py中添加代码:

'''
构建贝叶斯分类函数
'''
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):  
    p1=sum(vec2Classify*p1Vec)+np.log(pClass1)      #对应元素相乘log(A*B)=log(A)+log(B)所以这里+log(pClass1)
    p0=sum(vec2Classify*p0Vec)+np.log(1.0-pClass1)  #这里计算的p(w|Ci)*p(Ci)=p(w0,w1,……|Ci)*p(Ci)=p(w0|Ci)p(w1|Ci)……p(Ci)
    if p1>p0:
        return 1
    else:
        return 0


#测试朴素贝叶斯分类器
def testingNB():
    listOPosts,listClasses=loadDataSet()
    myVocabList=createVocabList(listOPosts)    #创建词汇表
    trainMat=[]
    for postinDoc in listOPosts:               #得到训练集
        trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
    p0V,p1V,pAb=trainNB0(np.array(trainMat),np.array(listClasses))  #训练分类器,注意列表到array格式的转化
    testEntry=['love','my','dalmation']
    thisDoc=np.array(setOfWords2Vec(myVocabList,testEntry))
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'属于侮辱类')
    else:
        print(testEntry,'属于非侮辱类')
    testEntry=['stupid','garbage']
    thisDoc=np.array(setOfWords2Vec(myVocabList,testEntry))
    if classifyNB(thisDoc,p0V,p1V,pAb):
        print(testEntry,'属于侮辱类')
    else:
        print(testEntry,'属于非侮辱类')

测试代码:

bayes.testingNB()

结果:
在这里插入图片描述

4.5.4准备数据:文档词袋模型

    我们将每个词出现与否作为一个特征,这种方式为词集模型。如果一个词在文档中不止出现一次,则为词袋模型,为了适应词袋模型,我们需要对函数setOfWords2Vec()进行修改。
在bayes.py中添加代码:

#基于词袋模型
def bagOfWords2VecMN(vocabList,inputSet):
    returnVec=[0]*len(vocabList)
    for word in inputSet:
        returnVec[vocabList.index(word)]+=1         #词汇没出现一次就加1
    return returnVec

4.6示例:使用朴素贝叶斯过滤垃圾邮件

4.6.1准备数据:切分文本

    对于一个文本字符串,可以使用Python的string.split()方法将其切分。
代码:

mySent='This book is the best book on Python or M.L. I have ever laid eyes upon.'
print(mySent.split())

结果:
在这里插入图片描述
    可以看到,切分的结果不错,但是标点符号也被当成了词的一部分。可以使用正则表示式来切分句子,其中分隔符是除单词、数字外的任意字符串。
代码:

import re

mySent='This book is the best book on Python or M.L. I have ever laid eyes upon.'
regEx=re.compile('\\W+')  #除单词数字外的任意字符串
print(regEx.split(mySent))

结果:
在这里插入图片描述
    现在得到了一系列词组成的词表,但是里面的空字符串需要去除掉。可以计算每个字符串的长度,只返回长度大于零的字符串。并且可以采用python内嵌的字符串全部转换大小写的方法(.lower() or .upper())
代码:

import re

mySent='This book is the best book on Python or M.L. I have ever laid eyes upon.'
listOfTokens = re.split(r'\W+',mySent)
print([tok.lower() for tok in listOfTokens if len(tok) > 0])

结果:
在这里插入图片描述

4.6.2测试算法:使用朴素贝叶斯进行交叉验证

在bayes.py中添加代码:

import re
def textParse(bigString):    #input is big string, #output is word list
    listOfTokens = re.split(r'\W+', bigString)
    return [tok.lower() for tok in listOfTokens if len(tok) > 0]

import random
def spamTest():
    docList=[]
    classList=[]
    fullText=[]
    for i in range(1,26):               #遍历25个文件
        wordList=textParse(open('email/spam/%d.txt'%i,'r').read())
        docList.append(wordList)
        fullText.append(wordList)
        classList.append(1)     #垃圾邮件标记为1
        wordList=textParse(open('email/ham/%d.txt'%i,'r').read())
        docList.append(wordList)
        fullText.append(wordList)
        classList.append(0)    #非垃圾邮件标记为0
    vocabList=createVocabList(docList)          #创建不重复词汇表
    trainingSet=list(range(50))                 #共50个文件,索引值0-50,列表格式
    testSet=[]
    for i in range(10):
        randIndex=int(random.uniform(0,len(trainingSet)))  #随机选择10个作为测试集
        testSet.append(trainingSet[randIndex])             #添加到测试集列表
        del(trainingSet[randIndex])                        #删除该索引值
    trainMat=[]
    trainClasses=[]
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))  #将选中的索引值对应样本添加到训练集
        trainClasses.append(classList[docIndex])
    p0V,p1V,pSpam=trainNB0(np.array(trainMat),np.array(trainClasses))
    errorCount=0
    for docIndex in testSet:
        wordVector=setOfWords2Vec(vocabList,docList[docIndex])
        if classifyNB(np.array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:
            errorCount+=1             #统计分类错误的样本数量
            print('分类错误的测试集:',docList[docIndex])
    print('错误率:%.2f%%'%(float(errorCount)/len(testSet)*100))

测试代码:

import bayes

#测试
bayes.spamTest()
Last modification:November 21, 2023
If you think my article is useful to you, please feel free to appreciate