机器学习实战 第四章 基于概率论的分类方法:朴素贝叶斯

本篇博客详细介绍了使用朴素贝叶斯方法进行文档分类,包括基于贝叶斯决策理论、条件概率的计算,以及如何通过Python实现词向量构建、训练和交叉验证,以过滤垃圾邮件。重点展示了如何处理文本数据并应用在实际场景中.
摘要由CSDN通过智能技术生成

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

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

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

4.2条件概率

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

4.3使用条件概率来分类

    贝叶斯分类准则:
    如果 p ( c 1 ∣ x , y ) > p ( c 2 ∣ x , y ) p(c_{1}\mid x,y)>p(c_{2}\mid x,y) p(c1x,y)p(c2x,y),那么属于类别 c 1 c_{1} c1
    如果 p ( c 1 ∣ x , y ) < p ( c 2 ∣ x , y ) p(c_{1}\mid x,y)<p(c_{2}\mid x,y) p(c1x,y)p(c2x,y),那么属于类别 c 2 c_{2} c2

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 ∣ w ) = p ( x ∣ c ) p ( c ) p ( w ) p(c\mid w)=\frac{p(x\mid c)p(c)}{p(w)} p(cw)=p(w)p(xc)p(c)对每个类计算该值,然后比较这两个概率值的大小( w w w表示一个向量)。
    首先可以通过类别 i i i (侮辱性留言或非侮辱性留言)中文档数除以总的文档数来计算概率 p ( c i ) p(c_{i}) p(ci)。接下来计算 p ( w ∣ c i ) p(w\mid c_{i}) p(wci),这里就要用到朴素贝叶斯假设。这里假设所有词都相互独立,该假设也称作条件独立性假设。
在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 ∣ c 1 ) p ( w 2 ∣ c 1 ) … … p(w_{1}\mid c_{1})p(w_{2}\mid c_{1})…… p(w1c1)p(w2c1)……,如果其中一个概率为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()

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值