1.解析并切分文本文件
2.随机构建训练测试集
3.使用贝叶斯分类算法分类
4.验证结果错误率
贝叶斯算法在另一篇文章中注释详细有述
另,该文参照《机器学习实战》,在代码中可能由于版本原因书中正则表达式切分词时有错误,目前未解决
文中有一部分为测试问题的输出函数
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''=================================================
@Project -> File :bayes -> bayesOfEmail
@IDE :PyCharm
@Author :zgq
@Date :2021/1/10 20:14
@Desc :
=================================================='''
import re
from numpy import *
import random
#2、创建一个包含在所有句子中的不重复出现的词列表
def createVocabList(dataSet):
vocabSet=set([]) #创建一个set变量,她可以去掉重复的词
for document in dataSet:
vocabSet=vocabSet | set(document) #将词条列表输给set构造函数,set就回返回一个不重复的list
return list(vocabSet)
#3、将输入的一句话转换为词向量(one hot类型)
def setOfWords2Vec(vocabList,inputSet):
returnVec=[0]*len(vocabList) #先将这个返回的词向量初始化一个长度为onehot的向量
for word in inputSet:
#对于传入这句话的每个单词来判断是否有 有放1没有放o
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): #trainMatrix是一个矩阵是一个二维的,每一行是一个样本(由词向量组成的矩阵)
#此处是上边要传递的不是原先的都是单词的矩阵,而是one hot类型的矩阵
numTrainDocs=len(trainMatrix) #len(矩阵)返回行数
numWords=len(trainMatrix[0]) #trainMatrix返回矩阵一行的长度,即每条样本的属性数(词数)
pAbusive=sum(trainCategory)/float(numTrainDocs) #文档属于侮辱性类的概率,第一项为侮辱性类词向量次数、第二行为总样本数
p0Num=ones(numWords) #做了一个全为0的向量(0,0,0,0),长度为样本数
p1Num=ones(numWords)
p0Denom=2.0 #
p1Denom=2.0
for i in range(numTrainDocs): #遍历文本矩阵每一行,即遍历每一条样本
if trainCategory[i]==1:
p1Num=p1Num+trainMatrix[i] #如果该行种类为1的话,将这行词向量盖上到这个(0,0,0,0,0,0,0) 上去
#如果这句话是侮辱性的句子,将这句话的里边的所有词都放入P1num中,最后执行完毕后会是一个(3,1,0,11,15……),对应于我的词汇表中每个词出现的次数
p1Denom=p1Denom+sum(trainMatrix[i]) #加和该句话的总词数
else:
p0Num=p0Num+trainMatrix[i]
p0Denom=p0Denom+sum(trainMatrix[i])
#在上面的过程中,步骤为对句子构成的词向量构成的矩阵,对每一句话,如果这句话是侮辱性类别的,在我们词表中记录她出现了,出现一次次数加一
#最后得到的是 p1Num 所有的词在句子为侮辱性的情况下出现的次数。
#而对于P1Denom,对与每句属于类被1的句子,都将这句话的词数加进去,最后得到了,这个总的 矩阵中,语料中,是侮辱性的词语总数
#因为 p1Num为一个向量,记录了每个词在所有的侮辱性句子中出现的总次数,将其除以侮辱性句子总词数,获得在c概率下p(w|1)
p1Vect=log(p1Num/p1Denom) #词向量除以浮点数,结果为我们的词汇表中每一个词在侮辱性句子下出现的概率
p0Vect=log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
#利用词向量概率来构建贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1=sum(vec2Classify*p1Vec)+log(pClass1)
p0=sum(vec2Classify*p0Vec)+log(1-pClass1)
if p1>p0:
return 1
else:
return 0
#接受一个大字符串并将其解析为字符串列表
def textParse(bigString):
listOfTokens=re.split(r'\W*',bigString) #将字符串切分
return [tok.lower() for tok in listOfTokens if len(tok)>2] #去掉长度小于2的字符串,且转化为小写
#文件解析及完整的垃圾邮件测试函数
def spamTest():
docList=[]
classList=[]
fullText=[]
for i in range(1,26):
wordList=textParse(open('email/spam/%d.txt' % i).read()) #spam是正面的,ham是负面的
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)
#解析函数中输出什么?
print("解析函数中输出的wordlist:")
print(wordList)
print("解析函数中输出的docList:")
print(fullText)
print("输出classlist")
print(classList)
print("测试一下open。read函数")
test_open=open('email/spam/1.txt').read()
print(test_open)
print("测试parse函数")
print(textParse(test_open))
print("创建词汇表:")
vocabList=createVocabList(docList) #创建不重复的词汇表用于构建one hot 向量
print("随机选择10个作为测试集放到testSet中,并且将其从trainingSet中删除")#此处选择的只是它的标签(就是它是第几个)而没有选择了本身,最后的选择是一个index【1,5,18,26,3……】
trainingSet=list(range(50)) #range()函数产生一个从0到50的列表 即trainingSet=[0,1,2,3……,49,50]
testSet=[] #训练集为50里面不放回的随机选10个
for i in range(10):
randIndex=int(random.uniform(0,len(trainingSet))) #返回一个从0到len范围的随机数
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex]) #不放回的随机抽取,把当前这个序号抽出来之后,后面就不会抽到她了
print("对于选为训练集的每一个文本,都将其转化为onehot类型的词向量,最后构成一个矩阵")
trainMat=[]
trainClasses=[]
for docIndex in trainingSet:
trainMat.append(setOfWords2Vec(vocabList,docList[docIndex])) #这边的doclist是将邮件刚解析为list后的东西,需要将其转换为此词向量
trainClasses.append((classList[docIndex])) #将其对应的类别也安排上
#使用朴素贝叶斯分类器进行分类,此处是求出了两个向量,一个是基于one hot词汇表上每个词对应是好词或者是坏此的概率,最后要用这个概率来求最后的总概率
p0V,p1V,pSpam=trainNB0(array(trainMat),array(trainClasses))
#开始使用训练集进行测试
errorCount=0
for docIndex in testSet:
print("测试集",docIndex,":",docList[docIndex])
wordVector=setOfWords2Vec(vocabList,docList[docIndex])
print('the right sequence is: %d' %classList[docIndex],'and our predict sequence is: %d' % classifyNB(array(wordVector),p0V,p1V,pSpam))
if classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:
errorCount=errorCount+1
print('the error rate is:',float(errorCount)/len(testSet))
spamTest()
运行结果
解析函数中输出的wordlist:
[]
解析函数中输出的docList:
[]
输出classlist
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
测试一下open。read函数
--- Codeine 15mg -- 30 for $203.70 -- VISA Only!!! --
-- Codeine (Methylmorphine) is a narcotic (opioid) pain reliever
-- We have 15mg & 30mg pills -- 30/15mg for $203.70 - 60/15mg for $385.80 - 90/15mg for $562.50 -- VISA Only!!! ---
测试parse函数
[]
创建词汇表:
随机选择10个作为测试集放到testSet中,并且将其从trainingSet中删除
对于选为训练集的每一个文本,都将其转化为onehot类型的词向量,最后构成一个矩阵
测试集 28 : []
the right sequence is: 1 and our predict sequence is: 1
测试集 16 : []
the right sequence is: 1 and our predict sequence is: 1
测试集 25 : []
the right sequence is: 0 and our predict sequence is: 1
测试集 11 : []
the right sequence is: 0 and our predict sequence is: 1
测试集 7 : []
the right sequence is: 0 and our predict sequence is: 1
测试集 9 : []
the right sequence is: 0 and our predict sequence is: 1
测试集 2 : []
the right sequence is: 1 and our predict sequence is: 1
测试集 3 : []
the right sequence is: 0 and our predict sequence is: 1
测试集 47 : []
the right sequence is: 0 and our predict sequence is: 1
测试集 0 : []
the right sequence is: 1 and our predict sequence is: 1
the error rate is: 0.6
定位到正则表达式切分文本出现错误,但是暂时不知道如何解决