700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 机器学习(6)——朴素贝叶斯(文本分类)

机器学习(6)——朴素贝叶斯(文本分类)

时间:2019-07-17 12:58:32

相关推荐

机器学习(6)——朴素贝叶斯(文本分类)

转载请注明作者和出处:/qq_28810395

Python版本: Python3.x

运行平台: Windows 10

IDE: Pycharm profession

如有需要数据集或整个工程文件的可以关注Stefan0704回复朴素贝叶斯

一、概率论相关

学习贝叶斯要涉及一些数学中的概率与条件概率,下面具体讲解,如下图在一个盒子中有7个砖头,其中三块是灰色,四块是黑色,如果随机从中取一块砖头,取到(灰|黑)的概率,就可以通过(灰|黑)砖头的数目除以总的砖头数目来得到。

上述概率分别为:

P(grey)=num(grey)num(all)=37{P_{(grey)}} = \frac{{num(grey)}}{{num(all)}} = \frac{3}{7}P(grey)​=num(all)num(grey)​=73​

P(black)=num(black)num(all)=47{P_{(black)}} = \frac{{num(black)}}{{num(all)}} = \frac{4}{7}P(black)​=num(all)num(black)​=74​

如果放置结果改变如下图,在盒子A、B(左、右)中选择,那么我们随机取就存在A、B盒选择的条件,在这样的条件下,我们在计算取(灰|黑)的概率,这就叫条件概率,其条件概率的定义为:

P(grey∣boxB)=P(greyandboxB)P(box){P_{({\rm{grey|boxB}})}} = \frac{{P(grey{\rm{ and }}boxB)}}{{P(box)}}P(grey∣boxB)​=P(box)P(greyandboxB)​

上述例子中我们计算在B盒中取灰砖的概率便为:

P(greyandboxB)=num(greyinB)num(AandB)=1/7P(boxB)=num(B)num(all)=3/7P(grey∣boxB)=P(greyandboxB)P(boxB)=1/73/7=13\begin{array}{l} {P_{({\rm{grey and boxB}})}} = \frac{{num(grey{\rm{ in }}B)}}{{num(A{\rm{ and B}})}} = 1/7\\ {P_{({\rm{boxB}})}} = \frac{{num(B)}}{{num(all)}} = 3/7\\ {P_{({\rm{grey|boxB}})}} = \frac{{P(grey{\rm{ and }}boxB)}}{{P(boxB)}} = \frac{{1/7}}{{3/7}} = \frac{1}{3} \end{array}P(greyandboxB)​=num(AandB)num(greyinB)​=1/7P(boxB)​=num(all)num(B)​=3/7P(grey∣boxB)​=P(boxB)P(greyandboxB)​=3/71/7​=31​​

贝叶斯准则是将条件概率中的条件和结果进行交换,即已知P(X|C)求P(C|X),其公式定义如下:

P(c∣x)=P(x∣c)P(c)P(x){P_{(c|x)}} = \frac{{P(x|c)P(c)}}{{P(x)}}P(c∣x)​=P(x)P(x∣c)P(c)​

具体的公式推导请看贝叶斯推导

二、朴素贝叶斯简介

朴素贝叶斯是贝叶斯决策理论的一部分,它是要求分类器给出一个最优的类别猜测结果,同时给出这个猜测的概率估计值的算法。相比于贝叶斯而言的“朴素”,是因为整个形式化过程只做最原始、最简单的假设,也就是特征之间相互独立。

更简单来说,朴素贝叶斯的思想基础是对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。举个简单的例子,你在街上看到一个黑人,我问你你猜这哥们哪里来的,你十有八九猜非洲。为什么呢?因为黑人中非洲人的比率最高,当然人家也可能是美洲人或亚洲人,但在没有其它可用信息下,我们会选择条件概率最大的类别,这就是朴素贝叶斯的思想基础。

具体描述请看朴素贝叶斯分类器

朴素贝叶斯的优缺点:

优点:在数据较小的情况下仍然有效,可以处理多类别问题,适用标称型数据。

缺点:对于输入数据的准备方式较为敏感。

