1.1 题目的主要研究内容
(1)工作的主要描述
利用贝叶斯模型实现简单的垃圾邮件分类,将正常邮件和垃圾邮件分别识别出来并分类。
1.2 题目研究的工作基础或实验条件
软件环境:Windows10
Python版本:3.7
相关模块:scikit-learn模块;jieba模块;numpy模块;以及⼀些Python⾃带的模块。
1.3 数据集描述
使用的中文邮件数据集和中文停用词表,其中spam文件夹中有7775封垃圾邮件,normal文件夹中有7063封正常邮件,test文件夹中有392封测试邮件,test文件夹中,文件名在1至200的为正常邮件,文件名7801~8000的为垃圾邮件。
1.4 特征提取过程描述
由于词典里的词数量太多,只保留了词频最高的4000个词作为最终创建的词典。词典准备好之后,把每封信的内容转换为词向量,其维度为4000,每一维代表一个高频词在该邮件中出现的频率,最后将这些词向量合并为一个大的特征向量矩阵,其大小为:(7063+7775)×4000,即前7063行为正常邮件的特征向量,其余为垃圾邮件的特征向量。
1.5 分类过程描述
对测试集中的每一封邮件用结巴分词,并用停用表进行简单过滤,然后使用正则表达式过滤掉邮件中的非中文字符,并计算得到P(s|w)(在已知词向量w的条件下求包含该词向量邮件是否为垃圾邮件的概率,s表示分类为垃圾邮件)最高的15个词,在计算过程中,若该词只出现在垃圾邮件的词典中,则令P(w|s')=0.01,反之亦然;若都未出现,则令P(s|w)=0.4(这里做的几个假设基于前人做的一些研究工作得出的)。然后计算得到的每封邮件中重要的15个词的贝叶斯概率,若概率>阈值α,则判为垃圾邮件,否则判为正常邮件。
1.6 主要程序代码
功能函数:
import jieba;
import os;
class spamEmailBayes:
# 获得中文停用词表
def getStopWords(self):
stopList=[]
for line in open("../data/中文停用词表.txt"):
stopList.append(line[:len(line)-1])
return stopList;
# 分别保存正常邮件与垃圾邮件中出现的词有多少邮件出现该词,得到两个词典
def get_word_list(self,content,wordsList,stopList):
#分词结果放入res_list
res_list = list(jieba.cut(content))
for i in res_list:
if i not in stopList and i.strip()!='' and i!=None:
if i not in wordsList:
wordsList.append(i)
# 若列表中的词已在词典中,则加1,否则添加进去
def addToDict(self,wordsList,wordsDict):
for item in wordsList:
if item in wordsDict.keys():
wordsDict[item]+=1
else:
wordsDict.setdefault(item,1)
def get_File_List(self,filePath):
filenames=os.listdir(filePath)
return filenames
# 通过计算每封邮件中p(s|w)(在已知词向量w的条件下求包含该词向量邮件是否为垃圾邮件的概率)来得到对分类影响最大的15个词
def getTestWords(self,testDict,spamDict,normDict,normFilelen,spamFilelen):
wordProbList={}
for word,num in testDict.items():
if word in spamDict.keys() and word in normDict.keys():
pw_s=spamDict[word]/spamFilelen
pw_n=normDict[word]/normFilelen
ps_w=pw_s/(pw_s+pw_n)
wordProbList.setdefault(word,ps_w)
if word in spamDict.keys() and word not in normDict.keys():
# 若该词只出现在垃圾邮件的词典中,则令P(w|s′)=0.01
pw_s=spamDict[word]/spamFilelen
pw_n=0.01
ps_w=pw_s/(pw_s+pw_n)
wordProbList.setdefault(word,ps_w)
if word not in spamDict.keys() and word in normDict.keys():
pw_s=0.01
pw_n=normDict[word]/normFilelen
ps_w=pw_s/(pw_s+pw_n)
wordProbList.setdefault(word,ps_w)
if word not in spamDict.keys() and word not in normDict.keys():
# 若都未出现,概率设为0.4
wordProbList.setdefault(word,0.4)
sorted(wordProbList.items(),key=lambda d:d[1],reverse=True)[0:15]
return (wordProbList)
#计算贝叶斯概率
def calBayes(self,wordList,spamdict,normdict):
ps_w=1
ps_n=1
for word,prob in wordList.items() :
print(word+"/"+str(prob))
ps_w*=(prob)
ps_n*=(1-prob)
p=ps_w/(ps_w+ps_n)
return p
print(str(ps_w)+""+str(ps_n))
#计算预测结果正确率
def calAccuracy(self,testResult):
rightCount=0
errorCount=0
for name ,catagory in testResult.items():
if (int(name)<1000 and catagory==0) or(int(name)>1000 and catagory==1):
rightCount+=1
else:
errorCount+=1
return rightCount/(rightCount+errorCount)
主函数:
from spam.spamEmail import spamEmailBayes
import re
# spam类对象
spam=spamEmailBayes()
# 保存词频的词典
spamDict={}
normDict={}
testDict={}
# 保存每封邮件中出现的词
wordsList=[]
wordsDict={}
# 保存预测结果,key为文件名,值为预测类别
testResult={}
# 分别获得正常邮件、垃圾邮件及测试文件名称列表
normFileList=spam.get_File_List(r"C:\Users\8\Desktop\BayesSpam\data\normal")
spamFileList=spam.get_File_List(r"C:\Users\8\Desktop\BayesSpam\data\spam")
testFileList=spam.get_File_List(r"C:\Users\8\Desktop\BayesSpam\data\test")
# 获取训练集中正常邮件与垃圾邮件的数量
normFilelen=len(normFileList)
spamFilelen=len(spamFileList)
# 获得停用词表,用于对停用词过滤
stopList=spam.getStopWords()
# 获得正常邮件中的词频
for fileName in normFileList:
wordsList.clear()
for line in open("../data/normal/"+fileName):
# 过滤掉非中文字符
rule=re.compile(r"[^\u4e00-\u9fa5]")
line=rule.sub("",line)
# 将每封邮件出现的词保存在wordsList中
spam.get_word_list(line,wordsList,stopList)
# 统计每个词在所有邮件中出现的次数
spam.addToDict(wordsList, wordsDict)
normDict=wordsDict.copy()
# 获得垃圾邮件中的词频
wordsDict.clear()
for fileName in spamFileList:
wordsList.clear()
for line in open("../data/spam/"+fileName):
rule=re.compile(r"[^\u4e00-\u9fa5]")
line=rule.sub("",line)
spam.get_word_list(line,wordsList,stopList)
spam.addToDict(wordsList, wordsDict)
spamDict=wordsDict.copy()
# 获得测试邮件中的词频
for fileName in testFileList:
testDict.clear( )
wordsDict.clear()
wordsList.clear()
for line in open("../data/test/"+fileName):
rule=re.compile(r"[^\u4e00-\u9fa5]")
line=rule.sub("",line)
spam.get_word_list(line,wordsList,stopList)
spam.addToDict(wordsList, wordsDict)
testDict=wordsDict.copy()
# 通过计算每个文件中p(s|w)来得到对分类影响最大的15个词
wordProbList=spam.getTestWords(testDict, spamDict,normDict,normFilelen,spamFilelen)
# 对每封邮件得到的15个词计算贝叶斯概率
p=spam.calBayes(wordProbList, spamDict, normDict)
if(p>0.9):
testResult.setdefault(fileName,1)
else:
testResult.setdefault(fileName,0)
# 计算分类准确率
testAccuracy=spam.calAccuracy(testResult)
for i,ic in testResult.items():
print(i+"/"+str(ic))
print(testAccuracy)
1.7 运行结果及分析
运行结果:
邮件判别结果:(0代表正常邮件,1代表垃圾邮件)
在392封邮件(正常邮件与垃圾邮件各一半)的测试集中,测试结果的分类准确率为95.15%,在仅仅统计词频计算概率的情况下,分类结果还是相当不错的。
功能函数:
import jieba;
import os;
class spamEmailBayes:
# 获得中文停用词表
def getStopWords(self):
stopList=[]
for line in open("../data/中文停用词表.txt"):
stopList.append(line[:len(line)-1])
return stopList;
# 分别保存正常邮件与垃圾邮件中出现的词有多少邮件出现该词,得到两个词典
def get_word_list(self,content,wordsList,stopList):
#分词结果放入res_list
res_list = list(jieba.cut(content))
for i in res_list:
if i not in stopList and i.strip()!='' and i!=None:
if i not in wordsList:
wordsList.append(i)
# 若列表中的词已在词典中,则加1,否则添加进去
def addToDict(self,wordsList,wordsDict):
for item in wordsList:
if item in wordsDict.keys():
wordsDict[item]+=1
else:
wordsDict.setdefault(item,1)
def get_File_List(self,filePath):
filenames=os.listdir(filePath)
return filenames
# 通过计算每封邮件中p(s|w)(在已知词向量w的条件下求包含该词向量邮件是否为垃圾邮件的概率)来得到对分类影响最大的15个词
def getTestWords(self,testDict,spamDict,normDict,normFilelen,spamFilelen):
wordProbList={}
for word,num in testDict.items():
if word in spamDict.keys() and word in normDict.keys():
pw_s=spamDict[word]/spamFilelen
pw_n=normDict[word]/normFilelen
ps_w=pw_s/(pw_s+pw_n)
wordProbList.setdefault(word,ps_w)
if word in spamDict.keys() and word not in normDict.keys():
# 若该词只出现在垃圾邮件的词典中,则令P(w|s′)=0.01
pw_s=spamDict[word]/spamFilelen
pw_n=0.01
ps_w=pw_s/(pw_s+pw_n)
wordProbList.setdefault(word,ps_w)
if word not in spamDict.keys() and word in normDict.keys():
pw_s=0.01
pw_n=normDict[word]/normFilelen
ps_w=pw_s/(pw_s+pw_n)
wordProbList.setdefault(word,ps_w)
if word not in spamDict.keys() and word not in normDict.keys():
# 若都未出现,概率设为0.4
wordProbList.setdefault(word,0.4)
sorted(wordProbList.items(),key=lambda d:d[1],reverse=True)[0:15]
return (wordProbList)
#计算贝叶斯概率
def calBayes(self,wordList,spamdict,normdict):
ps_w=1
ps_n=1
for word,prob in wordList.items() :
print(word+"/"+str(prob))
ps_w*=(prob)
ps_n*=(1-prob)
p=ps_w/(ps_w+ps_n)
return p
print(str(ps_w)+""+str(ps_n))
#计算预测结果正确率
def calAccuracy(self,testResult):
rightCount=0
errorCount=0
for name ,catagory in testResult.items():
if (int(name)<1000 and catagory==0) or(int(name)>1000 and catagory==1):
rightCount+=1
else:
errorCount+=1
return rightCount/(rightCount+errorCount)
主函数:
from spam.spamEmail import spamEmailBayes
import re
# spam类对象
spam=spamEmailBayes()
# 保存词频的词典
spamDict={}
normDict={}
testDict={}
# 保存每封邮件中出现的词
wordsList=[]
wordsDict={}
# 保存预测结果,key为文件名,值为预测类别
testResult={}
# 分别获得正常邮件、垃圾邮件及测试文件名称列表
normFileList=spam.get_File_List(r"C:\Users\8\Desktop\BayesSpam\data\normal")
spamFileList=spam.get_File_List(r"C:\Users\8\Desktop\BayesSpam\data\spam")
testFileList=spam.get_File_List(r"C:\Users\8\Desktop\BayesSpam\data\test")
# 获取训练集中正常邮件与垃圾邮件的数量
normFilelen=len(normFileList)
spamFilelen=len(spamFileList)
# 获得停用词表,用于对停用词过滤
stopList=spam.getStopWords()
# 获得正常邮件中的词频
for fileName in normFileList:
wordsList.clear()
for line in open("../data/normal/"+fileName):
# 过滤掉非中文字符
rule=re.compile(r"[^\u4e00-\u9fa5]")
line=rule.sub("",line)
# 将每封邮件出现的词保存在wordsList中
spam.get_word_list(line,wordsList,stopList)
# 统计每个词在所有邮件中出现的次数
spam.addToDict(wordsList, wordsDict)
normDict=wordsDict.copy()
# 获得垃圾邮件中的词频
wordsDict.clear()
for fileName in spamFileList:
wordsList.clear()
for line in open("../data/spam/"+fileName):
rule=re.compile(r"[^\u4e00-\u9fa5]")
line=rule.sub("",line)
spam.get_word_list(line,wordsList,stopList)
spam.addToDict(wordsList, wordsDict)
spamDict=wordsDict.copy()
# 获得测试邮件中的词频
for fileName in testFileList:
testDict.clear( )
wordsDict.clear()
wordsList.clear()
for line in open("../data/test/"+fileName):
rule=re.compile(r"[^\u4e00-\u9fa5]")
line=rule.sub("",line)
spam.get_word_list(line,wordsList,stopList)
spam.addToDict(wordsList, wordsDict)
testDict=wordsDict.copy()
# 通过计算每个文件中p(s|w)来得到对分类影响最大的15个词
wordProbList=spam.getTestWords(testDict, spamDict,normDict,normFilelen,spamFilelen)
# 对每封邮件得到的15个词计算贝叶斯概率
p=spam.calBayes(wordProbList, spamDict, normDict)
if(p>0.9):
testResult.setdefault(fileName,1)
else:
testResult.setdefault(fileName,0)
# 计算分类准确率
testAccuracy=spam.calAccuracy(testResult)
for i,ic in testResult.items():
print(i+"/"+str(ic))
print(testAccuracy)