基于多项式朴素贝叶斯的中文垃圾邮件识别

简介: 基于多项式朴素贝叶斯的中文垃圾邮件识别

1 目的

  对来源于附件1的156个中文邮件样本,其中前127个邮件为垃圾邮件,后面28个邮件为正常邮件。通过建立数学模型识别中文邮件是否为垃圾邮件,在模型建立过程中使用网格搜索的方式确定模型最优参数,使用交叉验证的方式评估模型的泛化能力。同时,利用建立好的模型对最后5个中文邮件类型进行识别。

2 实验环境

2.1 硬件环境

  ● 处理器:Intel® Core™ i5-8300H CPU @ 2.30GHz 2.30 GHz

  ● 内存:8GB DDR4 RAM

  ● 存储:256GB SSD

  ● 操作系统:Windows 10

2.2 软件环境

  ● Python:3.9.12

3 方案设计

图1 方案设计

4 预备知识

4.1 多项式朴素贝叶斯算法

  多项式朴素贝叶斯算法是文本分类中使用的两种经典的朴素贝叶斯变体之一,这个分布的每个类别

image.png

4.2 网格搜索方法

  网格搜索方法主要用于模型调参,也就是帮助我们找到一组最合适的模型设置参数,使得模型的预测达到更好的效果,这组参数于模型训练过程中学习到的参数不同,它是需要在训练前预设好的,我们称其为超参数(在机器学习的上下文中,超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。通常情况下,需要对超参数进行优化,给学习机选择一组最优超参数,以提高学习的性能和效果)。

  网格搜索方法通过将给定的参数列表中的值进行随机组合,并分别评估每个组合的模型效果,从而找到一组最优的超参数。例如某个模型有3个超参数,每个超参数我们分别设定有5种不同的取值范围,那么网格搜索需要分别对5 ∗ 5 ∗ 5 = 125 5*5*5=125555=125中参数组合进行效果评估,并从中找出最优的那一组。

4.3 交叉验证法

  交叉验证通过重复使用数据,将得到的样本数据进行切分,组合为不同的训练集和测试集,用训练集来训练模型,用测试集来评估模型预测的好坏。在此基础上可以得到多组不同的训练集和测试集,某次训练集中的某样本在下次可能成为测试集中的样本,即所谓“交叉”。 交叉验证包括简单交叉验证、 折交叉验证和留一法三种。

1.简单交叉验证

  简单交叉验证直接将数据集划分为训练集和验证集,首先利用训练集在不同的参数组合下训练模型,然后在测试集上评价不同参数组合模型的误差,选择测试误差最小的模型。

2.K折交叉验证

  将数据集均匀划分为 个子集,每次迭代,使用一个子集作为测试集,其他的作为训练集。 也可以看做是 折交叉验证的一个特例, 等于样本总数 。对于得到的 个模型,从其中挑选误差最小的作为最终的模型。原理如图2所示:

图2 K折交叉验证图解

3.留一法

  当 K 折交叉验证中的 K等于数据集样本数 K 时,我们便得到了K 折交叉验证的特例:留一法。因为留一法使用的训练集只比原始数据集少了一个样本,因此评估结果往往比较准确。但当样本数据集较大时,需要训练模型也就越多。

5 代码块功能

5.1 加载相关包

1.  from re import sub  
2.  from time import time  
3.  from os import listdir  
4.  from collections import Counter  
5.  from itertools import chain  
6.  from numpy import array  
7.  from jieba import cut  
8.  from sklearn.naive_bayes import MultinomialNB  
9.  from sklearn.model_selection import GridSearchCV  
10. from sklearn.model_selection import cross_val_score,\  
11.      ShuffleSplit, LeaveOneOut

