机器学习实战:朴素贝叶斯分类(二)

来源:互联网 发布:童装淘宝店铺名字大全 编辑:程序博客网 时间:2024/06/11 04:46

使用朴素贝叶斯过滤垃圾邮件

(1)收集数据:提供文本文件。

(2)准备数据:将文本文件解析成词条向量。

(3)分析数据:检查词条确保解析的正确性。

(4)训练算法:使用我们之前建立的trainNBayes0函数

(5)测试算法:使用classifyNB(),并且构建一个新的测试函数来计算文档集的错误率。

(6)使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上。


1、准备数据:切分文本

之前,词向量是预先给定好的,下面来看一下如何从文本中构建自己的词列表。

(1)可以使用string.split()方法对英文文本进行分割,默认分割符为空字符

mySentence="This book is the best book on Python or M.L. I have ever laid eyes upon."print(mySentence.split())
结果:

['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', 'or', 'M.L.', 'I', 'have', 'ever', 'laid', 'eyes', 'upon.']

(2)由于根据空字符分割,标点符号也被当成了词的一部分,接下来可以使用正则表达式,将分割符设为除单词数字外的任意字符串

# -*- coding:utf-8 -*-import re#正则表达式regEx=re.compile('\\W*')listOfTokens=regEx.split(mySentence)print(listOfTokens)
正则表达式'\\W*',第一个\表示转义字符,\W表示匹配任意不是字母、数字、下划线、汉字的字符,*表示前面的字符可以出现零次或多次

正则表达式教程:https://deerchao.net/tutorials/regex/regex.htm

结果:

['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', 'or', 'M', 'L', 'I', 'have', 'ever', 'laid', 'eyes', 'upon', '']

(3)现在得到了一个句子分割后的词表,但是里面包含空字符串,需要将空字符串去掉

listOfTokens=[tok for tok in listOfTokens if len(tok)>0]#只取字符串长度大于1的单词print(listOfTokens)
结果:
['This', 'book', 'is', 'the', 'best', 'book', 'on', 'Python', 'or', 'M', 'L', 'I', 'have', 'ever', 'laid', 'eyes', 'upon']

(4)使用lower()方法将大写字母转为小写

listOfTokens=[tok.lower() for tok in listOfTokens if len(tok)>0]#只取字符串长度大于1的单词print(listOfTokens)
['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python', 'or', 'm', 'l', 'i', 'have', 'ever', 'laid', 'eyes', 'upon']


2、测试算法:使用朴素贝叶斯进行交叉验证

(1)处理文本

#处理文本,将文本分割为词集,并取长度大于1的单词def textParse(bigString):listOfTokens=re.split(r'\W*',bigString)return [tok.lower() for tok in listOfTokens if len(tok)>2]

(2)测试

1)总共有50封邮件,分别在spam和ham文件夹中,每个文件夹中有25封邮件,spam文件夹中分类类别设为1,ham文件夹中的邮件分类为0

2)读取每个文件夹中的文件,并对文本分割,在读取文件的时候遇到了一个编码问题,最后发现给的数据中,有个别文件的编码格式如下,导致utf-8读取出现错误,然后将有问题的文件编码转为了utf-8,才成功的读取了文件。

Non-ISO extended-ASCII text, with CRLF line terminators

3)从50封邮件中选取10封作为测试集,40封作为训练集

4)使用之前建立的朴素贝叶斯分类函数进行测试

def spamTest():docList=[]#以文档为单位,所有文档组成的词集classList=[]#分类集fullText=[]#所有邮件分割后的文本组成的词集#打开所有的文件,获取邮件数据并将文本分割为词集for i in range(1,26):#从spam文件夹下读取文件wordList=textParse(open('/Users/shan/Documents/python/bayes/data/email/spam/%d.txt' % i,'r',encoding='utf-8').read())docList.append(wordList)#向文档词列表中添加每篇文档分割后的词集,以文档为单位,每篇文档占一个索引[['a',...],['b...'],...]fullText.extend(wordList)#向全文本词集中添加词集,['a',...,'b',...]classList.append(1)#添加分类#从ham文件夹下读取文件wordList=textParse(open('/Users/shan/Documents/python/bayes/data/email/ham/%d.txt' % i,'r',encoding='utf-8').read())docList.append(wordList)fullText.extend(wordList)classList.append(0)#创建词汇列表vocabList=createVocabList(docList)#训练集,总共50封邮件traningSet=list(range(50))print(traningSet)#测试集testSet=[]#从训练集中随机选取十封邮件作为测试集,然后删除for i in range(10):randIndex=int(random.uniform(0,len(traningSet)))testSet.append(traningSet[randIndex])del(traningSet[randIndex])#从训练集中删除用来测试的邮件trainMat=[]trainClasses=[]#从训练集中获取每篇文档的词向量并放入trainMat中,获取每篇文档的分类,放入trainClasses中for docIndex in traningSet:trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))trainClasses.append(classList[docIndex])#调用贝叶斯分类训练函数p0V,p1V,pSpam=trainNBayes0(array(trainMat),array(trainClasses))#错误率errorCount=0#从测试集中进行测试for docIndex in testSet:wordVector=setOfWords2Vec(vocabList,docList[docIndex])if(clssifyNBayes(array(wordVector),p0V,p1V,pSpam))!=classList[docIndex]:errorCount+=1print('错误率为:')print(float(errorCount)/len(testSet))

输出结果:

错误率为:0.0

由于每次测试集是随机选取,所以导致每次结果可能不一样,再次运行程序:

错误率为:0.1


python小知识:

(1)range和del

