读书笔记:机器学习实战【第4章:朴素贝叶斯】
来源:互联网 发布:淘宝认证 编辑:程序博客网 时间:2024/05/17 09:11
读书笔记:机器学习实战【第4章:朴素贝叶斯】
4.1 基于贝叶斯决策理论的分类方法
朴素贝叶斯:
优点:在数据较少的情况下依然有效,可以处理多类别问题
缺点:对于输入数据的准备方式较为敏感
适用数据类型:标称型数据
朴素贝叶斯是贝叶斯决策理论的一部分,贝叶斯决策理论的核心思想是选择具有最高概率的决策。
4.2 条件概率
4.3 使用条件概率分类
基本都是最基本的概率论知识,跳过。
4.4 使用朴素贝叶斯进行文档归类
由统计学知,如果每个特征需要N个样本,则对于10个特征就需要
而如果特征之间相互独立,样本数就可以从
4.5 使用Python进行文档归类
要从文本中获取特征,就需要先拆分文本,这里的特征是来自文本的词条,一个词条是字符的任意组合,可以把词条想象成单词,也可以使用非单词的词条(如URL,IP地址或任意其他字符串)。然后将每一个文本片段表示为一个词条向量,其中值为1表示词条出现在文档中,0表示未出现。
以在线社区的留言板为例,构建一个快速的分类器,如果某条留言使用了过滤或者侮辱性的语言,就将该留言标识为内容不当,对此问题建立两个类别:侮辱类和非侮辱类,使用1和0表示。
4.5.1 准备数据:从文本中构建词向量
首先,把文本看成单词向量或者词条向量,也就是说把句子转换成向量。考虑出现在所有文档中的所有单词,再决定把哪些词纳入词汇表或者说所要的词汇集合。
为此,首先要将每一篇文档转换为词汇表上的向量:
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代表侮辱性文字,0为正常言论。 return postingList,classVecdef createVocabList(dataSet): #创建词汇集合 vocabSet = set([]) #创建一个空集 for document in dataSet: vocabSet = vocabSet | set(document) #对每行创建集合,计算这一行和vocabSet的并集,即把每一行的都算进去。 return list(vocabSet)def setOfWords2Vec(vocabList,inputSet): ##把词汇列表(其实就是文档)转变为向量 returnVec = [0]*len(vocabList) ##创建词汇表长度的全0向量 for word in inputSet:#对文档中的所有单词 if word in vocabList: #如果单词曾经出现在词汇表中: returnVec[vocabList.index(word)] = 1 ##vocabList.index(word)是该word在vocabList中出现的位置,因为在该位置出现,则向量returnVec的对应位置表示为1。说明这个位置的单词,在已有词汇表出现。 else: print "the word %s is not in my Vocabulary!" % word return returnVec #最终返回的是一组根据词汇表产生的向量
接下来,监测函数的执行效果:
In [2]: list0Posts,listClasses = loadDataSet()In [3]: myVocabList = createVocabList(list0Posts)In [4]: myVocabListOut[4]: ['cute', 'love', 'help', 'garbage', 'quit', 'I', 'problems', 'is', 'park', 'stop', 'flea', 'dalmation', 'licks', 'food', 'not', 'him', 'buying', 'posting', 'has', 'worthless', 'ate', 'to', 'maybe', 'please', 'dog', 'how', 'stupid', 'so', 'take', 'mr', 'steak', 'my']
可以看到,获得的是一组不重复的单词表。
接下来,再看看生成词汇向量的运行效果:
In [6]: setOfWords2Vec(myVocabList,list0Posts[0])Out[6]: [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1]
4.5.2 训练算法:从词向量计算概率
根据贝叶斯准则:
我们根据上述公式,我们对每个类计算该概率值,然后比较概率的大小。具体计算方法是这样:首先通过类别i(即0或1,侮辱或非侮辱)中文档的数量除以总的文档数量来计算概率p(ci),接下来计算p(w|ci),这里要用到朴素贝叶斯假设,如果把特征向量w展开为一个个独立特征,那么旧可以将上述概率写作p(w0,w1,w2…wN|ci)。
这里假设所有单词都互相独立,该假设也被称作条件独立性假设,它意味着可以使用p(w0|ci)p(w1|ci)p(w2|ci)…p(wN|ci)来计算上述概率,这就极大简化了上述的过程。
该函数的伪代码如下:
计算每个类别中的文档数目对每篇训练文档: 对每个类别: 如果词条出现在文档中→增加该词条的计数值(即该词条出现过的次数) 增加所有词条的计数值(即词条总数) 对每个类别: 对每个词条: 将该词条的数目除以总词条数目得到条件概率 返回每个类别的条件概率
使用下述代码来实现:
def trainNB0(trainMatrix,trainCategory):##输入:文档矩阵,类别标签0-1向量 numTrainDocs = len(trainMatrix) #文档数 numWords = len(trainMatrix[0]) #每个文档的单词数,其实既然输入了矩阵,应该就是特征向量长度 pAbusive = sum(trainCategory)/float(numTrainDocs) ##这个其实就是类别为1的频率,即侮辱性语言出现的概率 p0Num = zeros(numWords);p1num = zeros(numWords) ##初始化0类别和1类别各个词汇的数目为全0 p0Denom = 0.0;p1Denom = 0.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 return p0Vect,p1Vect,pAbusive ##获得了两个类别的概率向量,以及类别1的概率
下面尝试验证代码:
In [9]: trainMat = []In [10]: for postinDoc in list0Posts: ...: trainMat.append(setOfWords2Vec(myVocabList,postinDoc)) ...:
这样,我们获得的是一组文档的特征向量,即处理完成的数据。
In [14]: p0V,p1V,pAb=trainNB0(trainMat,listClasses)In [15]: pAbOut[15]: 0.5In [16]: p0VOut[16]: array([ 0.04166667, 0.04166667, 0.04166667, 0. , 0. , 0.04166667, 0.04166667, 0.04166667, 0. , 0.04166667, 0.04166667, 0.04166667, 0.04166667, 0. , 0. , 0.08333333, 0. , 0. , 0.04166667, 0. , 0.04166667, 0.04166667, 0. , 0.04166667, 0.04166667, 0.04166667, 0. , 0.04166667, 0. , 0.04166667, 0.04166667, 0.125 ])In [17]: p1VOut[17]: array([ 0. , 0. , 0. , 0.05263158, 0.05263158, 0. , 0. , 0. , 0.05263158, 0.05263158, 0. , 0. , 0. , 0.05263158, 0.05263158, 0.05263158, 0.05263158, 0.05263158, 0. , 0.10526316, 0. , 0.05263158, 0.05263158, 0. , 0.10526316, 0. , 0.15789474, 0. , 0.05263158, 0. , 0. , 0. ])
使用该函数进行分类之前,还需要解决函数中的一些缺陷
4.5.3 测试算法:根据现实情况修改分类器
利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w0|1)p(w1|1)p(w2|1)。如果其中一个概率值为0,那么最后的乘积也会为0,为了降低这种影响,可以将所有词的出现数初始化为1,并将坟墓初始化为2。
因此,对trainNB0()中的第4、5行代码进行修改:
另一个要处理的问题是下溢出,这是由于太多很小的数相乘所造成的,为了解决下溢出问题可以采用对数,把return前的两行代码修改为:
p1Vect = log(p1Num/p1Denom)p0Vect = log(p0Num/p0Denom) ##取对数,防止下溢出
接下来,编写分类函数:
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1): #元素相乘: p1 = sum(vec2Classify*p1Vec) + log(pClass1) ##加对数概率等于乘以概率 p0 = sum(vec2Classify*p0Vec) + log(1-pClass1) ##加对数概率等于乘以概率 if p1 > p0: return 1 else : return 0def testingNB(): list0Posts,listClasses = loadDataSet() myVocabList = createVocabList(list0Posts) trainMat = [] for postinDec in list0Posts: trainMat.append(setOfWords2Vec(myVocabList,postinDec)) 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)
第一个函数是分类函数,第二个函数是方便起见的封装函数。
效果如下:
In [31]: testingNB()['love', 'my', 'dalmation'] classified as: 0['stupid', 'garbage'] classified as: 1
4.5.4 准备数据:文档词袋模型
目前为止,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型,如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为词袋模型,在词袋中,每个单词可以出现多次。
这里,对函数setOfWords2Vec稍加修改:
def bagOfWords2VecMN(vocabList,inputSet): returnVec = [0]*len(vocabList) for word in inputSet: if word in vocabList: returnVec[vocabList.index(word)] += 1 return returnVec
4.6 示例:使用朴素贝叶斯过滤垃圾邮件
4.6.1 准备数据:切分文本
下面介绍如何从文本文档中构建自己的词列表。
对于一个文本字符串,可以使用Python的string.split()方法切分,下面看看实际的运行效果:
In [35]: mySent = 'This book is the best book on Python or M.L. I have ever laid eyes upon.'In [36]: mySent.split()Out[36]: ['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', 'or', 'M.L.', 'I', 'have', 'ever', 'laid', 'eyes', 'upon.']
可以看到切分的效果不错,但问题在于,标点符号也被当成了词的一部分。
为此,可以使用正则表达式切分,把分隔符定位除了单词数字外的任意字符串:
In [37]: import reIn [38]: regEx = re.compile('\\W*')In [39]: listOfTokens = regEx.split(mySent)In [40]: listOfTokensOut[40]: ['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', 'or', 'M', 'L', 'I', 'have', 'ever', 'laid', 'eyes', 'upon', '']
关于正则表达式可以看:http://blog.csdn.net/drdairen/article/details/51134816
现在得到了一系列词组成的词表,但是里面的空字符串需要去掉,可以计算每个字符串的长度,只返回长度大于0的字符串:
In [41]: [tok for tok in listOfTokens if len(tok)>0]Out[41]: ['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', 'or', 'M', 'L', 'I', 'have', 'ever', 'laid', 'eyes', 'upon']
最后,我们发现句子中的第一个单次是大写的,如果目的是句子查找,那么这个特点会很有用,但是这里的文本只看成词袋,所以我们希望所有词的形势都是统一的。
Python有一些内嵌的方法可以将字符串全部转换成大写或小写,于是,这里使用.lower():
In [42]: [tok.lower() for tok in listOfTokens if len(tok)>0]Out[42]: ['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python', 'or', 'm', 'l', 'i', 'have', 'ever', 'laid', 'eyes', 'upon']
4.6.2 使用朴素贝叶斯进行交叉验证
下面将文本解析器集成到一个完整的分类器中。
def textParse(bigString): #文本拆分 import re listOfTokens = re.split(r'\W*',bigString) return [tok.lower() for tok in listOfTokens if len(tok)>2]def spamTest(): docList=[];classList=[];fullText=[] for i in range(1,26): wordList = textParse(open('email/spam/%d.txt' % i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(1) wordList = textParse(open('email/ham/%d.txt' % i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(0) vocabList = createVocabList(docList) trainingSet = range(50); testSet=[] #生成训练集编号list和测试集编号list(为空) for i in range(10): ##进行10次筛选,每次选出一个样本加入测试集,从训练集剔除 randIndex = int(random.uniform(0,len(trainingSet))) #随机生成一个序号 test.append(trainingSet[randIndex]) del(trainingSet[randIndex]) ##被选中序号加入测试集,从训练集开除 trainMat = [];trainingClasses = [] for docIndex in trainingSet: trainMat.append (setOfWords2Vec(vocabList,docList[docIndex])) trainingClasses.append(classList[docIndex]) p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses)) ##根据训练集计算概率分布向量 errorCount = 0 for docIndex in testSet:#对测试集分类 wordVector = setOfWords2Vec(vocabList,docList[docIndex]) if classifyNB(array(wordVector),p0V,p1V,pSpam) != classList[docIndex]: errorCount +=1;print 'Classification error doc:', docList[docIndex] print 'the error rate is :',float(errorCount)/len(testSet)
实验结果如下:
In [95]: spamTest()the error rate is : 0.0In [96]: spamTest()Classification error doc: ['home', 'based', 'business', 'opportunity', 'knocking', 'your', 'door', 'don', 'rude', 'and', 'let', 'this', 'chance', 'you', 'can', 'earn', 'great', 'income', 'and', 'find', 'your', 'financial', 'life', 'transformed', 'learn', 'more', 'here', 'your', 'success', 'work', 'from', 'home', 'finder', 'experts']
- 读书笔记:机器学习实战【第4章:朴素贝叶斯】
- 机器学习实战---读书笔记: 第4章 基于概率论的分类而方法:朴素贝叶斯
- 机器学习实战第4章-朴素贝叶斯(bayes)
- 机器学习实战读书笔记-朴素贝叶斯
- 《机器学习实战》读书笔记6:朴素贝叶斯源码
- 【读书笔记】机器学习实战-4.6节 朴素贝叶斯过滤垃圾邮件
- 代码注释:机器学习实战第4章 基于概率论的分类方法:朴素贝叶斯
- 【机器学习实战】第4章 朴素贝叶斯(Naive Bayes)
- 【机器学习实战】第4章 基于概率论的分类方法:朴素贝叶斯
- 机器学习实战笔记4(朴素贝叶斯)
- 机器学习实战4--朴素贝叶斯
- 机器学习实战笔记4(朴素贝叶斯)
- 机器学习实战-4朴素贝叶斯-python3
- 读书笔记:机器学习实战(3)——章4的朴素贝叶斯分类代码和个人理解与注释
- 《机器学习实战》读书笔记 第四章 朴素贝叶斯(part 1)
- 《机器学习实战》读书笔记 第四章 朴素贝叶斯(part 2)
- 机器学习实战 朴素贝叶斯
- 机器学习实战-朴素贝叶斯
- Unity中物体在固定路线移动
- transformClassesWithJarMergingForDebug错误原因以及解决方法
- java 面向对象基础
- HTML行元素和块元素
- codeforces843C Upgrading Tree -- 构造
- 读书笔记:机器学习实战【第4章:朴素贝叶斯】
- 构建嵌入式Linux应用系统 —— mp3播放器 madplay的移植
- Java实现Html转PDF
- 【Data_Structure笔记4】排序算法之【交换类排序】
- DirectFB学习之使用devmem驱动
- springboot搭建
- Linux信号(signal) 机制分析
- hdu 2082 找单词(母函数)
- Android vitamo 实现横竖屏的切换和页面内部的网络视频