朴素贝叶斯之邮件分类

实例

上一章介绍了贝叶斯分类的理论基础,下面举个例子说明。
有如下数据集:
朴素贝叶斯之邮件分类_第1张图片

P(Y=1)=915,P(Y=1)=615
对特征 X(1),Y=1
P(X(1)=1|Y=1)=29,P(X(1)=2|Y=1)=39,P(X(1)=3|Y=1)=49
对特征 X(2),Y=1
P(X(2)=S|Y=1)=19,P(X(2)=M|Y=1)=49,P(X(2)=L|Y=1)=49
对特征 X(1),Y=1
P(X(1)=1|Y=1)=36,P(X(1)=2|Y=1)=26,P(X(1)=3|Y=1)=16
对特征 X(2),Y=1
P(X(2)=S|Y=1)=36,P(X(2)=M|Y=1)=26,P(X(2)=L|Y=1)=16
对于给定的 x=2,ST

只需对 X(1)=2X(2)=S 时候的进行判断
P(Y=1)P(X(1)=2|Y=1)P(X(2)=S|Y=1)=9153919=145
P(Y=1)P(X(1)=2|Y=1)P(X(2)=S|Y=1)=6152636=345
则属于 1

邮件分类

提取词汇表

提取所有邮件中的词汇,组成词汇表,这里的词汇表中每个单词只能出现一次。

def loadDataSet():
    postingList=[['my','dog','has','flea','problem','help','please'],
                 ['maybe','not','take','him','to','dog','park','stupid'],
                 ['my','dalmation','is','so','cute','I','love','him'],
                 ['stop','posting','stupid','workless','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 creatVocabList(dataSet):
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet|set(document)
    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

loadDataSet() 是测试的数据
createVocabList() 创建词汇表, set 是元素只能出现一次的集合, | 是Python中的并集的一次,在集合中不断的加入新的词汇。
setOfWordsVec() 函数,输入参数是词汇表和要判断的文档,这里的判断是,只要文档中的原始在词汇表中出现了无论你出现几次,就值为1,返回的向量是和词汇表等长,只包含 0,1 的向量,代表对于位置是否在词汇表中出现。
该函数被称为词集模型,记录单词是否出现。

算法过程

P(ck|w)=P(w|ck)P(ck)P(w)=P(w0|ck)P(w1|ck),...,P(wn|ck)P(w)

计算每个类别中的文档数目

对每篇训练文档:
===对每个类别:
======如果词条出现在文档中,则增加该词条的计数
======增加所有出现词条的计数值
===对每个类别:
======对每个词条:
=========将该词条的数目除以总词条的数目得到条件概率
===返回每个类别的条件概率

def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs)
    # p0Num = zeros(numWords)
    # p1Num = zeros(numWords)
    # p0Denom = 0.0
    # p1Denom = 0.0
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i]==1:
            p1Num+=trainMatrix[i]
            p1Denom+=sum(trainMatrix[i])
        else:
            p0Num+=trainMatrix[i]
            p0Denom+=sum(trainMatrix[i])
    # p1Vect = p1Num/p1Denom
    # p0Vect = p0Num/p0Denom
    p1Vect = log(p1Num/p1Denom)
    p0Vect = log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive

上面的函数返回了条件概率和先验概率
为防止出现0的情况,对其中注释部分进行了修改

def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
    p1 = sum(vec2Classify*p1Vec)+log(pClass1)
    p0 = sum(vec2Classify*p0Vec)+log(1.0-pClass1)
    if p1>p0:
        return 1 
    else :
        return 0

上面的函数根据先验概率和条件概率,计算该邮件所属邮件的类别

def bagOfwords2VecMN(vocabList,inputSet):
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)]+=1
    return returnVec

该函数被称为词袋模型,统计每个单词出现的次数。

def textParse(bigString):
    import re
    listOfTokens = re.split(r'\W*',bigString)
    return [tok.lower() for tok in listOfTokens if len(tok)>3]

将字符转换成小写,并过滤掉长度小于3的单词

def spamTest():
    docList= []
    classList= []
    fullText=[]
    for i in range(1,26):
        wordList = textParse(open('email/spam/%d.txt'%i).read())
        docList.append(wordList)
        fullText.append(wordList)
        classList.append(1)
        wordList = textParse(open('email/ham/%d.txt'%i).read())
        docList.append(wordList)
        fullText.append(wordList)
        classList.append(0)
    vocabList = creatVocabList(docList)
    trainingSet = range(50)
    testSet = []
    for i in range(10):
        randIndex = int(random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:
        trainMat.append(bagOfwords2VecMN(vocabList,docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V,p1V,pSpam= trainNB0(array(trainMat),array(trainClasses))
    errorCount = 0
    for docIndex in testSet:
        wordVector= bagOfwords2VecMN(vocabList,docList[docIndex])
        if classifyNB(array(wordVector),p0V,p1V,pSpam)!= classList[docIndex]:
            errorCount+=1
            print 'error email:',docList[docIndex]
    print 'the error rate is:',float(errorCount)/len(testSet)

测试程序
在50封邮件中,正常邮件和垃圾邮件各占25,在在其中随机抽取10封电子邮件,预测其所在的类别。

运行几次发现错误率在6-10%之间。

总结

1.朴素贝叶斯分类分类的结果受类的均衡程度影响比较大,如果某个类的数据比较多,则其先验概率比较大,很可能使测试数据都分到这个类,所有在分类时候要对类别的数据进行均衡。
2.邮件分类的词汇表是训练集所有的单词组成的
在计算后条件概率的时候,实际上把正类邮件在一起考虑,负类邮件在一起考虑。
若被分到正类,计算测试邮件中单词,在词汇表中出现的每个单词出现的概率,也即条件概率。训练集中正类所占的比例就是先验概率。每个单词的条件概率的乘积再乘以先验概率就是,该测试样本属于正类的概率。
负类,同上
3.在提取单词作为词汇表时候,要过滤掉单词长度比较短的如:is 、a 、an、the,等,至于过录掉长度为几的单词,需要自己测试。
4.在朴素贝叶斯定理中我们认为特征之间是相互独立的,然而邮件中的单词不一定是相互独立的。
5.对于一些复杂的邮件,受特点的词汇影响比较大分类的准确率会很差的。比如:恐怖分子在准备发生恐怖袭击的时候,邮件讨论的话题一定是关于炸弹、弹药的词汇。然而如果是个弹药专家,ta的邮件中也弹药方面的词汇也是比较多的,不能直接简单的根据条件概率判断,邮件的发送者的身份对邮件的内容影响比较大,同时许多情况下邮件的发送者身份有不知道,所有要添加人工因素(这个我就不知道了),来提高准确率。

你可能感兴趣的:(朴素贝叶斯分类,邮件分类)