您的位置:首页 > 其它

朴素贝叶斯之邮件分类

2015-07-11 21:09 429 查看

实例

上一章介绍了贝叶斯分类的理论基础,下面举个例子说明。

有如下数据集:



P(Y=1)=915,P(Y=−1)=615P(Y=1)=\frac{9}{15},P(Y=-1)=\frac{6}{15}

对特征X(1),Y=1X^{(1)},Y=1

P(X(1)=1|Y=1)=29,P(X(1)=2|Y=1)=39,P(X(1)=3|Y=1)=49P(X^{(1)}=1|Y=1)=\frac{2}{9},P(X^{(1)}=2|Y=1)=\frac{3}{9},P(X^{(1)}=3|Y=1)=\frac{4}{9}

对特征X(2),Y=1X^{(2)},Y=1

P(X(2)=S|Y=1)=19,P(X(2)=M|Y=1)=49,P(X(2)=L|Y=1)=49P(X^{(2)}=S|Y=1)=\frac{1}{9},P(X^{(2)}=M|Y=1)=\frac{4}{9},P(X^{(2)}=L|Y=1)=\frac{4}{9}

对特征X(1),Y=−1X^{(1)},Y=-1

P(X(1)=1|Y=−1)=36,P(X(1)=2|Y=−1)=26,P(X(1)=3|Y=−1)=16P(X^{(1)}=1|Y=-1)=\frac{3}{6},P(X^{(1)}=2|Y=-1)=\frac{2}{6},P(X^{(1)}=3|Y=-1)=\frac{1}{6}

对特征X(2),Y=−1X^{(2)},Y=-1

P(X(2)=S|Y=−1)=36,P(X(2)=M|Y=−1)=26,P(X(2)=L|Y=−1)=16P(X^{(2)}=S|Y=-1)=\frac{3}{6},P(X^{(2)}=M|Y=-1)=\frac{2}{6},P(X^{(2)}=L|Y=-1)=\frac{1}{6}

对于给定的x=(2,S)Tx=(2,S)^T

只需对X(1)=2和X(2)=SX^{(1)}=2和X^{(2)}=S时候的进行判断

P(Y=1)P(X(1)=2|Y=1)P(X(2)=S|Y=1)=915∗39∗19=145P(Y=1)P(X^{(1)}=2|Y=1)P(X^{(2)}=S|Y=1)=\frac{9}{15}*\frac{3}{9}*\frac{1}{9}=\frac{1}{45}

P(Y=−1)P(X(1)=2|Y=−1)P(X(2)=S|Y=−1)=615∗26∗36=345P(Y=-1)P(X^{(1)}=2|Y=-1)P(X^{(2)}=S|Y=-1)=\frac{6}{15}*\frac{2}{6}*\frac{3}{6}=\frac{3}{45}

则属于 −1-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()loadDataSet()是测试的数据

createVocabList()createVocabList()创建词汇表,setset是元素只能出现一次的集合,||是Python中的并集的一次,在集合中不断的加入新的词汇。

setOfWordsVec()setOfWordsVec()函数,输入参数是词汇表和要判断的文档,这里的判断是,只要文档中的原始在词汇表中出现了无论你出现几次,就值为1,返回的向量是和词汇表等长,只包含0,10,1的向量,代表对于位置是否在词汇表中出现。

该函数被称为词集模型,记录单词是否出现。

算法过程

P(ck|w)=P(w|ck)P(ck)P(w)=P(w0|ck)P(w1|ck),...,P(wn|ck)P(w)P(c_k|w)=\frac{P(w|c_k)P(c_k)}{P(w)}=\frac{P(w_0|c_k)P(w_1|c_k),...,P(w_n|c_k)}{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的邮件中也弹药方面的词汇也是比较多的,不能直接简单的根据条件概率判断,邮件的发送者的身份对邮件的内容影响比较大,同时许多情况下邮件的发送者身份有不知道,所有要添加人工因素(这个我就不知道了),来提高准确率。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: