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 is abusive, 0 not return postingList,classVec
2、去除训练数据集的重复词,并且添加到一个列表中
#处理数据集,去除所有分词向量的重复词,添加到一个列表中 #先创建一个集合筛选出不重复的词向量,然后转为列表 def createVocabList(dataSet): #这里的集合里面也是列表 vocabSet = set([]) #create empty set for document in dataSet: #利用并集将每个向量添加到set集合中 vocabSet = vocabSet | set(document) #union of the two sets return list(vocabSet)
3、将输入文本转化为0/1向量
#统计待分类的词向量在词列表中是否出现 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
4、计算输入文档属于侮辱性文档的概率p(ci),以及在已知该文档属于侮辱性文档的情况下,计算每个单词属于侮辱性的概率P(w/ci)
#朴素贝叶斯分类器训练函数,根据训练数据集训练得到分类器 #trainMatrix 所有文档的词条向量 #trainCategory 每篇文档对应的类别标签向量组成的矩阵 def trainNB0(trainMatrix, trainCategory): #文档数目 numTrainDocs = len(trainMatrix) #每篇文档的单词数 numWords = len(trainMatrix[0]) #侮辱性文档占总文档数的比例(因为侮辱性文档标签为1,加起来就是文档总数) pAbusive = sum(trainCategory) / float(numTrainDocs) p0Num = zeros(numWords) p1Num = zeros(numWords) # change to ones() p0Denom = 0.0 p1Denom = 0.0 # change to 2.0 for i in range(numTrainDocs): #如果该文档属于类别1 if trainCategory[i] == 1: #将该文档的词向量添加到向量p1Num中 #p1Num向量为1行numWords列 #最后p1Num为所有属于类别1的所有文档的词向量之和 #因为这里的输入文本trainMatrix[i]已经转换为了0/1向量 #eg:[0,0,0,0,0,0,0,0] + [1,0,1,1,0,0,0,0]+[1,1,1,1,0,0,0,0]+...... #这里的p1Num为已知文档类型,求各个单词在词库中出现的总数 p1Num += trainMatrix[i] #侮辱类的总单词数 p1Denom += sum(trainMatrix[i]) else: #否则如果是类别0,则将该文档的词向量添加到向量p0Num p0Num += trainMatrix[i] #然后将该文档的所有词向量加起来给p0Num p0Denom += sum(trainMatrix[i]) #p1Vect也是一个向量 p1Vect = p1Num / p1Denom # change to log() p0Vect = p0Num / p0Denom # change to log() #pAbusive 侮辱性文档占所有文档数的概率 #p0Vect 已知该文档属于侮辱性文档的情况下,词汇表中的每个单词属于侮辱性的概率 #p1Vect 已知该文档属于侮辱性文档的情况下,词汇表中的每个单词不属于侮辱性的概率 return p0Vect, p1Vect, pAbusive
5、针对第四步需要对两个地方进行优化
p0Num = ones(numWords) p1Num = ones(numWords) # change to ones() p0Denom = 2.0 p1Denom = 2.0 # change to 2.0采用拉普拉斯平滑,在分子上添加a(一般为1),分母上添加ka(k表示类别总数),即在这里将所有词的出现数初始化为1,并将分母初始化为2*1=2
p1Vect = log(p1Num / p1Denom) # change to log() p0Vect = log(p0Num / p0Denom) # change to log()由于有的单词出现的概率很小,导致乘积在四舍五入的情况下导致下溢出
6、根据trainNB0()函数返回的p(wi|c0),p(wi/c1),p(c1) 判断待分类文档是否属于污蔑行文档
#@vec2Classify:待测试分类的词条向量 #@p0Vec:类别0所有文档中各个词条出现的频数p(wi|c0) #@p1Vec:类别1所有文档中各个词条出现的频数p(wi|c1) #@pClass1:类别为1的文档占文档总数比例p(c1) def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): # 根据朴素贝叶斯分类函数分别计算待分类文档属于类1和类0的概率 #vec2Classify * p1Vec得到的是p(w/ci) #由于贝叶斯p(w/c)=p(w1,w2,w3..../c1)=p(w1)/p(c1) * p(w2)/p(c1) * ..... #由于这里的每一项p(w1)/p(c1)是一个对数,所以最后需要加起来 #最后由公式:p(w/c)*p(c) 还需要乘以p(c1) 对于对数而言,就是相加 #最后根据公式p(c/w) = p(w/c)*p(c) / p(w) #p1就是该文档属于污蔑性文档的概率 p1 = sum(vec2Classify * p1Vec) + log(pClass1) # element-wise mult #p0就是该文档不属于污蔑行文档的概率 p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) if p1 > p0: return 1 else: return 0
7、封装函数的所有操作,便于分类测试
def testingNB(): #获取训练数据集的文档矩阵,和类标签矩阵 listOPosts, listClasses = loadDataSet() #将文档矩阵转换为一个不重复的列表 myVocabList = createVocabList(listOPosts) trainMat = [] for postinDoc in listOPosts: #根据postinDoc是否在myVocabList,将postinDoc转换为0/1向量 trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) #将文档矩阵和类标签向量转为数组,利用trainNB0()得到 '''#@p0Vec:类别0所有文档中各个词条出现的频数p(wi|c0) #@p1Vec:类别1所有文档中各个词条出现的频数p(wi|c1) #@pClass1:类别为1的文档占文档总数比例p(c1)''' p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses)) #测试文档 testEntry = ['love', 'my', 'dalmation'] #将测试文档转换为0/1向量矩阵,并且转为数组的形式 thisDoc = array(setOfWords2Vec(myVocabList, testEntry)) #对测试文档进行分类 print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb)) #第二个测试文档 testEntry = ['stupid', 'garbage'] #转换为0/1词条向量 thisDoc = array(setOfWords2Vec(myVocabList, testEntry)) print (testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
8、优化特征的标准:从根据每个词是否出现为特征(文档词集模型)转为---》统计每个词出现的次数为特征
#文档词袋模型,从根据每个词是否出现为特征(文档词集模型)转为---》统计每个词出现的次数为特征 def bagOfWords2VecMN(vocabList, inputSet): #建立一个和词集列表等长的行向量,初始化每个词的出现次数为0 returnVec = [0] * len(vocabList) #遍历输入集统计输入集中的单词在词列表中出现的次数 for word in inputSet: if word in vocabList: #vocabList.index(word)返回word在vocablist的索引值 returnVec[vocabList.index(word)] += 1 return returnVec
为此朴素贝叶斯分类器就完全实现了
接下来利用我们构建的分类器过滤垃圾邮件
9、准备数据,切分文本
def textParse(bigString): # input is big string, #output is word list import re #这里的r表示原生字符串 分隔规则:匹配任意字母数字下划线 进行分隔 listOfTokens = re.split(r'\W*', bigString) #选出长度大于2的字符串并且转化为小写 return [tok.lower() for tok in listOfTokens if len(tok) > 2]
10、垃圾邮件测试函数
def spamTest(): docList = []; classList = []; fullText = [] for i in range(1, 26): #依次打开spam目录下的每个txt文件,并且读取内容 #按照分隔规则,将文本进行切割,得到一个列表 wordList = textParse(open('email/spam/%d.txt' % i).read()) #将每个切割后的文本作为一个整体的列表添加到doclist列表里面 docList.append(wordList) #将所有切割后的文本添加具体的元素内容到fullText fullText.extend(wordList) #标签列表添加标签1 classList.append(1) #打开ham文件夹下的所有txt文件,读取内容 #进行切割文本 wordList = textParse(open('email/ham/%d.txt' % i).read()) #将每个文本作为整体加入到wordlist docList.append(wordList) #将每个文本的内容加入到wordlist fullText.extend(wordList) #标签列表添加标签0 classList.append(0) #处理数据集doclist,去除重复,并且将所有的文本单词向量添加到一个列表 vocabList = createVocabList(docList) # create vocabulary #0-49 trainingSet = range(50); testSet = [] # create test set #0-9 #从trainingset集合里面随机选择10个数添加到testset列表里面 for i in range(10): #uniform() 方法将随机生成下一个实数,它在 [x, y) 范围内。 #random.uniform()返回一个浮点数 #获取随机下标 randIndex = int(random.uniform(0, len(trainingSet))) testSet.append(trainingSet[randIndex]) #删除选出的随机数,防止下次再次选出 del (trainingSet[randIndex]) trainMat = []; trainClasses = [] #遍历剩下的trainingSet的40个整数 for docIndex in trainingSet: # train the classifier (get probs) trainNB0 #遍历doclist的40个文本,统计他们在词典vocabList每个单词出现的次数 #将每个文本对应的词典次数向量添加到trainmat trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) #将40个文本对应标签也添加到trainClasses标签列表里面 trainClasses.append(classList[docIndex]) #将所有文本对应的词袋向量以及标签矩阵转为数组计算概率 p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses)) errorCount = 0 #遍历选出的10个测试整数 for docIndex in testSet: # classify the remaining items #同样的将对应的文本添转为词袋向量 wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) #利用测试集进行分类 if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: errorCount += 1 print ("classification error", docList[docIndex]) print ('the error rate is: ', float(errorCount) / len(testSet)) # return vocabList,fullText
到这里垃圾邮件的过滤也已经完成了
下面展示贝叶斯另外一个功能根据个人广告获取区域倾向
1、统计高频词
#统计高频词 def calcMostFreq(vocabList,fullText): import operator #创建一个空字典,用于装取词典里面每个单词在fulltext里面出现的次数 freqDict = {} for token in vocabList: #直接使用count()函数统计文本fulltext中具体单词出现的次数 freqDict[token]=fullText.count(token) #freqDict.iteritems()返回一个迭代器 #key=operator.itemgetter(1)根据字典的第二个域排序:也就是单词出现的次数 #参考博客:http://blog.csdn.net/dongtingzhizi/article/details/12068205 '''orted(iterable[, cmp[, key[, reverse]]]) 参数解释: (1)iterable指定要排序的list或者iterable,不用多说; (2)cmp为函数,指定排序时进行比较的函数,可以指定一个函数或者lambda函数 reverse参数就不用多说了,是一个bool变量,表示升序还是降序排列,默认为false(升序排列),定义为True时''' sortedFreq = sorted(freqDict.iteritems(), key=operator.itemgetter(1), reverse=True) #返回前30个单词 return sortedFreq[:30]