主要内容:
一.利用朴素贝叶斯进行文档分类
二.利用朴素贝叶斯进行垃圾邮件过滤
(这一章忍不住偷懒了,只做了代码的注释,羞愧羞愧,大懒鬼!)
一.利用朴素贝叶斯进行文档分类
1 # coding:utf-8 2 ''' 3 Created on Oct 19, 2010 4 @author: Peter 5 ''' 6 from numpy import * 7 8 9 '''创造一些数据''' 10 def loadDataSet(): 11 postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], 12 ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'], 13 ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'], 14 ['stop', 'posting', 'stupid', 'worthless', 'garbage'], 15 ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], 16 ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']] 17 classVec = [0, 1, 0, 1, 0, 1] # 相应的标签,1是带有侮辱性的,0不带侮辱性 18 return postingList, classVec 19 20 '''创建单词表,先用set去重,然后再以list的形式返回''' 21 def createVocabList(dataSet): 22 vocabSet = set([]) # create empty set 23 for document in dataSet: 24 vocabSet = vocabSet | set(document) # union of the two sets 25 return list(vocabSet) 26 27 '''返回一个01列表,表示单词表中的某个单词是否出现在inputSet中''' 28 def setOfWords2Vec(vocabList, inputSet): 29 returnVec = [0] * len(vocabList) 30 for word in inputSet: 31 if word in vocabList: 32 returnVec[vocabList.index(word)] = 1 33 else: 34 print "the word: %s is not in my Vocabulary!" % word 35 return returnVec 36 37 '''文档词袋模型,即把原来“单词是否出现在文档上”改为“单词在文档上出现了几次”''' 38 def bagOfWords2VecMN(vocabList, inputSet): 39 returnVec = [0] * len(vocabList) 40 for word in inputSet: 41 if word in vocabList: 42 returnVec[vocabList.index(word)] += 1 #把原先的 “=1”改为“+=1” 43 return returnVec 44 45 46 '''求出P(c1)、P(w|c1)、P(w|c0). 当P(c1)求出时,P(c0) = 1-P(c1)''' 47 '''其中P(w|c1)是一个列表,等于[P(w1|c1),P(w2|c1),P(w3|c1)……],P(w|c0)也是一个列表;P(c0)为一个实数''' 48 def trainNB0(trainMatrix, trainCategory): 49 numTrainDocs = len(trainMatrix) #训练数据集的大小,即第一维 50 numWords = len(trainMatrix[0]) #一条训练数据集的特征数,也就是单词表的长度,即第二维 51 pAbusive = sum(trainCategory) / float(numTrainDocs) #P(c1) 52 ''' 53 以下是计算P(w|c1)、P(w|c0)的初始化阶段。 54 在计算P(w1|c1)*P(w2|c0)*……时,如果其中一个为0,那么结果就为0,为了降低这种影响, 55 可将所有单词的出现次数初始化为1,并将坟墓初始化为2 56 ''' 57 p0Num = ones(numWords) 58 p1Num = ones(numWords) # change to ones() 59 p0Denom = 2.0 60 p1Denom = 2.0 # change to 2.0 61 '''''' 62 for i in range(numTrainDocs): 63 if trainCategory[i] == 1: 64 p1Num += trainMatrix[i] 65 p1Denom += sum(trainMatrix[i]) 66 else: 67 p0Num += trainMatrix[i] 68 p0Denom += sum(trainMatrix[i]) 69 ''' 70 以下是计算P(w|c1)、P(w|c0)的最终部分。 71 在计算P(w1|c1)*P(w2|c0)*……时,由于太多很小的数相乘,很容易造成下溢,当四舍五入时结果就很可能为0, 72 解决办法是对乘积取对数,就有:ln(ab) = lna+lnb把“小数相乘”转化为小数相加,避免了下溢。 73 由于x与lnx在x>0处有相同的增减性,两者的最值不相同,但最值点是相同的,所以不会影响最终的分类。 74 ''' 75 p1Vect = log(p1Num / p1Denom) # P(w|c1) 76 p0Vect = log(p0Num / p0Denom) # P(w|c0) 77 '''''' 78 return p0Vect, p1Vect, pAbusive 79 80 '''利用朴素贝叶斯进行分类,其中vec2Classify为输入数据,他是有关单词表的01串, 81 p0Vec, p1Vec, pClass1则为trainNB0()的3个返回值,即P(w|c0)、P(w|c1)、P(c1)''' 82 def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): 83 ''' 84 vec2Classify * p1Vec是什么意思? 85 可知p1Vec为训练数据集中的P(w|c1),它是一个列表(对应着单词表)。在计算P(w|c1)时,每一个单词的概率即P(wi|c1)都计算了, 86 但对于输入数据而言,有些单词出现了,有些单词没有出现,而我们只需要计算基础了的单词,所以要乘上系数ec2Classify 87 ''' 88 ''' 89 sum(vec2Classify * p1Vec)计算的是P(w|c1),log(pClass1)计算的是P(c1) 90 按照贝叶斯公式,理应还要减去P(w)的那一部分。但由于p1、p0都需要减去这一部分的值, 91 且我们只需要比较p1、p0的大小而不需求求出具体的值,所以可以省去这一部分的计算 92 ''' 93 p1 = sum(vec2Classify * p1Vec) + log(pClass1) # element-wise mult 94 p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) 95 if p1 > p0: 96 return 1 97 else: 98 return 0 99 100 101 def testingNB(): 102 listOPosts, listClasses = loadDataSet() #提取数据 103 myVocabList = createVocabList(listOPosts) 104 trainMat = [] 105 for postinDoc in listOPosts: #数据加工,trainMat、listClasses为训练数据集,其中trainMat为特征X,listClasses为标签Y 106 trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) 107 p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses)) #计算出概率P(c1)、P(w|c1)、P(w|c0) 108 testEntry = ['love', 'my', 'dalmation'] #测试数据 109 thisDoc = array(setOfWords2Vec(myVocabList, testEntry)) 110 print testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb) 111 testEntry = ['stupid', 'garbage'] 112 thisDoc = array(setOfWords2Vec(myVocabList, testEntry)) 113 print testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb) 114 115 testingNB()
三.利用朴素贝叶斯进行垃圾邮件过滤
1 # coding:utf-8 2 3 from numpy import * 4 5 '''创建单词表,先用set去重,然后再以list的形式返回''' 6 def createVocabList(dataSet): 7 vocabSet = set([]) # create empty set 8 for document in dataSet: 9 vocabSet = vocabSet | set(document) # union of the two sets 10 return list(vocabSet) 11 12 '''文档词袋模型,即把原来“单词是否出现在文档上”改为“单词在文档上出现了几次”''' 13 def bagOfWords2VecMN(vocabList, inputSet): 14 returnVec = [0] * len(vocabList) 15 for word in inputSet: 16 if word in vocabList: 17 returnVec[vocabList.index(word)] += 1 #把原先的 “=1”改为“+=1” 18 return returnVec 19 20 '''求出P(c1)、P(w|c1)、P(w|c0). 当P(c1)求出时,P(c0) = 1-P(c1)''' 21 '''其中P(w|c1)是一个列表,等于[P(w1|c1),P(w2|c1),P(w3|c1)……],P(w|c0)也是一个列表;P(c0)为一个实数''' 22 def trainNB0(trainMatrix, trainCategory): 23 numTrainDocs = len(trainMatrix) #训练数据集的大小,即第一维 24 numWords = len(trainMatrix[0]) #一条训练数据集的特征数,也就是单词表的长度,即第二维 25 pAbusive = sum(trainCategory) / float(numTrainDocs) #P(c1) 26 ''' 27 以下是计算P(w|c1)、P(w|c0)的初始化阶段。 28 在计算P(w1|c1)*P(w2|c0)*……时,如果其中一个为0,那么结果就为0,为了降低这种影响, 29 可将所有单词的出现次数初始化为1,并将坟墓初始化为2 30 ''' 31 p0Num = ones(numWords) 32 p1Num = ones(numWords) # change to ones() 33 p0Denom = 2.0 34 p1Denom = 2.0 # change to 2.0 35 '''''' 36 for i in range(numTrainDocs): 37 if trainCategory[i] == 1: 38 p1Num += trainMatrix[i] 39 p1Denom += sum(trainMatrix[i]) 40 else: 41 p0Num += trainMatrix[i] 42 p0Denom += sum(trainMatrix[i]) 43 ''' 44 以下是计算P(w|c1)、P(w|c0)的最终部分。 45 在计算P(w1|c1)*P(w2|c0)*……时,由于太多很小的数相乘,很容易造成下溢,当四舍五入时结果就很可能为0, 46 解决办法是对乘积取对数,就有:ln(ab) = lna+lnb把“小数相乘”转化为小数相加,避免了下溢。 47 由于x与lnx在x>0处有相同的增减性,两者的最值不相同,但最值点是相同的,所以不会影响最终的分类。 48 ''' 49 p1Vect = log(p1Num / p1Denom) # P(w|c1) 50 p0Vect = log(p0Num / p0Denom) # P(w|c0) 51 '''''' 52 return p0Vect, p1Vect, pAbusive 53 54 '''利用朴素贝叶斯进行分类,其中vec2Classify为输入数据,他是有关单词表的01串, 55 p0Vec, p1Vec, pClass1则为trainNB0()的3个返回值,即P(w|c0)、P(w|c1)、P(c1)''' 56 def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): 57 ''' 58 vec2Classify * p1Vec是什么意思? 59 可知p1Vec为训练数据集中的P(w|c1),它是一个列表(对应着单词表)。在计算P(w|c1)时,每一个单词的概率即P(wi|c1)都计算了, 60 但对于输入数据而言,有些单词出现了,有些单词没有出现,而我们只需要计算基础了的单词,所以要乘上系数ec2Classify 61 ''' 62 ''' 63 sum(vec2Classify * p1Vec)计算的是P(w|c1),log(pClass1)计算的是P(c1) 64 按照贝叶斯公式,理应还要减去P(w)的那一部分。但由于p1、p0都需要减去这一部分的值, 65 且我们只需要比较p1、p0的大小而不需求求出具体的值,所以可以省去这一部分的计算 66 ''' 67 p1 = sum(vec2Classify * p1Vec) + log(pClass1) # element-wise mult 68 p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) 69 if p1 > p0: 70 return 1 71 else: 72 return 0 73 74 def textParse(bigString): # 提取、处理、过滤单词 75 import re 76 listOfTokens = re.split(r'\W*', bigString) 77 return [tok.lower() for tok in listOfTokens if len(tok) > 2] 78 79 def spamTest(): 80 docList = [] 81 classList = [] 82 fullText = [] 83 for i in range(1, 26): 84 wordList = textParse(open('email/spam/%d.txt' % i).read()) 85 docList.append(wordList) 86 fullText.extend(wordList) 87 classList.append(1) 88 wordList = textParse(open('email/ham/%d.txt' % i).read()) 89 docList.append(wordList) 90 fullText.extend(wordList) 91 classList.append(0) 92 vocabList = createVocabList(docList) # 创建单词表 93 trainingSet = range(50) 94 testSet = [] # 从50条数据中随机选出10个作为测试数据,trainingSet和testSet存的都是下标,对应在docList和classList上 95 for i in range(10): 96 randIndex = int(random.uniform(0, len(trainingSet))) 97 testSet.append(trainingSet[randIndex]) 98 del (trainingSet[randIndex]) 99 trainMat = [] 100 trainClasses = [] 101 for docIndex in trainingSet: # 剩下的的都作为训练数据集 102 trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) 103 trainClasses.append(classList[docIndex]) 104 p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses)) #计算出概率P(c1)、P(w|c1)、P(w|c0) 105 errorCount = 0 106 for docIndex in testSet: # 进行测试 107 wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) 108 if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: 109 errorCount += 1 110 print "classification error", docList[docIndex] 111 print 'the error rate is: ', float(errorCount) / len(testSet) 112 # return vocabList,fullText 113 114 115 spamTest()