myList=list(range(0,10))#生成长度为10的列表print(myList)del(myList[5])#删除索引为5的元素print(myList)
输出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][0, 1, 2, 3, 4, 6, 7, 8, 9]

(2)append和extend

myList1=[1,2,3]myList2=[4,5,6]myList1.append(myList2)print(myList1)
append结果:

[1, 2, 3, [4, 5, 6]]

myList1=[1,2,3]myList2=[4,5,6]myList1.extend(myList2)print(myList1)
extend结果:
[1, 2, 3, 4, 5, 6]



完整代码:

# -*- coding:utf-8 -*-from numpy import *import re#加载数据def loadDataSet():#留言列表postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],#留言1['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],#留言2,带有stupid愚蠢的词语['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],#留言3['stop', 'posting', 'stupid', 'worthless', 'garbage'],#留言4,带有stupid,garbage垃圾等词语['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],#留言5['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]#留言6,带有stupid#类别标签集合,1代表带有侮辱性的评论,0代表正常言论classVec=[0,1,0,1,0,1]return postingList,classVec#创建一个不重复词汇列表,列表中的内容为所有留言中出现的单词def  createVocabList(dataset):vocabSet=set([])#创建一个空的集合#遍历数据集,将每条留言中单词放入vocabsetfor document in dataset:vocabSet=vocabSet|set(document)#操作费|用于求两个集合的并集return list(vocabSet)#将输入文档转为词向量def setOfWords2Vec(vocabList,inputSet):returnVec=[0]*len(vocabList)#创建一个和词汇表等长的向量,并初始化为0#遍历输入文档中的所有单词,如果词汇列表中已经含有该单词,将输出的文档向量中对应值设为1for word in inputSet:if word in vocabList:returnVec[vocabList.index(word)]=1else:print("the word:%s is not in my vocabulary"% word)return returnVec#朴素贝叶斯分类训练函数def trainNBayes0(trainMatrix,trainCategory):numTrainDocs=len(trainMatrix)#文档数 6numWords=len(trainMatrix[0])#获取trainMatrix中第一个元素长度,也就是词汇表中的总数 32#类别1的概率pAbusive=sum(trainCategory)/float(numTrainDocs)#类别标签集合中的总和(类别只有0和1,因此总和=类别为1的总文档数)/文档总数p0Num=ones(numWords)#创建numWords行(32)的矩阵,记录类别0中每个单词出现的次数,并初始化为1p1Num=ones(numWords)#创建numWords行(32)的矩阵,记录类别1中每个单词出现的次数,并初始化为1p0Denom=2.0#初始化为2p1Denom=2.0#初始化为2for i in range(numTrainDocs):#如果是类别1的文档if(trainCategory[i])==1:p1Num+=trainMatrix[i]#每个单词出现的次数p1Denom+=sum(trainMatrix[i])#所有单词出现的次数之和else:p0Num+=trainMatrix[i]p0Denom+=sum(trainMatrix[i])#求条件概率P(单词|文档类别)=单词出现次数/所有单词出现次数之和p1Vect=log(p1Num/p1Denom) p0Vect=log(p0Num/p0Denom)return p0Vect,p1Vect,pAbusive#朴素贝叶斯分类函数(待测试文档的词向量,类别0中所有单词的条件概率矩阵,类别1中所有单词的条件概率矩阵,类别1的概率)def clssifyNBayes(words2Vec,p0Vect,p1Vect,pClassify1):p1=sum(words2Vec*p1Vect)+log(pClassify1)p0=sum(words2Vec*p0Vect)+log(1.0-pClassify1)if p1>p0:return 1else:return 0#处理文本,将文本分割为词集,并取长度大于1的单词def textParse(bigString):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):#从spam文件夹下读取文件wordList=textParse(open('/Users/shan/Documents/python/bayes/data/email/spam/%d.txt' % i,'r',encoding='utf-8').read())docList.append(wordList)#向文档词列表中添加每篇文档分割后的词集,以文档为单位,每篇文档占一个索引[['a',...],['b...'],...]fullText.extend(wordList)#向全文本词集中添加词集,以单词为单位,每个单词占一个索引['a',...,'b',...]classList.append(1)#添加分类#从ham文件夹下读取文件wordList=textParse(open('/Users/shan/Documents/python/bayes/data/email/ham/%d.txt' % i,'r',encoding='utf-8').read())docList.append(wordList)fullText.extend(wordList)classList.append(0)#创建词汇列表vocabList=createVocabList(docList)#训练集,总共50封邮件,创建一个长度为50的列表traningSet=list(range(50))print(traningSet)#测试集testSet=[]#从训练集中随机选取十封邮件作为测试集,然后删除for i in range(10):randIndex=int(random.uniform(0,len(traningSet)))#随机生成0到50间的数字testSet.append(traningSet[randIndex])#记录用来测试的邮件的索引del(traningSet[randIndex])#从训练集中删除用来测试的邮件索引trainMat=[]trainClasses=[]#从训练集中获取每篇文档的词向量并放入trainMat中,获取每篇文档的分类,放入trainClasses中for docIndex in traningSet:trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))trainClasses.append(classList[docIndex])#调用贝叶斯分类训练函数p0V,p1V,pSpam=trainNBayes0(array(trainMat),array(trainClasses))#错误率errorCount=0#从测试集中进行测试for docIndex in testSet:wordVector=setOfWords2Vec(vocabList,docList[docIndex])if(clssifyNBayes(array(wordVector),p0V,p1V,pSpam))!=classList[docIndex]:errorCount+=1print('错误率为:')print(float(errorCount)/len(testSet))spamTest() 




阅读全文
0 0