#基于概率论的分类方法:朴素贝叶斯 ''' 优点:在数据较少的情况下任然有效,可以处理多类别问题。 缺点:对于输入数据的准备方式较为敏感。 适用数据类型:标称型数据 贝叶斯概率引入先验知识和逻辑推理来处理不确定命题。 朴素贝叶斯的一般过程: 1.收集数据:可以使用任何方法。本章使用RSS源。 2.准备数据:需要数值型或布尔型数据。 3.分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好。 4.训练算法:计算不同的独立特征的条件概率。 5.测试算法:计算错误率。 6.一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本。 ''' from numpy import * #词表到词向量转换函数 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,classVec def createVocabList(dataSet): #创建词表变量 vocabSet = set([]) for document in dataSet: #创建两个集合的并集 vocabSet = vocabSet | set(document) return list(vocabSet) def setOfWords2Vec(vocabList, inputSet): #创建一个其中所含元素都为0的向量 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) #sum(list)= 0+1+0+1+0+1 #初始化概率 #p0Num = zeros(numWords); p1Num = zeros(numWords) #分子,词的出现次数 #p0Denom = 0.0; p1Denom = 0.0 #分母 #利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,如果 #其中一个概率值为0,那么左后的乘积也为0。为降低这种影响,可以将所有词的出现次数初始化为1,并将分母初始化为2 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 #根据实际情况修改分类器,主要修改上面接口trainNB0() 内的初始化概率大的分子分母和元素做除法 ''' 1.利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,如果其中一个概率值为0,那么左后的乘积也为0。 为降低这种影响,可以将所有词的出现次数初始化为1,并将分母初始化为2 2.另一个问题是 下溢出,由于太多很小的数相乘造成的。一种解决办法是对乘积取自然对数。ln(a*b) = ln(a)+ln(b) ''' #朴素贝叶斯分类函数 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 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)) #文档词袋模型 ''' 目前为止,我们将每个词测出现与否作为一个特征,这可以被描述为词集模型(set-of-words model)。 如果一个词在文档中出现不止一次,,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法 称为词袋模型(bag-of-words model).在词袋中,每个单词可以出现多次,而词集中,每个词智能出现一次。 我们对代码做些修改,使分类器工作得更好。 ''' 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) > 2] def spamTest(): docList=[]; classList=[]; fullText=[] #导入并解析文本文件 for i in range(1,26): ''' #原始文件email/ham/6.txt及email/spam/17.txt有个字符存在乱码,删除乱码就好了 try: wordList=textParse(open('email/spam/%d.txt' % i,encoding='UTF-8' ).read()) except UnicodeDecodeError: print(i) ''' 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 = list(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(setOfWords2Vec(vocabList, docList[docIndex])) trainClasses.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 ('the error rate is:',float(errorCount)/len(testSet)) if __name__ == '__main__': ''' #从文本中构建词向量 listOPosts,listClasses = loadDataSet() myVocabList = createVocabList(listOPosts) print(myVocabList) retVec0 = setOfWords2Vec(myVocabList, listOPosts[0]) print (retVec0) ''' ''' #从词向量计算概率 listOPosts,listClasses = loadDataSet() myVocabList = createVocabList(listOPosts) trainMat=[] for postinDoc in listOPosts: trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) p0V,p1V,pAb=trainNB0(trainMat,listClasses) print('词表:',myVocabList) print('正常评论各词出现的频率:',p0V) print('侮辱性评论各词出现的频率:',p1V) print('侮辱类评论出现的概率:',pAb) ''' #示例1.测试 恶意留言 分类器效果 #testingNB() 示例2.测试 垃圾邮件 分类效果 #spamTest() ''' 对于分类而言,使用概率有时比使用硬规则更为有效。贝叶斯概率及贝叶斯准则提供了一种利用已知值来 估计未知概率的有效方法。 可以通过特征之间的条件独立性假设,降低对数据量的需求。独立性假设是指一个词的出现概率并不依赖于 文档中的其他词。当然我们也知道这种假设过于简单。这就是之所以称为朴素贝叶斯的原因。尽管条件独立 性假设并不正确,但是朴素贝叶斯任然是一种有效的分类器。 '''
邮件训练测试链接:https://pan.baidu.com/s/1R8tXk4E_1RzoGowzZzKJ2g
提取码:af2o