机器学习系列--Naive Bayes Classification
2017-08-09 00:11
225 查看
Native Bayes
贝叶斯决策理论的核心思想:选择最高概率的决策。朴素贝叶斯是贝叶斯决策理论的一部分。下面不加证明地直接给出贝叶斯定理:
朴素贝叶斯分类的正式定义如下:
因为分母对于所有类别为常数,因为我们只要将分子最大化皆可。又因为各特征属性是条件独立的,所以有:
分类问题
现在实际的来研究一个文本分类的问题,下面是朴素贝叶斯分析问题的一般过程:1.收集数据:可以使用任何方法。
2.准备数据:需要数值型或者布尔型的数据。
3.分析数据:有大量的特征时,绘制特征作用不大,此时使用直方图效果会更好。
4.训练算法:计算不同的独立特征的条件概率。
5.测试算法:计算错误率。
6.使用算法:一个常见的朴素贝叶斯应用是文档分类,可以在任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本。
为了简单起见,先研究一个社区的评论条目,构建一个快速过滤器,来分辨每条评论属于侮辱性评论还是非侮辱性评论,分别用1和0来代表,接下来的代码演示如何将词条数据转换成布尔型或数值型的数据:
词表到向量的转换函数:
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
上面这个load数据的函数,功能很简单就是创建两个数组,并返回它们,一个是词条列表即评论,共6个评论,一个是每个词条所对应的类别列表。这里所说的词条也就是一个句子,比如例子中的一个评论,后续还可能是一篇文章,一封邮件等,而词条列表则是由这些词条组成的数组。后面会这么用这个函数:
def createVocabList(dataSet): vocabSet = set([]) #create empty set for document in dataSet: vocabSet = vocabSet | set(document) #union of the two sets return list(vocabSet)
这个函数旨在创建包含上面词条列表所有词汇,但不会重复的数组。先创建一个空的set数据类型,然后遍历每个词条,用 ‘|’求并集,返回不重复的单词。看一下函数的效果:
返回的不重复的词汇列表,可能是没按照顺序的,不过这个无关紧要。
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
上面这个函数的作用就是将词汇数组,输出成程序可以更好识别的向量。向量的每一个元素为1或0,表示词汇表中的单词在输入文档(词条)中是否出现。实现原理是遍历文档每个词,再判断每个词是否出现在词汇表中,进而对输出向量进行赋值操作。看一下效果:
从词向量计算概率
将朴素贝叶斯的定理应用到这个社区评论过滤的案例中,前面已经提到用x,y表示的贝叶斯定理,现在用w 代表一个向量,该向量由多个数值组成,其实际意义就是社区每条评论的数值化结果即[0,1,0,0,1,1,1….]这种形式,用字母Ci代表评论的类别,在这个案例中只有两个类别:C1=1,侮辱性评论,C2=非侮辱性评论。那我们这个案例的研究目的就是分辨新输入的评论属于哪一类别,也就是给定评论内容(词条数组向量)的条件下,属于哪种类别的概率,用符号表示就是P(Ci | w)。有贝叶斯定理可以得到:利用这个公式,每当输入一个文档,我们会计算这个文档属于每一个类别的概率,选择最大概率的那个类别作为评判结果,体现了贝叶斯决策理论的核心思想:选择最高概率的决策。
主要分析一下右边的式子:
分子:P(w | Ci)代表着在已知类别的情况下,对每个单词求其出现的概率,这个就跟前一篇介绍生成学习算法中大象和小狗的例子差不多了,在这里w回事由很多值组成的向量,即可写成P(w0,w1,w2,w3,…,wn|Ci),按照前面朴素贝叶斯的假设:w中每个特征条件独立,所以可以写成:P(w0 | Ci)P(w1 | Ci)P(w2 | Ci)P(w3 | Ci)…..P(wn | Ci) ,按照这样来计算上述概率。P(Ci)为ci类别的个数占总个数的概率,比如这个案例中p(C1)=p(c2)=3/6=0.5。
分母:是表示某个词向量的概率,在实际情况中,词向量是给定的也就是输入数据,所以分母通常为常数,所以可忽略不计,不影响整体概率结果。
下面这个函数是求P(w |ci)的过程:
def trainNB0(trainMatrix,trainCategory): numTrainDocs = len(trainMatrix) #trainMatrix的长度,也就是几篇文档 numWords = len(trainMatrix[0]) #trainMatrix中每篇文档的长度,即有多少个单词 pAbusive = sum(trainCategory)/float(numTrainDocs) #trainCategory中类别1的个数除以文档总数,也就是类别1的比例 p0Num = ones(numWords); p1Num = ones(numWords) #创建全是0的数组,后面为了防止出现全0导致概率无效,换成了全1数组,change to ones() p0Denom = 2.0; p1Denom = 2.0 #分母基数初始化为2 ,防止出现分母为0,或者比分子小情况,change to 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 = log(p1Num/p1Denom) #这里原本是不加log运算,得出的结果就是概率,不过后面会有乘法计算,为了避免最后结果变成0,采用log运算 #change to log() p0Vect = log(p0Num/p0Denom) #change to log() return p0Vect,p1Vect,pAbusive #分别返回类别0中,各个单词的概率;类别1中各个单词的概率;类别为1的概率
上面这些函数的过程,下面用手写草图详细介绍下其中数据的处理流程,便于理解:
上述函数的运行结果就是得到在两个类别下的每个单词的概率:
下面是加上log函数的结果:
p0v: [-2.56494936 -2.56494936 -2.56494936 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -3.25809654 -2.56494936 -3.25809654 -3.25809654 -2.56494936 -2.56494936 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -3.25809654 -2.15948425 -3.25809654 -3.25809654 -3.25809654 -2.56494936 -1.87180218 -3.25809654 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -2.56494936 -2.56494936] p1v: [-1.94591015 -3.04452244 -3.04452244 -2.35137526 -3.04452244 -3.04452244 -3.04452244 -1.94591015 -3.04452244 -2.35137526 -2.35137526 -3.04452244 -3.04452244 -1.65822808 -3.04452244 -3.04452244 -3.04452244 -2.35137526 -2.35137526 -2.35137526 -2.35137526 -2.35137526 -3.04452244 -3.04452244 -2.35137526 -2.35137526 -3.04452244 -2.35137526 -2.35137526 -3.04452244 -3.04452244 -3.04452244]
经过数据训练之后我们得到两个类别下每个单词会出现的概率,以上数据仅为参考,我发现函数 createVocabList 每次出来的结果都不一样,是随机的,我用python是3.6的,所以这里就不一一对比数据的准确性,我再debug的时候对比是没错的。
关键部分理解完了,下面分析一下如何使用这个训练结果,我贴上全部的代码:
import numpy as np 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 def trainNB0(trainMatrix,trainCategory): numTrainDocs = len(trainMatrix) numWords = len(trainMatrix[0]) pAbusive = sum(trainCategory)/float(numTrainDocs) p0Num = np.ones(numWords); p1Num = np.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 = np.log(p1Num/p1Denom) p0Vect = np.log(p0Num/p0Denom) return p0Vect, p1Vect, pAbusive def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) # element-wise mult p0 = sum(vec2Classify * p0Vec) + np.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 if __name__ == '__main__': listOPosts, listClasses = loadDataSet() myVocabList = createVocabList(listOPosts) print(myVocabList) trainMat = [] for postinDoc in listOPosts: trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses)) # print("p0v:",p0V) # print("p1v:",p1V) testEntry = ['love', 'my', 'dalmation'] thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb)) testEntry = ['stupid', 'garbage'] thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
主要业务逻辑看main函数,
p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listClasses))
这一步过后就得到了每个单词的概率向量了。接下来开始分类,对于新的数据(语句),这边先得对照词汇表转化成向量。
testEntry = ['love', 'my', 'dalmation'] thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
向量化的结果(这个也要看词汇表排序的,仅做参考):
[0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
下面进入分类:
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) # element-wise mult p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1) if p1 > p0: return 1 else: return 0
输入的四个参数,第一个是新数据的向量化结果,第二第三分别是各类别下每个单词概率的向量,第四个为其中某一类别的概率。
函数主要实现了贝叶斯公式中 右边分子的部分,也就是 在给定类别下这个词向量的概率和这个类别的概率的相乘。
这里的sum(vec2Classify * p1Vec) 操作,实际上就是:
[0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] * [-2.56494936 -2.56494936 -2.56494936 -3.25809654 -2.56494936 -2.56494936
-2.56494936 -3.25809654 -2.56494936 -3.25809654 -3.25809654 -2.56494936
-2.56494936 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -3.25809654
-2.15948425 -3.25809654 -3.25809654 -3.25809654 -2.56494936 -1.87180218
-3.25809654 -3.25809654 -2.56494936 -2.56494936 -2.56494936 -2.56494936
-2.56494936 -2.56494936]
一一对应相乘最后相加,可以看到这个过程,0所对应的项相乘都变成0了,只有在词汇表中出现过的单词为1 余对应概率相乘才会有有效结果,最后相加是求这整个词向量的概率结果,这里想加并没有和前面的“w中每个特征条件独立,所以可以写成:P(w0 | Ci)P(w1 | Ci)P(w2 | Ci)P(w3 | Ci)…..P(wn | Ci) ,按照这样来计算上述概率。”矛盾,这边是log函数相加,log(P(w0 | Ci)P(w1 | Ci)P(w2 | Ci)P(w3 | Ci)…..P(wn | Ci))= log((P(w0 | Ci))+log(P(w1 | Ci))+……+log(P(wn | Ci))。最后加上类别概率的log结果,其实就是公式中的P(w|Ci)*P(Ci)的相乘结果的log,可以拆成两个log相加。
分类函数中,把把每个类别的概率都算出来,然后进行对比,谁比较大就属于那个类别。这就是简单的朴素贝叶斯分类算法,选择概率最大的那个。
相关文章推荐
- 机器学习之Naive Bayes&&python实践
- Naive Bayes text classification
- TEXT CLASSIFICATION FOR SENTIMENT ANALYSIS – NAIVE BAYES CLASSIFIER
- Twenty Newsgroups Classification实例任务之TrainNaiveBayesJob(一)
- 机器学习系列(2):logistic回归,贝叶斯(bayes)方法
- 【机器学习实战之二】:C++实现基于概率论的分类方法--朴素贝叶斯分类(Naive Bayes Classifier)
- 机器学习系列04——贝叶斯决策(Bayes)
- Naive Bayes algorithm for spam classification (Matlab实现)
- Naive Bayes Classification
- 【机器学习】分类算法之朴素贝叶斯分类(Naive Bayesian classification)
- BAT机器学习面试1000题系列
- 机器学习——分类算法3:朴素贝叶斯(Bayes) 思想 和 代码解释
- 【机器学习系列之四】概率统计学习基础
- 算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification)(转)
- 机器学习系列笔记(四)
- 机器学习系列笔记(六)
- PGM学习之三 朴素贝叶斯分类器(Naive Bayes Classifier)
- 阿里巴巴机器学习系列课程
- 【Python系列4】国外程序员整理的机器学习资源大全——Python篇
- 机器学习三人行(系列四)----手写数字识别实战(附代码)