三、朴素贝叶斯一般流程

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

四、利用朴素贝叶斯进行文本分类

以在线社区的留言板为例。为了使社区正能力的蓬勃发展,对于一些侮辱性的言论要进行屏蔽,为此需要一种快速的过滤器,如果某条留言使用了负面或者侮辱性的语句,我们就要标识语言不当,过滤掉相应留言。在此中问题建立两个类别:侮辱类和非侮辱类,使用1和0分别表示。

1.准备数据:文本中构建词向量

首先要将处理的文本看成单词向量或者词条向量,也就是将句子转换成向量。

准备代码

from numpy import *def loadDataSet(): #创建一些实验样本#假设数据为最简单的6篇文章,每篇文章大概7~8个词汇左右,如下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] #对应上述6篇文章的分类结果,1为侮辱性,0为非侮辱性return postingList,classVecdef createVocabList(dataSet):# 将所有文章中的词汇取并集汇总vocabSet = set([]) # 定义一个set空集(set存储的内容无重复)for document in dataSet:# 遍历导入的dataset数据,将所有词汇取并集存储至vocabSet中vocabSet = vocabSet | set(document) # | 符号为取并集,即获得所有文章的词汇表return list(vocabSet)def setOfWords2Vec(vocabList, inputSet):#该函数输入参数为词汇表及某篇文章,输出为文档向量,向量每个元素为1或0,分别表示词汇表中的单词在输入文档中是否出现;returnVec = [0]*len(vocabList)#构建一个0向量;for word in inputSet: # 遍历词汇表,如果文档中出现了词汇表中单词,则将输出的文档向量中对应值设为1,旨在计算各词汇出现的次数;if word in vocabList:returnVec[vocabList.index(word)] = 1 #因为上一段代码里,给的文章例子里的单词都是不重复的,如果有重复的单词的话,这段代码改写为:returnVec[vocabList.index(word)] += 1更为合适;else: print ("the word: %s is not in my Vocabulary!" % word)return returnVec #返回向量化后的某篇文章

检验代码

import bayesif __name__ == '__main__':listOPosts,listClasses = bayes.loadDataSet()myVocaList = bayes.createVocabList(listOPosts)print(myVocaList)

结果

['garbage', 'help', 'posting', 'quit', 'worthless', 'maybe', 'park', 'is', 'food', 'I', 'has', 'love', 'cute', 'buying', 'take', 'stop', 'mr', 'dog', 'please', 'him', 'ate', 'steak', 'licks', 'so', 'to', 'flea', 'stupid', 'dalmation', 'how', 'my', 'problems', 'not']

会发现我们现在已经可以把文本分割成不会重复的单词,现在我们检验一下词条是否在文档中,输出文档向量。检验代码

import bayesif __name__ == '__main__':listOPosts,listClasses = bayes.loadDataSet()myVocaList = bayes.createVocabList(listOPosts)print(myVocaList)print(listOPosts[0])print(bayes.setOfWords2Vec(myVocaList,listOPosts[0]))#检测文档1存在情况并输出文档向量print(listOPosts[2])print(bayes.setOfWords2Vec(myVocaList, listOPosts[2])) # 检测文档3存在情况并输出文档向量

结果

['garbage', 'help', 'posting', 'quit', 'worthless', 'maybe', 'park', 'is', 'food', 'I', 'has', 'love', 'cute', 'buying', 'take', 'stop', 'mr', 'dog', 'please', 'him', 'ate', 'steak', 'licks', 'so', 'to', 'flea', 'stupid', 'dalmation', 'how', 'my', 'problems', 'not']['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'][0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0]['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'][0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0]

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

文本的预处理我们已经处理完成了,下步我们就要将训练集的每篇文章向量化,然后计算了训练集中各分类中各词汇出现的概率p(wi∣c0),p(wi∣c1),p(wi_|c_0),p(w_i|c_1),p(wi∣​c0​),p(wi​∣c1​),和侮辱性文章的概率p(c1)p(c_1)p(c1​)、非侮辱性文章的概率p(c0)p(c_0)p(c0​)。现在我们回忆下朴素贝叶斯的公式:

