朴素贝叶斯之邮件分类
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的邮件中也弹药方面的词汇也是比较多的,不能直接简单的根据条件概率判断,邮件的发送者的身份对邮件的内容影响比较大,同时许多情况下邮件的发送者身份有不知道,所有要添加人工因素(这个我就不知道了),来提高准确率。
相关文章推荐
- leetCode 44.Wildcard Matching (通配符匹配) 解题思路和方法
- uva 104 arbitrage(套利)
- 用缓动函数模拟碰撞效果
- Text Justification
- 大约SQL现场“这包括”与“包括在”字符串的写法
- python 命令行参数解析
- 【啊哈!算法】算法6:只有五行的Floyd最短路算法
- java面向对象学习心得
- leetcode 日经贴,Cpp code -Lowest Common Ancestor of a Binary Search Tree
- 不要再挺着,我是我自己
- Android使用学习之绘图(Canvas,Paint)与手势感应及其应用(乒乓球小游戏)
- c++内存管理
- ubuntu下如何截图
- Java学习笔记03 俄罗斯方块界面
- 在nodeschool学习作用域链和闭包(Scope Chains And Closures)
- IJG JPEG 函数库:文件列表
- JAVA基础-字符串
- BZOJ 1192 鬼谷子的钱袋
- 求最大连续区间和的几种方法
- 暑假学习计划与学长的经验