【初中生讲机器学习】9. 我是怎么用朴素贝叶斯实现垃圾邮件分类的?真的超全!

创建时间:2024-02-14
最后编辑时间:2024-02-15
作者:Geeker_LStar

你好呀~这里是 Geeker_LStar 的人工智能学习专栏,很高兴遇见你~
我是 Geeker_LStar,一名初三学生,热爱计算机和数学,我们一起加油~!
⭐(●’◡’●) ⭐ 那就让我们开始吧!

okkkk,大概十天以前我写了一篇关于朴素贝叶斯分类器原理的文章,but 没有更对应的实例代码,这篇来填坑~!
关于朴素贝叶斯的原理,这里:【初中生讲机器学习】5. 从概率到朴素贝叶斯算法,一篇带你看明白!

说到朴素贝叶斯,最出名的应用肯定就是垃圾邮件分类啦!这篇就来实现这个!

先讲大思路:判断一封邮件是否为垃圾邮件,我们需要从它所包含的词语入手(比如如果一封邮件包含 “彩票” 或 “广告”,那它很可能是一封垃圾邮件),也就是说,我们要求出 P ( 垃圾邮件 ∣ 词语 ) P(垃圾邮件|词语) P(垃圾邮件词语)。根据上一篇贝叶斯公式的推导, P ( 垃圾邮件 ∣ 词语 ) = P ( 词语 ∣ 垃圾邮件 ) P ( 垃圾邮件 ) P ( 词语 ) P(垃圾邮件|词语) = \frac{P(词语|垃圾邮件)P(垃圾邮件)}{P(词语)} P(垃圾邮件词语)=P(词语)P(词语垃圾邮件)P(垃圾邮件),等号右边这三个量,就是模型需要通过训练集学习出的。

在支持向量机的实例那一篇,我们是直接从 sklearn 中调用的数据集,但是这次不行了emm,so 我们需要自己下载:click here~,点 “同意协议”,然后下载 “trec06c.tgz” 那个,它是中文邮件数据集,一共有六万多封邮件数据。下载之后解压,注意和项目文件放在同一个文件夹中噢~

好,来看一下目录结构:

trec06c- data(存储所有邮件数据)- 000- 000- 001- ...- 299- 001- ...- 215- delay(没什么用)- index- full(存储所有邮件的标签:spam/ham)- index

