本系列以书中源码为主,稍作修改并添加注释,均实际运行可行。为免后来者踩坑,特此公开!欢迎打赏!
转载请注明出处!
from numpy import *
import re
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表示含有侮辱类单词
return postingList,classVec
def createVocabList(dataSet): #创建词表向量
vocabSet = set([]) #初始化空集
for document in dataSet:
vocabSet =vocabSet | set(document)#将所有文档中的不重复词并入
return list(vocabSet)#列表化
def bagOfWords2VecMN(vocabList,inputSet): #将输入文档按照词表向量向量化。参数:词汇表,某文档。改进:词集模型改为词袋模型
returnVec = [0]*len(vocabList)#创建长度为词汇表的列表,并置0
for word in inputSet:#判断输入文档是否出现在词表向量中,若有则在相应位置置1,若无则返回提示语
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
def trainNB0(trainMatrix,trainCategory):#朴素贝叶斯分类器训练函数。参数:1:向量化文档2:词条向量
numTrainDocs = len(trainMatrix)#获取文档矩阵中文档的数目
numWords = len(trainMatrix[0])#获取词条向量的长度
pAbusive = sum(trainCategory)/float(numTrainDocs)#计算文档属于侮辱性文档的概率
p0Num = ones(numWords); p1Num = ones(numWords)#创建两个长度为词条向量等长的列表,平滑处理:初始值设为1
p0Denom = 2.0;p1Denom = 2.0#平滑处理,初始值设为2
for i in range(numTrainDocs):
if trainCategory[i] ==1:#如果该词条向量对应的标签为1
p1Num += trainMatrix[i]#统计所有类别为1的词条向量中各个词条出现的次数
p1Denom += sum(trainMatrix[i])#统计类别为1的词条向量中出现的所有词条的总数
else:
p0Num += trainMatrix[i]#同上
p0Denom += sum(trainMatrix[i])
p1Vect = log(p1Num/p1Denom)#利用Numpy数组计算p(wi/c1),即类1条件下各词条出现的概率
p0Vect = log(p0Num/p0Denom)#利用Numpy数组计算p(wi/c0),为避免下溢,后面会改为log()
return p0Vect,p1Vect,pAbusive#返回
#朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):#注意参数2,3均已log化
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))
#文件解析及完整的垃圾邮件测试函数
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):
#打开并读取指定目录下的文本并进行数据清洗
wordList = textParse(open(r'选择你自己的数据集存储路径\spam\%d.txt' % i).read())
docList.append(wordList)#保存得到的字符串列表
fullText.extend(wordList)#保存得到的全部字符串
classList.append(1)#类列表添加标签1
#打开并取得另外一个类别为0的文件,然后进行处理
wordList = textParse(open(r'选择你自己的数据集存储路径\ham\%d.txt' % i,encoding='gb18030',errors='ignore').read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)#将所有邮件中出现的字符串构建成字符串列表,即词表向量
trainingSet = list(range(50)); testSet=[] #构建一个大小为50的整数列表和一个空列表
for i in range(10):#随机选取1-50中的10个数,作为索引,构建测试集
randIndex = int(random.uniform(0,len(trainingSet)))#随即选取1-50中的一个整型数
testSet.append(trainingSet[randIndex])#将选出的数的列表索引值添加到testSet列表中
del(trainingSet[randIndex]) #从整数列表中删除选出的数,防止下次再次选出。同时将剩下的作为训练集
trainMat=[]; trainClasses = []#新建两个列表
for docIndex in trainingSet:#遍历训练集中的每个字符串列表
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))#将字符串列表转为词条向量,然后添加到训练矩阵中
trainClasses.append(classList[docIndex])#将该邮件的类标签存入训练类标签列表中
p0V,p1V,pSpam = trainNB0(array(trainMat),array(trainClasses))#计算贝叶斯函数需要d阿概率值并返回
errorCount = 0
for docIndex in testSet: #遍历测试集中的字符串列表
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
spamTest()