5.2 数据预处理

  1.建立getWordsFromFile(txtFile) 函数,作用为清除中文邮件中的噪声文本。首先以UTF-8编码方式打开中文邮件,然后依次删除邮件内容中每一行的空白字符;例如“[”、“.”、数字、“~”的此类干扰字符或无效字符。在对其进行Jieba 分词后删除长度为1的词,然后将结果保存为列表。

1.  def getWordsFromFile(txtFile):  
2.      # 获取每一封邮件中的所有词语  
3.      words = []  
4.      # 所有存储邮件文本内容的记事本文件都使用UTF8编码  
5.      with open(txtFile, encoding='utf8') as fp:  
6.          for line in fp:  
7.              # 遍历每一行,删除两端的空白字符  
8.              line = line.strip()  
9.              # 过滤干扰字符或无效字符  
10.             line = sub(r'[.【】0-9、—。,!~\*#@%$&]', '', line)  
11.             # 分词  
12.             line = cut(line)  
13.             # 过滤长度为1的词  
14.             line = filter(lambda word: len(word)>1, line)  
15.             # 把本行文本预处理得到的词语添加到words列表中  
16.             words.extend(line)  
17.     # 返回包含当前邮件文本中所有有效词语的列表  
18.     return words

  2.建立getTopNWords(topN,dstDir=‘datasets’) 函数,作用为:

  (1)将每一中文邮件进行预处理后的单词列表作为子列表依次存放allWords列表中;

  (2)将allWords列表中出现最多的前TopN个单词存入topWords列表。其中getTopNWords(topN,dstDir=‘datasets’) 函数的两个参数分别为TopN单词个数和数据存放的文件夹位置,初始值设置为topN=600,dstDir=‘D:\研\附件1’。在此设置下,调用getTopNWords(topN,dstDir=‘datasets’) 函数过程中,将获取到每一个中文邮件的保存的地址,存入到txtFiles列表,通过循环的方式调用getWordsFromFile(txtFile) 函数,对前151个中文邮件进行预处理后将结果依次存入allWords列表。然后统计allWords列表中每一单词出现次数后,将次数排序后的前600个单词保存到topWords列表。allWords列表内容部分见图3;topWords中前六个单词依次为:‘华为’, ‘我们’, ‘企业’, ‘人工智能’, ‘智能’, ‘技术’。

图3 allWords列表部分内容

1.  allWords = []  
2.  def getTopNWords(topN,dstDir='datasets'):  
3.      # 按文件编号顺序处理当前文件夹中所有记事本文件  
4.      # 训练集中共151封邮件内容,0.txt到126.txt是垃圾邮件内容  
5.      # 127.txt到150.txt为正常邮件内容  
6.      txtFiles = [dstDir+'\\'+str(i)+'.txt' for i in range(151)]  
7.      # 获取训练集中所有邮件中的全部单词  
8.      for txtFile in txtFiles:  
9.          allWords.append(getWordsFromFile(txtFile))  
10.     # 获取并返回出现次数最多的前topN个单词  
11.     freq = Counter(chain(*allWords))  
12.     return [w[0] for w in freq.most_common(topN)]  
13.   
14. # 全部训练集中出现次数最多的前600个单词  
15. topWords = getTopNWords(600,'D:\\研\\附件1')

  (3)循环遍历allWords列表,按顺序统计每一子列表中出现topWords列表中的词的个数,依次存入到vectors ,然后将vectors 列表转化为数组格式,然后创建labels 数组,labels数组中值一一对应邮件的类型,其中1表示垃圾邮件,0表示正常邮件。

1.  vectors = []  
2.  for words in allWords:  
3.      temp = list(map(lambda x: words.count(x), topWords))  
4.      vectors.append(temp)  
5.  vectors = array(vectors)  
6.  # 训练集中每个邮件的标签,1表示垃圾邮件,0表示正常邮件  
7.  labels = array([1]*127 + [0]*24)

5.3 模型构建

  以vectors 、labels分别作为模型训练集的X和Y,首先建立多项式朴素贝叶斯模型,设置参数网格中,各参数设置范围见表1所示。

表1 各参数选择范围

  然后使用GridSearchCV进行参数寻优,然后使用GridSearchCV进行参数寻优,网格搜索过程中,最佳参数组合为:alpha =0.05,fit_prior =‘Flase’,最佳得分为0.9669,耗时0.1267s。接着在此参数设置下建立支持向量机模型。

1.  # 建立模型  
2.  model = MultinomialNB()  
3.  # 待测试的参数  
4.  parameters = {'alpha': (0.01,0.05,0.1,0.2,0.5,1,2)}  
5.  # 网格搜索  
6.  start = time()  
7.  clf = GridSearchCV(model, parameters)  
8.  clf.fit(vectors,labels)  
9.  # 解除注释可以查看详细结果  
10. #print(clf.cv_results_)  
11. print(clf.best_params_)  
12. print('得分:', clf.score(vectors,labels))  
13. print('用时(秒):', time()-start)  
14. # 确定模型  
15. model =MultinomialNB(alpha=0.05)  
16. model.fit(vectors,labels)  
17. print('数据加载完成。')

5.4 模型评估

  1.简单交叉验证过程中,将训练集以7:3的比例随机切分10次,以最佳模型进行训练和预测,10次得分分别为:0.7609、0.7174、0.6522、0.6957、0.7826、0.5652、0.7174、0.8043、0.6957、0.7391,平均得分为0.713,耗时0.02秒。

1.  start = time()  
2.  scores = cross_val_score(model,vectors, labels,  
3.                             cv=ShuffleSplit(test_size=0.3,  
4.                                               train_size=0.7,  
5.                                               n_splits=10))  
6.  print('交叉验证(随机拆分)得分情况:\n', scores)  
7.  print('平均分:\n', scores.mean())  
8.  print('用时(秒):', time()-start)  
9.  print('='*20)

  2.K折交叉验证过程中,设置K=10,以最佳模型进行训练和预测,10次得分分别为:0.875、0.8667、0.8、0.7333、0.7333、0.7333、0.7333、0.5333、0.8、0.5333,平均得分为0.7342,耗时0.017秒。

1.  start = time()  
2.  scores = cross_val_score(model, vectors, labels, cv=10)  
3.  print('交叉验证(k折叠)得分情况\n', np.round(scores,4))  
4.  print('平均分:\n', np.round(scores.mean(),4))  
5.  print('用时(秒):', np.round(time()-start,4))  
6.  print('='*20)

  3.留一交叉验证过程中,能获取交叉验证(逐个测试)得分情况:

[1. 1. 1. 1. 1. 0. 1. 1. 1. 0. 1. 1. 1. 0. 1. 0. 1. 1. 1. 1. 1. 0. 1. 1.

\1. 1. 1. 1. 0. 0. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1.

\0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 0. 1. 1. 0. 1. 0. 1. 1. 1.

\1. 1. 1. 0. 0. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0. 1. 1. 0. 0. 1. 1. 1.

\0. 1. 1. 0. 1. 0. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 0.

\1. 0. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 0. 0.

\0. 0. 1. 1. 1. 1. 0.]

  平均得分为0.7682,耗时0.2333秒。

1.  start = time()  
2.  scores = cross_val_score(model,vectors, labels,  
3.                             cv=LeaveOneOut())  
4.  print('交叉验证(逐个测试)得分情况:\n', np.round(scores,4))  
5.  print('平均分:\n', np.round(scores.mean(),4))  
6.  print('用时(秒):', np.round(time()-start,4))  
7.  print('='*20)

5.5 模型应用

  首先定义predict(txtFile) 函数,作用为将txtFile地址下的中文邮件内容进行噪声文本清除、依据topWords构建词向量、利用最优模型预测操作,若预测结果为1返回“垃圾邮件”、否则返回“正常邮件”。然后循环遍历最后五个中文邮件的地址,依次输出预测结果。对于最后5个邮件,预测结果分别为:“垃圾邮件”、“垃圾邮件”、“垃圾邮件”、“垃圾邮件”、“垃圾邮件”。

1.  def predict(txtFile):  
2.      # 获取指定邮件文件内容,返回分类结果  
3.      words = getWordsFromFile(txtFile)  
4.      currentVector = array(tuple(map(lambda x: words.count(x),  
5.                                      topWords)))  
6.      result =model.predict(currentVector.reshape(1, -1))[0]  
7.      return '垃圾邮件' if result==1 else '正常邮件'  
8.    
9.  # 151.txt至155.txt为测试邮件内容  
10. for mail in ('D:\\研\\附件1\\'+'%d.txt'%i for i in range(151, 156)):  
11.     print(mail, predict(mail), sep=':')

5 总结

  本文建立了多项式朴素贝叶斯模型对中文垃圾邮件进行识别,并在实验过程中经过了数据预处理、模型构建、模型评估、模型应用步骤。在数据预处理过程,结合了传统中文文本数据预处理方式;在模型构建过程中使用网格寻优的方式获取多项式朴素贝叶斯模型的最优参数;在模型评估过程中使用了三种交叉验证方式进行模型的泛化能力评估,最后在应用中对未知的五个中文邮件进行了是否为垃圾邮件的识别。

6 完整代码

8.  # 1.**导入所需的库**  
9.  from re import sub  
10. from time import time  
11. from collections import Counter  
12. from itertools import chain  
13. from numpy import array  
14. import numpy as np  
15. from jieba import cut  
16. from sklearn.naive_bayes import MultinomialNB  
17. from sklearn.model_selection import GridSearchCV  
18. from sklearn.model_selection import cross_val_score,\  
19.      ShuffleSplit, LeaveOneOut  
20.   
21. # 2.**数据预处理**  
22. # 去除噪声文本  
23. def getWordsFromFile(txtFile):  
24.     # 获取每一封邮件中的所有词语  
25.     words = []  
26.     # 所有存储邮件文本内容的记事本文件都使用UTF8编码  
27.     with open(txtFile, encoding='utf8') as fp:  
28.         for line in fp:  
29.             # 遍历每一行,删除两端的空白字符  
30.             line = line.strip()  
31.             # 过滤干扰字符或无效字符  
32.             line = sub(r'[.【】0-9、—。,!~\*#@%$&]', '', line)  
33.             # 分词  
34.             line = cut(line)  
35.             # 过滤长度为1的词  
36.             line = filter(lambda word: len(word)>1, line)  
37.             # 把本行文本预处理得到的词语添加到words列表中  
38.             words.extend(line)  
39.     # 返回包含当前邮件文本中所有有效词语的列表  
40.     return words  
41.   
42. # 存放所有文件中的单词  
43. # 每个元素是一个子列表,其中存放一个文件中的所有单词  
44. allWords = []  
45. def getTopNWords(topN,dstDir='datasets'):  
46.     # 按文件编号顺序处理当前文件夹中所有记事本文件  
47.     # 训练集中共151封邮件内容,0.txt到126.txt是垃圾邮件内容  
48.     # 127.txt到150.txt为正常邮件内容  
49.     txtFiles = [dstDir+'\\'+str(i)+'.txt' for i in range(151)]  
50.     # 获取训练集中所有邮件中的全部单词  
51.     for txtFile in txtFiles:  
52.         allWords.append(getWordsFromFile(txtFile))  
53.     # 获取并返回出现次数最多的前topN个单词  
54.     freq = Counter(chain(*allWords))  
55.     return [w[0] for w in freq.most_common(topN)]  
56.   
57. # 全部训练集中出现次数最多的前600个单词  
58. topWords = getTopNWords(600,'D:\\研\\附件1')  
59.   
60. # 获取特征向量,前600个单词的每个单词在每个邮件中出现的频率  
61. vectors = []  
62. for words in allWords:  
63.     temp = list(map(lambda x: words.count(x), topWords))  
64.     vectors.append(temp)  
65. vectors = array(vectors)  
66. # 训练集中每个邮件的标签,1表示垃圾邮件,0表示正常邮件  
67. labels = array([1]*127 + [0]*24)   
68.    
69. # 3.**模型建立**  
70. # 建立多项式朴素贝叶斯模型  
71. model = MultinomialNB()  
72. # 待测试的参数  
73. parameters = {'alpha': (0.01,0.05,0.1,0.2,0.5,1,2)}  
74. # 网格搜索  
75. start = time()  
76. clf = GridSearchCV(model, parameters)  
77. clf.fit(vectors,labels)  
78. # 解除注释可以查看详细结果  
79. #print(clf.cv_results_)  
80. print(clf.best_params_)  
81. print('得分:', np.round(clf.score(vectors,labels),4))  
82. print('用时(秒):', np.round(time()-start,4))  
83. # 确定模型  
84. model =MultinomialNB(alpha=0.05)  
85. model.fit(vectors,labels)  
86. print('数据加载完成。')  
87.   
88. # 4.**模型评估**  
89. #简单交叉验证  
90. start = time()  
91. scores = cross_val_score(model,vectors, labels,  
92.                            cv=ShuffleSplit(test_size=0.3,  
93.                                              train_size=0.7,  
94.                                              n_splits=10))  
95. print('交叉验证(随机拆分)得分情况:\n', np.round(scores,4))  
96. print('平均分:\n', np.round(scores.mean(),4))  
97. print('用时(秒):', np.round(time()-start,4))  
98. print('='*20)  
99.   
100.  # K折交叉验证  
101.  start = time()  
102.  scores = cross_val_score(model, vectors, labels, cv=10)  
103.  print('交叉验证(k折叠)得分情况\n', np.round(scores,4))  
104.  print('平均分:\n', np.round(scores.mean(),4))  
105.  print('用时(秒):', np.round(time()-start,4))  
106.  print('='*20)  
107.    
108.  # 留一交叉验证  
109.  start = time()  
110.  scores = cross_val_score(model,vectors, labels,  
111.                             cv=LeaveOneOut())  
112.  print('交叉验证(逐个测试)得分情况:\n', np.round(scores,4))  
113.  print('平均分:\n', np.round(scores.mean(),4))  
114.  print('用时(秒):', np.round(time()-start,4))  
115.  print('='*20)  
116.    
117.  # 5.**模型应用**  
118.  def predict(txtFile):  
119.      # 获取指定邮件文件内容,返回分类结果  
120.      words = getWordsFromFile(txtFile)  
121.      currentVector = array(tuple(map(lambda x: words.count(x),  
122.                                      topWords)))  
123.      result =model.predict(currentVector.reshape(1, -1))[0]  
124.      return '垃圾邮件' if result==1 else '正常邮件'  
125.    
126.  # 151.txt至155.txt为测试邮件内容  
127.  for mail in ('D:\\研\\附件1\\'+'%d.txt'%i for i in range(151, 156)):  
128.      print(mail, predict(mail), sep=':')


相关文章
|
5月前
|
机器学习/深度学习 TensorFlow 算法框架/工具
数字识别
【7月更文挑战第26天】数字识别。
46 2
|
6月前
|
机器学习/深度学习 存储 算法
使用支持向量机算法解决手写体识别问题
使用支持向量机算法解决手写体识别问题
36 2
|
6月前
|
算法 Serverless
使用朴素贝叶斯算法过滤垃圾邮件
使用朴素贝叶斯算法过滤垃圾邮件
53 2
|
7月前
|
机器学习/深度学习 数据采集 自然语言处理
数据分享|Python酒店评论文本分析:tfidf、贝叶斯、逻辑回归,支持向量机SVM、K最邻近KNN、随机森林、LDA主题模型
数据分享|Python酒店评论文本分析:tfidf、贝叶斯、逻辑回归,支持向量机SVM、K最邻近KNN、随机森林、LDA主题模型
|
7月前
|
机器学习/深度学习 自然语言处理 算法
weka文本挖掘分析垃圾邮件分类模型
weka文本挖掘分析垃圾邮件分类模型
|
7月前
|
机器学习/深度学习 数据采集 自然语言处理
使用Python实现一个简单的垃圾邮件分类器
使用Python实现一个简单的垃圾邮件分类器
203 0
|
数据采集 存储 机器学习/深度学习
秒懂算法 | 基于朴素贝叶斯算法的垃圾信息的识别
本文将带领大家亲手实现一个垃圾信息过滤的算法。 在正式讲解算法之前,最重要的是对整个任务有一个全面的认识,包括算法的输入和输出、可能会用到的技术,以及技术大致的流程。 本任务的目标是去识别一条短信是否为垃圾信息,即输入为一条文本信息,输出为二分类的分类结果。2002年,Paul Graham提出使用“贝叶斯推断”过滤垃圾邮件。1000封垃圾邮件可以过滤掉995封,且没有一个误判。另外,这种过滤器还具有自我学习的功能,会根据新收到的邮件,不断调整。收到的垃圾邮件越多,它的准确率就越高。 朴素贝叶斯算法是一种有监督的机器学习算法,即算法的实现包含了构建训练集、数据预处理、训练、在测试集上验证
506 0
秒懂算法 | 基于朴素贝叶斯算法的垃圾信息的识别
|
机器学习/深度学习 文字识别 监控
使用 HOG 功能和多类 SVM 分类器对数字进行分类
使用 HOG 功能和多类 SVM 分类器对数字进行分类。
154 0
|
机器学习/深度学习 资源调度 算法
使用线性SVM实现对垃圾邮件分类
使用线性SVM实现对垃圾邮件分类
257 0
使用线性SVM实现对垃圾邮件分类
|
机器学习/深度学习 自然语言处理 数据处理
使用朴素贝叶斯过滤垃圾邮件
使用朴素贝叶斯过滤垃圾邮件
245 0
使用朴素贝叶斯过滤垃圾邮件