嗯。。不过事情没有那么简单(()六万多组数据,内存会炸(我实验过了,的确炸了,but 我实在不想再去服务器训练一次了emm),所以我们可以拿出其中一些来用,我选了 3000 组数据放在一个新文件夹(test)当中。不用担心 3000 组不够,我训练的结果还是很不错的。


test 目录下只复制原 trec06c 目录下的 data 和 full 文件夹即可,并且注意删掉不用的数据,像这样:

data 文件夹

okkk,准备完数据我们来导入相关库
解释一下这些库的功能(注释里也有~):

  • re:它负责正则表达式(regular expression),可以使用正则表达式来匹配、搜索和替换文本。
  • jieba:它用来分词(结巴嘛)。
  • os:它是 Python 用来和操作系统打交道的库,功能很广,包括但不限于路径(文件目录)操作、进程管理、环境参数等。
  • codecs:它负责处理编码相关的问题,比如转换编码、解码等。
  • matplotlib:老生常谈了,数据可视化。
  • sklearn:这个之前讲过啦,不过提一句 TfidfVectorizer,它是专门用于计算文档中词语的 TF-IDF 值的一个函数,后面会详细讲哒。
'''第一步:导入相关库'''
import re    # 正则(字符串匹配)
import jieba    # 分词
import os    # 文件执行
import codecs    # 解码器(转换编码)
from sklearn import naive_bayes    # 朴素贝叶斯
from sklearn import svm
from sklearn.model_selection import train_test_split    # 划分训练集和测试集
from sklearn.metrics import roc_curve, recall_score, accuracy_score, roc_auc_score
from sklearn.feature_extraction.text import TfidfVectorizer
import matplotlib.pyplot as plt

andand,邮件数据这个比较难搞的是我们需要对数据进行一些预处理。毕竟鸢尾花数据集当中特征都给咱弄好了 but 这个没有,而且邮件散落在不同的文件夹。
好 ok 启动。

根据上面说的大思路,我们主要关注邮件中出现的词语,也就是邮件正文部分,所以我们可以大胆一点,把不是中文字符的东西全部去掉。去掉的部分包含邮件头、链接、电话号码这些。
(其实这些东西也能帮助判定是否为垃圾邮件,不过作用比较小,所以忽略也没什么大问题~)
这就需要正则表达式上场了。下面前两行正则表达式,帮我们去掉所有非中文字符,第三行去掉左右两端的空格。

# 去除非中文字符
def clear_email_content(string):string = re.sub(r"[^\u4e00-\u9fff]", "", string)string = re.sub(r"\s{2,}", "", string)return string.strip()

举个例子,我把一封邮件丢进去(这封邮件是 data/000/002),邮件的原文如下图:

原文

然后经过一番洗礼,输出是这样的:

输出

but,我们怎么对每一封邮件都这么操作呢?and 这个操作过后我们下一步干什么?别急,看过来:

first,先不管细节,但是多年的编程经验告诉我们, 我们需要先建立一个存储所有邮件数据的数组
然后,我们得遍历邮件内容路径(根据上面的目录结构,是 data 文件夹),遍历到每一封邮件之后把它的内容读取出来,进行去非中文处理,保存在临时变量中。
接着我们对临时变量(它保存了处理后的邮件内容)进行分词,得到一个分词列表,但是由于后续计算 tf-idf 等需要,得再把这个列表拼接成字符串,得到一个分词后的字符串(别看挺复杂,实际最终效果就是把原始内容用空格分成了一个个词)。
最后,我们把分词字符串加入到存储所有邮件数据的数组中,这样我们就处理完了一封邮件,可以重复上述流程来处理下一封啦!

先写一下伪代码:
真的这绝对是最好理解的伪代码,,,,几乎没几个代码()

# 伪代码
邮件内容列表 = []
def 读取邮件内容函数(邮件内容路径):for 子路径 in 邮件内容路径:if 还是目录:递归调用 读取邮件内容函数else:记录邮件内容的变量 = ''邮件内容 = 打开文件()for 每一行 in 邮件内容:= 去除非中文字符函数()变量 += 行关闭文件()分词列表 = 分词(变量)邮件字符串 = 将分词列表拼接为字符串(分词列表)将 邮件字符串 添加到 邮件内容列表

现在是不是感觉代码呼之欲出了((来来来:
真的我保证你一定会了,注释超详细,实在不行就对着上面思路 and 伪代码~
讲两个函数。第一个是 “codecs.open()”。这个函数主要是把邮件编码(或者说读出来的东西的编码)转为 gbk,要不然会乱码,记住一定要写 ignore errors,否则会报错。
第二个是 jieba.cut() 函数,它是 jieba 分词中的精准模式(另外两种分别是全模式和搜索引擎模式),它不会考虑所有可能的分词方式(全模式),而是只给出一种最有可能的分词方式,具有较高的召回率和较低的错误率。

# 正经代码
content_list = []    # 存储所有邮件内容的数组
data_path = "D:\PyProject\\algorithm\classification\\naive bayes\\test\data"    # 邮件内容路径# 获取邮件正文内容,分词后加入邮件内容列表
def get_email_content(path):# 遍历 data 目录,读取每一封邮件files = os.listdir(path)  # 获取该路径下的目录和文件的路径,files 是一个路径列表for file in files:# 是目录就进入目录路径并继续遍历下一层,直到是文件为止if os.path.isdir(path + '/' + file):get_email_content(path + '/' + file)# 是文件就提取文件(邮件)内容else:email = ''    # 记录邮件内容f = codecs.open(path + '/' + file, 'r', 'gbk', errors='ignore')    # 转换为 gbk 编码# 对邮件数据进行预处理——调用 clear_mail_content() 函数for line in f:line = clear_email_content(line)email += line    # 拼接成一个字符串f.close()# 对处理后的内容进行分词(精准模式),去掉长度为 1 的词(字)email = [word for word in jieba.cut(email) if (word.strip != '' and len(word)>1)]# 将列表转化为字符串,词之间用空格分隔email = (' '.join(email))# 将分词后的内容加入邮件内容列表(二维数组,每一个元素都是一封分词后的邮件)content_list.append(email)
get_email_content(data_path)

ok~这样所有邮件数据就读取完了,接下来把所有邮件的标签也这么搞一下,方便后续工作。

还是一样的套路,建一个数组,然后遍历 test/full/index.txt,如果标签是 spam,说明是垃圾邮件,就记录为 1,如果是 ham 就记录为 0.

label_list = []    # 存储所有邮件标签的数组
label_path = "D:\PyProject\\algorithm\classification\\naive bayes\\test\\full\index.txt"# 将邮件标签存入数组中,1 代表垃圾邮件,0 代表正常邮件
def get_email_label(path):f = open(path, 'r')# 将邮件标签转化为 0 和 1,添加到列表中for line in f:# 垃圾邮件 spamif line[0] == 's':label_list.append('1')# 正常邮件 hamelif line[0] == 'h':label_list.append('0')
get_email_label(label_path)

好哒!恭喜我们,已经完成了数据预处理!
接下来…好戏开场!

我们现在已经有了所有邮件分词后的数据,
回到最开始讲的大思路。模型的目标是求出 P ( 词语 ) P(词语) P(词语) P ( 词语 ∣ 垃圾邮件 ) P(词语∣垃圾邮件) P(词语垃圾邮件) P ( 垃圾邮件 ) P(垃圾邮件) P(垃圾邮件) P ( 垃圾邮件 ) P(垃圾邮件) P(垃圾邮件) 还算好求,用垃圾邮件数量除以正常邮件数量就 ok 了,重点在词语上——我们怎么求出一个词语出现的概率 and 在垃圾邮件中,这个词语出现的概率?
难不成遍历存储所有邮件内容的数组???那这工作量也有点太大了。而且现在的所有数据都还是文字形式,是没法直接拿去训练的,我们必须得想办法把它们转化为**实数(矩阵)**的形式。

别担心,TF-IDF 来帮我们啦!

TF-IDF,中文名 “词频-逆文件概率”,英文全称 term frequency–inverse document frequency,常用于自然语言处理(NLP)。
它的主要思想是:如果一个词在该文档中出现的概率高,而在其它文档中出现的概率低,那么这个词(对于这个文件)有较好的代表性。

用到我们的任务当中,如果一些词在垃圾邮件中出现的概率高,而在正常邮件中出现的概率低,那么这些词可以较好地代表垃圾邮件。再通俗一点就是,有这些词的邮件很有可能是垃圾邮件。

ok,知道了我们用它干什么,再来看看它是怎么计算的。
先来看 TF,TF 的意思是词频,也就是一个词在文档中出现的次数。但是为了避免这个次数在长文件中比在短文件中多而影响判断,我们还需要对词频做归一化处理,通常是用词频除以文档总词数,得到 TF 值。
公式如下,其中 T F i , j TF_{i, j} TFi,j 表示该词条在该文档中的 TF 值,分子 n i , j n_{i,j} ni,j 表示词条 t i t_i ti 在文档 d j d_j dj 中出现的次数,分母表示文档的总词数。
T F i , j = n i , j ∑ k k i , j TF_{i, j} = \frac {n_{i,j}}{\sum_{k} k_{i,j}} TFi,j=kki,jni,j
emmm 但是,光有 TF 是不够的,因为有些词比如 “我们”、“他们” 可能在很多文件中的 TF 值都很高(即都出现了很多次),那这些词就不具有代表性了。为此,我们找来了 IDF。
牢记,TF-IDF 是为了衡量一个词对某个文档而言是否足够有代表性
IDF 和词条在所有文档中的出现频率是反过来的。也就是说,如果一个词在很多文档中都出现了,那么它的 IDF 会较低,而如果它只在很少的文档中出现,那么它的 IDF 会很高。
公式如下,其中 N 为文档总数,j 是包含词条 t i t_i ti 的文档数,加 1 是为了防止出现分母为 0 的情况(类似拉普拉斯平滑)。
I D F = l g N 1 + ∣ j : t i ∈ d j ∣ IDF = lg\frac{N}{1+|j:t_i ∈d_j|} IDF=lg1+j:tidjN

综上,如果某词条在某文档中出现的次数多,而在其它文档中出现的次数少,那么它会同时拥有较高的 TF 值和 IDF 值,它的 TF-IDF 值就等于 TF 值和 IDF 值的乘积,即:
T F − I D F = T F × I D F = n i , j ∑ k k i , j × l g N 1 + ∣ j : t i ∈ d j ∣ TF-IDF = TF × IDF = \frac{n_{i,j}}{\sum_{k} k_{i,j}} ×lg\frac{N}{1+|j:t_i ∈d_j|} TFIDF=TF×IDF=kki,jni,j×lg1+j:tidjN

最后放一张超好看的图来总结:

总结

okkkk,用 TF-IDF,我们就可以用之前已经统计好的(分好词的邮件)计算每个词的 TF-IDF 值了~ Python 提供了相关的方法——sklearn 库中的 TfidfVectorizer
先把代码放在这,然后再来讲。

# 将每一封邮件转化为词频矩阵(稀疏矩阵)
# TF-IDF,词频-逆文件频率,自然语言处理中常用的方法(加权技术)
# TF-IDF 用以评估一字词对于一个文件的重要程度。
# 字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库(其他文章)中出现的频率成反比下降。
# 即,一个词语在一篇文章中出现次数越多,同时在所有文档中出现次数越少,越能够代表该文章。
def tfidf(list):tf = TfidfVectorizer()    # 实例化data = tf.fit_transform(list)    # 训练,获得每一个词的 tf-idfglobal XX = data.toarray()    # 二维数组,每一个元素都是一个邮件的词频矩阵
tfidf(content_list)

看函数体中的第二行,data 变量中保存了每一封邮件中的每一个词的 TF-IDF 值,从第一封邮件到第三千封邮件。不过不是数组的形式,输出以后会是这样的:
“0” 代表第一封邮件,“0” 后面的整数是这个词语在所有词语构成的词语表中的编码(索引),最后那个小数才是这个词的 TF-IDF 值

在这里插入图片描述

再看第四行,“X” 是我们把原始数据转化为可用于训练的数据的结果(和 SVM 实例里面的鸢尾花数据集很像),X 是一个二维数组,数组中的每个元素(第一层)都代表一封邮件,“邮件” 里面(第二层)存储着词语的信息,和之前不同的是词语已经被转化成了一个个 TF-IDF 值,也就是词频矩阵
其实就相当于把 data 转成数组,同一封邮件的词的 TF-IDF 构成一个元素,所有的元素(邮件)构成 X。
类似下面这样,嗷不要奇怪为什么一堆是 0,因为词语太多了,而一封邮件肯定不会包含所有的词,那些不被这封邮件包含的词在这封邮件中的 TF-IDF 值就是 0。呃有点绕但是其实还蛮好理解的啦~
X

ok!恭喜我们!好戏部分已经搞定啦~~接下来就是常规训练 & 测试环节咯。

这段就直接放在这了()实在没什么可讲的了,之前 SVM 和 KNN 的实例已经讲过 N 遍了。

'''第三步:训练 and 测试'''
# 获取并划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, label_list, test_size=0.3, random_state=218)
# 用三种模型进行训练,比较它们的结果
# 多项式贝叶斯
mul_bayes = naive_bayes.MultinomialNB()    # 实例化
mul_bayes.fit(X_train, y_train)    # 训练
y1_predict = mul_bayes.predict(X_test)    # 预测(测试)
y1_proba = mul_bayes.predict_proba(X_test)    # 预测属于两个类别(正常/垃圾)的概率
# 伯努利贝叶斯
bernoulli_bayes = naive_bayes.BernoulliNB()
bernoulli_bayes.fit(X_train, y_train)
y2_predict = bernoulli_bayes.predict(X_test)    # 预测(测试)
y2_proba = bernoulli_bayes.predict_proba(X_test)
# 支持向量机
svm = svm.SVC(kernel="rbf", C=1, gamma="auto", probability=True)
svm.fit(X_train, y_train)
y3_predict = svm.predict(X_test)    # 预测(测试)
y3_proba = svm.predict_proba(X_test)# 输出测试结果
# pos_label:正样本标签(本例中正样本为垃圾邮件)
print("多项式朴素贝叶斯准确率:", accuracy_score(y_test, y1_predict))
print("多项式朴素贝叶斯召回率:", recall_score(y_test, y1_predict, pos_label="1"))
print("多项式朴素贝叶斯 AUC 值:", roc_auc_score(y_test, y1_proba[:, 1]))
print("伯努利朴素贝叶斯准确率:", accuracy_score(y_test, y2_predict))
print("伯努利朴素贝叶斯召回率:", recall_score(y_test, y2_predict, pos_label="1"))
print("伯努利朴素贝叶斯 AUC 值:", roc_auc_score(y_test, y2_proba[:, 1]))
print("支持向量机准确率:", accuracy_score(y_test, y3_predict))
print("支持向量机召回率:", recall_score(y_test, y3_predict, pos_label="1"))
print("支持向量机 AUC 值:", roc_auc_score(y_test, y3_proba[:, 1]))

嗯!然后我们来画一个 ROC 曲线嘿嘿,之前在这篇【初中生讲机器学习】6. 分类算法中常用的模型评价指标有哪些?here! 里面我讲过 ROC。

注释里写了,画 ROC 需要知道模型预测数据为正样本的概率,然后再用 roc_curve() 得到真正率 TPR,假正率 FPR。如果不知道是怎么得到的,可以参考上面放的那篇文章~

# 绘制 ROC 曲线图
# 获取模型对测试集数据的预测情况(属于两个类别的概率)
# ROC 曲线需要获取模型预测数据为正样本的概率,即 [:, 1]
score_m = mul_bayes.predict_proba(X_test)[:, 1]
score_b = bernoulli_bayes.predict_proba(X_test)[:, 1]
score_s = svm.predict_proba(X_test)[:, 1]
#使用 roc_curve 方法得到三个模型的真正率 TP,假正率 FP和阈值 threshold
fpr_m, tpr_m, thres_m = roc_curve(y_test, score_m, pos_label="1")
fpr_b, tpr_b, thres_b = roc_curve(y_test, score_b, pos_label="1")
fpr_s, tpr_s, thres_s = roc_curve(y_test, score_s, pos_label="1")

最后就是画图环节啦~代码直接放在这了,有注释。

# 创建画布
fig, ax = plt.subplots(figsize=(10,8))
# 自定义标签名称(使用 AUC 值)
ax.plot(fpr_m,tpr_m,linewidth=2,label='MultinomialNB (AUC={})'.format(str(round(roc_auc_score(y_test, y1_proba[:, 1]), 3))))
ax.plot(fpr_b,tpr_b,linewidth=2,label='BernoulliNB (AUC={})'.format(str(round(roc_auc_score(y_test, y2_proba[:, 1]), 3))))
ax.plot(fpr_s,tpr_s,linewidth=2,label='SVM (AUC={})'.format(str(round(roc_auc_score(y_test, y3_proba[:, 1]), 3))))
# 绘制对角线
ax.plot([0,1],[0,1],linestyle='--',color='grey')
# 调整字体大小
plt.legend(fontsize=12)
# 展现画布
plt.show()

ok 啊!大功告成!!来看看投喂三千组数据是什么结果!~

结果

由此看来,两个贝叶斯模型表现的还是不错的!SVM 的准确率有点低了emm,可能是因为它有较高的召回率(查全率),导致部分非垃圾邮件也被分到了垃圾邮件,准确率就变低了。

来看看 ROC,它们仨都很好呢!

ROC

综合来看,指标最好的是多项式朴素贝叶斯

完整代码:

# 垃圾邮件检索
# 朴素贝叶斯
# TF-IDF 技术
# 首先对数据进行预处理,然后利用 tf-idf 将每封邮件转化为词频矩阵,最后训练模型
# 数据集下载:https://plg.uwaterloo.ca/~gvcormac/treccorpus06/'''第一步:导入相关库'''
import re    # 正则(字符串匹配)
import jieba    # 分词
import os    # 文件执行
import codecs    # 解码器
from sklearn import naive_bayes    # 朴素贝叶斯
from sklearn import svm
from sklearn.model_selection import train_test_split    # 划分训练集和测试集
from sklearn.metrics import roc_curve, recall_score, accuracy_score, roc_auc_score
from sklearn.feature_extraction.text import TfidfVectorizer
import matplotlib.pyplot as plt'''第二步:数据预处理'''
content_list = []
label_list = []
# data_path = "D:\PyProject\\algorithm\classification\\naive bayes\\trec06c\data"
# label_path = "D:\PyProject\\algorithm\classification\\naive bayes\\trec06c\\full\index"
# 原始数据集过大,此处选择 3000 组数据,900 组作为测试集
data_path = "D:\PyProject\\algorithm\classification\\naive bayes\\test\data"
label_path = "D:\PyProject\\algorithm\classification\\naive bayes\\test\\full\index.txt"# 数据预处理
# 去除非中文字符
def clear_email_content(string):string = re.sub(r"[^\u4e00-\u9fff]", "", string)string = re.sub(r"\s{2,}", "", string)return string.strip()# 获取邮件正文内容,分词后加入邮件内容列表
def get_email_content(path):# 遍历 data 目录,读取每一封邮件files = os.listdir(path)  # 获取该路径下的目录和文件的路径,files 是一个路径列表for file in files:# 是目录就进入目录路径并继续遍历下一层,直到是文件为止if os.path.isdir(path + '/' + file):get_email_content(path + '/' + file)# 是文件就提取文件(邮件)内容else:email = ''    # 记录邮件内容f = codecs.open(path + '/' + file, 'r', 'gbk', errors='ignore')    # 转换为 gbk 编码# 对邮件数据进行预处理——调用 clear_mail_content() 函数for line in f:line = clear_email_content(line)email += line    # 拼接成一个字符串f.close()# 对处理后的内容进行分词(精准模式),去掉长度为 1 的词(字)email = [word for word in jieba.cut(email) if (word.strip != '' and len(word)>1)]# 将列表转化为字符串,词之间用空格分隔email = (' '.join(email))# 将分词后的内容加入邮件内容列表(二维数组,每一个元素都是一封分词后的邮件)content_list.append(email)
get_email_content(data_path)# 将邮件标签存入数组中,1 代表垃圾邮件,0 代表正常邮件
def get_email_label(path):f = open(path, 'r')# 将邮件标签转化为 0 和 1,添加到列表中for line in f:# 垃圾邮件 spamif line[0] == 's':label_list.append('1')# 正常邮件 hamelif line[0] == 'h':label_list.append('0')
get_email_label(label_path)# 将每一封邮件转化为词频矩阵(稀疏矩阵)
# TF-IDF,词频-逆文件频率,自然语言处理中常用的方法(加权技术)
# TF-IDF 用以评估一字词对于一个文件的重要程度。
# 字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库(其他文章)中出现的频率成反比下降。
# 即,一个词语在一篇文章中出现次数越多,同时在所有文档中出现次数越少,越能够代表该文章。
def tfidf(list):tf = TfidfVectorizer()    # 实例化data = tf.fit_transform(list)    # 训练,获得每一个词的 tf-idfglobal XX = data.toarray()    # 二维数组,每一个元素都是一个邮件的词频矩阵
tfidf(content_list)'''第三步:训练 and 测试'''
# 获取并划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, label_list, test_size=0.3, random_state=218)
# 用三种模型进行训练,比较它们的结果
# 多项式贝叶斯
mul_bayes = naive_bayes.MultinomialNB()    # 实例化
mul_bayes.fit(X_train, y_train)    # 训练
y1_predict = mul_bayes.predict(X_test)    # 预测(测试)
y1_proba = mul_bayes.predict_proba(X_test)    # 预测属于两个类别(正常/垃圾)的概率
# 伯努利贝叶斯
bernoulli_bayes = naive_bayes.BernoulliNB()
bernoulli_bayes.fit(X_train, y_train)
y2_predict = bernoulli_bayes.predict(X_test)    # 预测(测试)
y2_proba = bernoulli_bayes.predict_proba(X_test)
# 支持向量机
svm = svm.SVC(kernel="rbf", C=1, gamma="auto", probability=True)
svm.fit(X_train, y_train)
y3_predict = svm.predict(X_test)    # 预测(测试)
y3_proba = svm.predict_proba(X_test)# 输出测试结果
# pos_label:正样本标签(本例中正样本为垃圾邮件)
print("多项式朴素贝叶斯准确率:", accuracy_score(y_test, y1_predict))
print("多项式朴素贝叶斯召回率:", recall_score(y_test, y1_predict, pos_label="1"))
print("多项式朴素贝叶斯 AUC 值:", roc_auc_score(y_test, y1_proba[:, 1]))
print("伯努利朴素贝叶斯准确率:", accuracy_score(y_test, y2_predict))
print("伯努利朴素贝叶斯召回率:", recall_score(y_test, y2_predict, pos_label="1"))
print("伯努利朴素贝叶斯 AUC 值:", roc_auc_score(y_test, y2_proba[:, 1]))
print("支持向量机准确率:", accuracy_score(y_test, y3_predict))
print("支持向量机召回率:", recall_score(y_test, y3_predict, pos_label="1"))
print("支持向量机 AUC 值:", roc_auc_score(y_test, y3_proba[:, 1]))# 绘制 ROC 曲线图
# 获取模型对测试集数据的预测情况(属于两个类别的概率)
# ROC 曲线需要获取模型预测数据为正样本的概率,即 [:, 1]
score_m = mul_bayes.predict_proba(X_test)[:, 1]
score_b = bernoulli_bayes.predict_proba(X_test)[:, 1]
score_s = svm.predict_proba(X_test)[:, 1]
#使用 roc_curve 方法得到三个模型的真正率 TP,假正率 FP和阈值 threshold
fpr_m, tpr_m, thres_m = roc_curve(y_test, score_m, pos_label="1")
fpr_b, tpr_b, thres_b = roc_curve(y_test, score_b, pos_label="1")
fpr_s, tpr_s, thres_s = roc_curve(y_test, score_s, pos_label="1")
# 创建画布
fig, ax = plt.subplots(figsize=(10,8))
# 自定义标签名称(使用 AUC 值)
ax.plot(fpr_m,tpr_m,linewidth=2,label='MultinomialNB (AUC={})'.format(str(round(roc_auc_score(y_test, y1_proba[:, 1]), 3))))
ax.plot(fpr_b,tpr_b,linewidth=2,label='BernoulliNB (AUC={})'.format(str(round(roc_auc_score(y_test, y2_proba[:, 1]), 3))))
ax.plot(fpr_s,tpr_s,linewidth=2,label='SVM (AUC={})'.format(str(round(roc_auc_score(y_test, y3_proba[:, 1]), 3))))
# 绘制对角线
ax.plot([0,1],[0,1],linestyle='--',color='grey')
# 调整字体大小
plt.legend(fontsize=12)
# 展现画布
plt.show()

OK!!!以上就是垃圾邮件分类器的完整实现啦!!快去试试吧~~

这篇文章用朴素贝叶斯算法实现了垃圾邮件分类,希望对你有所帮助!⭐
欢迎三连!!一起加油!🎇
——Geeker_LStar

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/683632.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode刷题之或操作使用场景

文章目录 概要题目问题分析小结 概要 今天晚上上床前刷了一个leetcode的题目,是leetcode的2103题,因为是个简单题,我只是想复习一下hash表的用法。结果反而让我看到了或操作的使用场景。 题目 总计有 n 个环,环的颜色可以是红、…

Zabbix图形中文乱码问题(显示口口)解决办法

一 切换到zabbix安装目录assets/fonts下,下载字体 这里使用是nginxphp作为zabbix-web展示,使用find 命令查找 进入目录下,将原有字体备份,下载msyh字体 wget https://www.xxshell.com/download/sh/zabbix/ttf/msyh.ttf 二 修改配…

[GYCTF2020]Blacklist

感觉是[强网杯 2019]随便注 的加强版,之前做的是最后可以通过prepare和execute实现对select的绕过,但是这题把这两个关键字也过滤了。 前面堆叠注入没啥问题,卡在了最后读取flag 查看其他师傅的wp,发现这个handler的可以当作丐版s…

CPU-GPU异构并行化APSP算法

一、Floyd-Warshall算法 介绍 Floyd-Warshall算法(英语:Floyd-Warshall algorithm),中文亦称弗洛伊德算法或佛洛依德算法,是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权(但…

奔跑吧小恐龙(Java)

前言 Google浏览器内含了一个小彩蛋当没有网络连接时,浏览器会弹出一个小恐龙,当我们点击它时游戏就会开始进行,大家也可以玩一下试试,网址:恐龙快跑 - 霸王龙游戏. (ur1.fun) 今天我们也可以用Java来简单的实现一下这…

FileZilla Server 1.8.1内网搭建

配置环境服务器服务器下载服务器配置服务器配置 Server - ConfigureServer Listeners - Port 协议设置 Protocols settingsFTP and FTP over TLS(FTPS) Rights management(权利管理)Users(用户) 客户端建立连接 配置环境 服务器处于局域网内: 客户端 < -访问- > 公网 &l…

[嵌入式系统-17]:RT-Thread -3- 源代码目录结构

目录 前言&#xff1a;功能模块 一、RT-Thread 源代码目录结构 二、支持的CPU架构 三、SRC内核代码 前言&#xff1a;功能模块 一、RT-Thread 源代码目录结构 从RT-Thread的GitHub官网上面下载了内核源码&#xff0c;下载链接如下 https://github.com/RT-Thread/rt-thread…

HarmonyOS 通过getInspectorByKey获取指定元素高宽等属性

例如 这里 我们有这样一个组件 Entry Component struct Dom {build() {Column() {Row() {Circle({ width: 200, height: 200 }).fill(#20101010)}.id(ES)}.width(100%).height(100%)} }这里 我们就写了个很基本的组件结构 然后 我们写了个 Circle 组件 定义了宽高 然后 如果我…

数据接收程序

#include<reg51.h> //包含单片机寄存器的头文件 sbit pPSW^0; /***************************************************** 函数功能&#xff1a;接收一个字节数据 ***************************************************/ unsigned char Receive(void) { unsigned…

116. 填充每个节点的下一个右侧节点指针

给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到…

Flink理论—容错之状态

Flink理论—容错之状态 在 Flink 的框架中&#xff0c;进行有状态的计算是 Flink 最重要的特性之一。所谓的状态&#xff0c;其实指的是 Flink 程序的中间计算结果。Flink 支持了不同类型的状态&#xff0c;并且针对状态的持久化还提供了专门的机制和状态管理器。 Flink 使用…

7 大 Android 数据恢复软件,可轻松找回丢失的数据

每年&#xff0c;由于各种原因&#xff0c;数百万人从他们的 Android 设备中丢失数据。它可能像意外删除文件一样简单&#xff0c;也可能像系统崩溃一样复杂。在这种情况下&#xff0c;拥有高效的数据恢复工具可以证明是救命稻草。Mac 用户尤其需要找到与其系统兼容的软件。好消…

不止于浏览器:掌握Node.js,开启全栈开发新篇章!

介绍&#xff1a;Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;特别适合构建高性能的网络服务器和实时应用。具体介绍如下&#xff1a; 服务器端JavaScript&#xff1a;Node.js的核心优势之一是在服务器端运行JavaScript&#xff0c;这使得前端开发者可以…

如何利用SpringSecurity进行认证与授权

目录 一、SpringSecurity简介 1.1 入门Demo 二、认证 ​编辑 2.1 SpringSecurity完整流程 2.2 认证流程详解 2.3 自定义认证实现 2.3.1 数据库校验用户 2.3.2 密码加密存储 2.3.3 登录接口实现 2.3.4 认证过滤器 2.3.5 退出登录 三、授权 3.1 权限系统作用 3.2 授…

软件实例分享,门诊处方软件存储模板处方笺教程,个体诊所电子处方开单系统软件教程

软件实例分享&#xff0c;门诊处方软件存储模板处方笺教程&#xff0c;个体诊所电子处方开单系统软件教程、 一、前言 以下软件教程以 佳易王诊所电子处方管理软件V17.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 电子处方软件支持病历汇总…

CGAL Mesh分割

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 网格分割是将一个网格分解成更小的、有意义的子网格的过程。该过程用于建模,索具,纹理,形状检索,变形等应用。CGAL为我们提供了一个依赖于形状直径函数(SDF)的算法实现,即给定一个三角形表面网格包围一个3D实体…

对进程与线程的理解

目录 1、进程/任务&#xff08;Process/Task&#xff09; 2、进程控制块抽象(PCB Process Control Block) 2.1、PCB重要属性 2.2、PCB中支持进程调度的一些属性 3、 内存分配 —— 内存管理&#xff08;Memory Manage&#xff09; 4、线程&#xff08;Thread&#xff09;…

auto关键字详讲

目录 1.问题思考 2.auto关键字介绍 3. 早期auto的缺陷&#xff1a; 4.什么叫自动存储器&#xff1f; 5. c标准auto关键字 5.1auto的使用细节 5.2 auto什么时候不能推导变量的类型呢&#xff1f; 5.3基于范围的for循环 5.3.1范围for的用法 5.3.2 范围for的使用条件 6.…

书生浦语大模型实战营-课程笔记(3)

本节课主要是跟着教程做的&#xff0c;操作的东西放到作业里记录了。 这里主要记录一些视频里讲的非操作性的东西。 RAG外挂知识库&#xff1f;优点是成本低&#xff0c;不用重新训练 RAG的一个整体流程。 涉及了文本相似度匹配&#xff0c;是不是和传统的问答系统&#xff0…

【Linux学习】线程池

目录 23.线程池 23.1 什么是线程池 23.2 为什么需要线程池 23.3 线程池的应用场景 23.4 实现一个简单的线程池 23.4.1 RAII风格信号锁 23.4.2 线程的封装 23.4.3 日志打印 22.4.4 定义队列中存放Task类任务 23.4.5 线程池的实现(懒汉模式) 为什么线程池中需要有互斥锁和条件变…