P(ci∣w)=P(w∣ci)⋅P(ci)P(w)P({c_i}|w) = \frac{{P(w|{c_i}) \cdot P({c_i})}}{{P(w)}}P(ci​∣w)=P(w)P(w∣ci​)⋅P(ci​)​

这里cic_ici​可理解为文章的分类,c0c_0c0​代表不合格,c1c_1c1​代表合格。www可以理解为文章的各个特征,即词汇,每个词汇都算是一篇文章的独立特征,假设有n个独立特征,所有词都相互独立,上式可以写成:

P(ci∣w)==P(w0,w1,w2,...,wn∣ci)⋅P(ci)P(w0,w1,w2,...,wn)=P(w0∣ci)P(w1∣ci)P(w2∣ci)⋅⋅⋅P(wn∣ci)⋅P(ci)P(w0)P(w1)⋅⋅⋅P(wn)P({c_i}|w) = = \frac{{P({w_0},{w_1},{w_2},...,{w_n}|{c_i}) \cdot P({c_i})}}{{P({w_0},{w_1},{w_2},...,{w_n})}} = \frac{{P({w_0}|{c_i})P({w_1}|{c_i})P({w_2}|{c_i}) \cdot \cdot \cdot P({w_n}|{c_i}) \cdot P({c_i})}}{{P({w_0})P({w_1}) \cdot \cdot \cdot P({w_n})}}P(ci​∣w)==P(w0​,w1​,w2​,...,wn​)P(w0​,w1​,w2​,...,wn​∣ci​)⋅P(ci​)​=P(w0​)P(w1​)⋅⋅⋅P(wn​)P(w0​∣ci​)P(w1​∣ci​)P(w2​∣ci​)⋅⋅⋅P(wn​∣ci​)⋅P(ci​)​

理论已经回顾,下面便是计算概率的伪代码:

计算每个类别中的文档数目 #求 P(c0),P(c1)对每篇训练文档:对每个类别:如果词条出现在文档中:增加该词条计数值增加所有词条计数值 #求 P(w) 对每个类别:对每个词条:将该词条的数目除以总词条数目得到条件概率 #求 P(w|ci)

训练算法代码

#朴素贝叶斯分类器训练函数def trainNB0(trainMatrix,trainCategory):numTrainDocs = len(trainMatrix) #计算有多少篇文章numWords = len(trainMatrix[0]) #计算第一篇文档的词汇数pAbusive = sum(trainCategory)/float(numTrainDocs) #计算p(c1),p(c0)=1-p(c1)p0Num = np.zeros(numWords) #构建一个空矩阵,用来计算非侮辱性文章中词汇数p1Num = np.zeros(numWords) #构建一个空矩阵,用来计算侮辱性文章中词汇数p0Denom = 0.0; p1Denom = 0.0 for i in range(numTrainDocs): #遍历每一篇文章,来求P(w|c)if trainCategory[i] == 1: #判断该文章是否为侮辱性文章p1Num += trainMatrix[i] #累计每个词汇出现的次数p1Denom += sum(trainMatrix[i]) #计算所有该类别下不同词汇出现的总次数else:#如果该文章为非侮辱性文章p0Num += trainMatrix[i] p0Denom += sum(trainMatrix[i])p1Vect = p1Num/p1Denom #计算每个词汇出现的概率P(wi|c1)p0Vect = p0Num/p0Denom #计算每个词汇出现的概率P(wi|c0)return p0Vect,p1Vect,pAbusive

检验代码

import bayesif __name__ == '__main__':listOPosts,listClasses = bayes.loadDataSet()myVocaList = bayes.createVocabList(listOPosts)trainMat = []for postinDoc in listOPosts:trainMat.append(bayes.setOfWords2Vec(myVocaList, postinDoc))# 先文章转化为词条向量,并放入traninMat中p0V, p1V, pAb = bayes.trainNB0(trainMat, listClasses) # 计算训练集的p(c1),p(wi|c0),p(wi|c1)print(p0V, p1V, pAb,sep='\n')

结果

[0. 0.04166667 0. 0. 0. 0.0. 0.04166667 0. 0.04166667 0.04166667 0.041666670.04166667 0. 0. 0.04166667 0.04166667 0.041666670.04166667 0.08333333 0.04166667 0.04166667 0.04166667 0.041666670.04166667 0.04166667 0. 0.04166667 0.04166667 0.1250.04166667 0. ][0.05263158 0. 0.05263158 0.05263158 0.10526316 0.052631580.05263158 0. 0.05263158 0. 0. 0.0. 0.05263158 0.05263158 0.05263158 0. 0.105263160. 0.05263158 0. 0. 0. 0.0.05263158 0. 0.15789474 0. 0. 0.0. 0.05263158]0.5

我们计算的得出,属于侮辱类的概率是0.5,观察表中第一个单词‘garbage’,在类别1中出现一次,在类别0未出现,对应的条件概率是0.05263158和0.0,我们再看类别1中概率最大值(0.15789474),我们在数组中找到其单词‘stupid’,说明 **‘stupid’**最能表征类别1(侮辱性文档类)的单词。

`

3.训练函数修正:根据现实情况修改分类器

在上述的贝叶斯分类器进行文档分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w0∣1)p(w1∣1)p(w2∣1)p(w_0|1)p(w_1|1)p(w_2|1)p(w0​∣1)p(w1​∣1)p(w2​∣1),如果其中一个概率为0,那么最后的乘积也为0,与事实不符。为此进行下述改变:

在trainNB0()函数中进行修改

#p0Num = zeros(numWords); p1Num = zeros(numWords)p0Num = ones(numWords); p1Num = ones(numWords) # change to ones()#p0Denom = 0.0; p1Denom = 0.0 p0Denom = 2.0; p1Denom = 2.0 # change to 2.0

还有在计算概率时候很多,由于每个概率都非常小,再多次相乘之后数值基本接近为0,不符合现实,为此选择取对数。

如图所示,采用对数可以避免下溢处或者浮点数舍入导致的错误,同时采用自然对数进行处理不会有任何损失,比较曲线,两者的极值点位置同样,仅仅是取值不同,但不影响最终结果,在代码中进行修改。

#p1Vect = p1Num/p1Denom #p0Vect = p0Num/p0Denom p1Vect = log(p1Num / p1Denom) # change to log()p0Vect = log(p0Num / p0Denom) # change to log()

4.最终环节:实测分类

现在我们的所有已经准备充分了,就缺个最终分类函数,根据P(c0∣wi)>P(c1∣wi)P(c_0|w_i)>P(c_1|w_i)P(c0​∣wi​)>P(c1​∣wi​),则判断该文章为非侮辱性;如果P(c0∣wi)<P(c1∣wi)P(c_0|w_i)<P(c_1|w_i)P(c0​∣wi​)<P(c1​∣wi​),则判断该文章为侮辱性。代码如下:

def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):#vec2Classify为输入的一个向量化的新文章p1 = sum(vec2Classify * p1Vec) + log(pClass1)# 由于是取对数,所以乘积变为直接求和即可,注意这里list和list是无法相乘,vec2Classify需要为array格式p0 = sum(vec2Classify * p0Vec) + log(1-pClass1)if(p1>p0):return 1if(p0>p1):return 0

现在我们进行测试

def testingNB():listOPosts,listClasses = loadDataSet()myVocabList = createVocabList(listOPosts)trainMat=[]for postinDoc in listOPosts:trainMat.append(setOfWords2Vec(myVocabList, postinDoc))p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))testEntry = ['love', 'my', 'dalmation']thisDoc = array(setOfWords2Vec(myVocabList, testEntry))print (testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))testEntry = ['stupid', 'garbage']thisDoc = array(setOfWords2Vec(myVocabList, testEntry))print (testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))

主函数代码

import bayesif __name__ == '__main__':print(bayes.testingNB())

结果

['love', 'my', 'dalmation'] classified as: 0['stupid', 'garbage'] classified as: 1

五、参考信息

/c369624808/article/details/78906630

/blog//12/naive_bayes_classifier.html

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。