【机器学习】贝叶斯垃圾邮件识别

实验三:贝叶斯垃圾邮件识别

本次作业以垃圾邮件分类任务为基础,要求提取文本特征并使用朴素贝叶斯算法进行垃圾邮件识别(调用已有工具包或自行实现)。

1 任务介绍

​ 电子邮件是互联网的一项重要服务,在大家的学习、工作和生活中会广泛使用。但是大家的邮箱常常被各种各样的垃圾邮件填充了。有统计显示,每天互联网上产生的垃圾邮件有几百亿近千亿的量级。因此,对电子邮件服务提供商来说,垃圾邮件过滤是一项重要功能。而朴素贝叶斯算法在垃圾邮件识别任务上一直表现非常好,至今仍然有很多系统在使用朴素贝叶斯算法作为基本的垃圾邮件识别算法。

​ 本次实验数据集来自Trec06的中文垃圾邮件数据集,目录解压后包含三个文件夹,其中data目录下是所有的邮件(未分词),已分词好的邮件在data_cut目录下。邮件分为邮件头部分和正文部分,两部分之间一般有空行隔开。标签数据在label文件夹下,文件中每行是标签和对应的邮件路径。spam表示垃圾邮件,ham表示正常邮件。

1.1 基本要求:

  1. 提取正文部分的文本特征;
  2. 划分训练集和测试集(可以借助工具包。一般笔记本就足够运行所有数据,认为实现困难或算力不够的同学可以采样一部分数据进行实验。);
  3. 使用朴素贝叶斯算法完成垃圾邮件的分类与预测,要求测试集准确率Accuracy精准率Precision召回率Recall均高于0.9(本次实验可以使用已有的一些工具包完成如sklearn);
  4. 对比特征数目(词表大小)对模型效果的影响;
  5. 提交代码和实验报告。

1.2 扩展要求:

  1. 邮件头信息有时也可以协助判断垃圾邮件,欢迎学有余力的同学们尝试;
  2. 尝试自行实现朴素贝叶斯算法细节;
  3. 尝试对比不同的概率计算方法。

2 导入工具包

import random # 随机相关包
import numpy as np # 常用数学运算工具包
import pandas as pd # pandas数据分析库
import matplotlib.pyplot as plt
import seaborn as sns
from tqdm import tqdm # 进度条工具包
from sklearn.model_selection import train_test_split # 数据集划分
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer # 提取文本特征向量的类
from sklearn.naive_bayes import MultinomialNB, BernoulliNB, ComplementNB # 三种朴素贝叶斯算法,差别在于估计p(x|y)的方式
from sklearn.metrics import accuracy_score, precision_score, recall_score,classification_report # 评价指标RANDOM_SEED = 2023

3 读取数据

data_path = './data/trec06c-utf8/data/' # 数据集目录
data_cut_path = './data/trec06c-utf8/data_cut/' # 切分数据集目录
index_path = './data/trec06c-utf8/label/index' # 标签文件路径

读取邮件,将其邮件头和正文根据空行分割开,读取成两个文件

def read_file(path): # 读取一个邮件文件,返回邮件头和正文信息with open(path, 'r', encoding='utf-8') as f: # 读入文件file = f.read()head = file.split('\n\n',maxsplit=1)[0]text = file.split('\n\n',maxsplit=1)[1]return head, texttest_head, test_text = read_file(data_path + '000/000')
print(f'HEAD:\n{test_head}')
print(f'\nTEXT:\n{test_text}')

HEAD:
Received: from hp-5e1fe6310264 ([218.79.188.136])
by spam-gw.ccert.edu.cn (MIMEDefang) with ESMTP id j7CAoGvt023247
for lu@ccert.edu.cn; Sun, 14 Aug 2005 09:59:04 +0800 (CST)
Message-ID: 200508121850.j7CAoGvt023247@spam-gw.ccert.edu.cn
From: “yan”<(8月27-28,上海)培训课程>
Reply-To: yan@vip.163.com"b4a7r0h0@vip.163.com
To: lu@ccert.edu.cn
Subject: =?gb2312?B?t8eyxs7xvq3A7bXEssbO8bncwO0to6jJs8XMxKPE4qOp?=
Date: Tue, 30 Aug 2005 10:08:15 +0800
MIME-Version: 1.0
Content-type: multipart/related;
type=“multipart/alternative”;
boundary=“----=_NextPart_000_004A_2531AAAC.6F950005”
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2800.1158
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1441

TEXT:
[课 程 背 景]

每一位管理和技术人员都清楚地懂得,单纯从技术角度衡量为合算的方案,也许
却是一个财务陷阱,表面赢利而暗地里亏损,使经
营者无法接受。如何将技术手段与财务运作相结合,使每位管理和技术人员都从
本课程通过沙盘模拟和案例分析,使企业各级管理和技术人员掌握财务管理知识

​ 联系人:桂先生
​ 电话:021-58219359 传真:021-58219402
​ E-mail: gsb158@163.com

根据index文件,对data文件进行读取

  • 因为数据量较大,利用tqdm加入进度条显示读取进度
label_list, head_list, text_list = [], [], [] 
with open(index_path, 'r') as index_file: # 读入标签文件lines = [line.strip() for line in index_file if line.strip() != ''] # 读入所有非空行,并去掉换行符for line in tqdm(lines):label, path = line.split() # 分割为标签和文件路径label = 1 if label == 'spam' else 0 # 标签转化成0 1,垃圾邮件为1path = data_cut_path + path.replace('../data/','') #转换路径head, text = read_file(path) # 读入头信息和正文文本label_list.append(label)head_list.append(head)text_list.append(text)
██████████████████████████████████████████████████████████████████████████| 64620/64620 [00:20<00:00, 3108.41it/s]

将数据存储为DataFrame格式,并展示数据

df = pd.DataFrame({'labels': label_list, 'heads': head_list, 'texts': text_list})
df
labelsheadstexts
01Received: from hp-5e1fe6310264 ([218.79.188.13...[ 课 程 背 景 ]\n\n \n每 一位 管理 和 技术人员 都 清楚 地 ...
10Received: from jdl.ac.cn ([159.226.42.8])\n\tb...讲 的 是 孔子 后人 的 故事 。 一个 老 领导 回到 家乡 , 跟 儿子 感情 不 和...
21Received: from 163.con ([61.141.165.252])\n\tb...尊敬 的 贵 公司 ( 财务 / 经理 ) 负责人 您好 !\n我 是 深圳 金海 实业 有...
31Received: from 12.com ([222.50.6.150])\n\tby s...贵 公司 负责人 ( 经理 / 财务 ) 您好 :\n深圳市 华龙 公司 受 多家 公司 委...
41Received: from dghhkjk.com ([59.36.183.208])\n...这是 一封 HTML 格式 信件 !\n\n- - - - - - - - - - - - ...
............
646151Received: from 163.com ([218.18.139.38])\n\tby...贵 公司 负责人 ( 经理 / 财务 ) 您好 :\n我 公司 是 深圳市 华源 实业 有限...
646161Received: from 12.com ([222.50.12.121])\n\tby ...尊敬 的 商家 朋友 您好 :\n我 是 深圳市 裕华 实业 有限公司 的 。 我司 实力雄...
646171Received: from 163.com ([219.133.253.212])\n\t...贵 公司 负责人 ( 经理 / 财务 ) 您好 !\n我 是 深圳市 康特 实业 有限公司 ...
646181Received: from tencent-0ba99d8 ([210.22.28.223...\n这是 一个 HTML 格式 的 邮件\nFRAME : easymain\n\n\n\n
646191Received: from 163.com ([219.133.253.212])\n\t...贵 公司 负责人 ( 经理 / 财务 ) 您好 !\n我 是 深圳市 康特 实业 有限公司 ...

64620 rows × 3 columns

4 划分数据集

应未在数据分析之前划分数据集

feature_cols = ['heads','texts']
X = df[feature_cols]
Y = df['labels']
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=RANDOM_SEED)
print(X.shape, Y.shape, x_train.shape, x_test.shape, y_train.shape, y_test.shape) # 输出数据行列信息
# 将训练集进行二次划分,以便进行模型调优
xt_train, xt_test, yt_train, yt_test = train_test_split(x_train, y_train, test_size=0.2, random_state=RANDOM_SEED)
print(xt_train.shape, xt_test.shape, yt_train.shape, yt_test.shape) # 输出数据行列信息
(64620, 2) (64620,) (51696, 2) (12924, 2) (51696,) (12924,)
(41356, 2) (10340, 2) (41356,) (10340,)
x_train.head(10)
headstexts
13202Received: from mail.com ([222.175.114.131])\n\...这里 所 凝聚 的 是 无数 网络 精英 的 心血 , 在 往下读 之前 , 请 先 让 自...
57760Received: from 126.com ([219.133.129.188])\n\t...\n贵 公司 负责人 :\n\n    你好 !\n\n    我 公司 为 深圳市 维拉\...
11829Received: from mail.cernet.com (staff.cernet.c...你 婆婆 的 表现 是 正常 的\n而 你 对 你 婆婆 有 这样 的 表现 没有 预计\n...
4439Received: from 163.com ([219.148.61.13])\n\tby...\n尊敬 的 阁下 :\n\n      我们 现在 正在 开展 一项 《 关于 青年 生活...
50671Received: from lnfzb.com ([221.222.182.164])\n...\n邮件 群发 - - - 最 直接 、 最 有效 的 广告 方式 !\n\n\n\n【 网...
12351Received: from 163.com ([219.133.131.33])\n\tb...贵 公司 负责人 ( 经理 / 财务 ) 您好 :\n我 公司 是 深圳市 华源 实业 有限...
25373Received: from 12565.com ([222.175.41.249])\n\...红 十月 商务 王是 一款 自动 为 企业 发布 产品 信息 的 软件 ( 能够 在 十分钟...
51314Received: from sea.net.edu.cn ([202.112.5.66])...如 题 , GG 会爱上 可 看透 你们 的 MM 吗 ? 你们 会 不会 觉得 这样 的 ...
24542Received: from 163.com ([219.134.22.61])\n\tby...尊敬 的 公司 您好 ! 打扰 之处 请 见谅 !\n我 深圳 公司 愿 在 互惠互利 、 ...
15069Received: from silversand.net ([219.136.103.68...- - - - - - - 中国式 执行 与 海尔 兵法 大 / 型 / 公 / 开 / 课...

5 数据处理

通过Create_Vec创建文本向量化器(vectorizer)对象

  • 通过选择不同参数来创建不同文vectorizer
  • V_type:可选CV(CountVectorizer)和TV(TfidfVectorizer)
  • max_df:用于指定词汇在文档中的最大出现频率。取值:0.0-1.0,例如0.8表示忽略在80%以上的文档中出现的词汇。
  • min_df:用于指定词汇在文档中的最小出现数目。取值:整数,例如2表示忽略在不到2个文档中出现的词汇。
# 通过Create_Vec创建文本向量化器(vectorizer)对象
def Create_Vec(V_type,max_df,min_df):if (V_type == 'CV'):vectorizer = CountVectorizer(max_df = max_df, min_df = min_df)elif (V_type == 'TV'):vectorizer = TfidfVectorizer(max_df = max_df, min_df = min_df)else:print('erro: vectorizer type wrong!')return 0return vectorizer
vectorizer = Create_Vec('TV',0.6,5)

根据创建的vectorizer来对数据集拟合转换

  • 分别对headstetxs列进行转换,得到两类稀疏矩阵

将拟合转换后得到的稀疏矩阵图形化展示出来

  • 利用plt.spy函数绘制稀疏矩阵非零元素分布
  • 其横坐标表示矩阵的列索引,纵坐标表示矩阵的行索引。
# 使用 fit_transform 进行拟合和转换
xheads_train = vectorizer.fit_transform(x_train['heads']) 
# 使用 transform 只进行数据的转换
xheads_test = vectorizer.transform(x_test['heads'])
print(xheads_train.shape, xheads_test.shape) # 输出矩阵大小plt.figure(figsize=(10, 8))
# plt.spy 函数用于绘制稀疏矩阵的非零元素分布,其中横坐标表示矩阵的列索引,纵坐标表示矩阵的行索引。
plt.spy(xheads_train, markersize=0.1, aspect='auto')
plt.xlabel('Features (Words)')
plt.ylabel('Documents')
plt.title('xheads_train')
plt.show()
(51696, 7802) (12924, 7802)

在这里插入图片描述

# 使用 fit_transform 进行拟合和转换
xtexts_train = vectorizer.fit_transform(x_train['texts']) 
# 使用 transform 只进行数据的转换
xtexts_test = vectorizer.transform(x_test['texts'])
print(xtexts_train.shape, xtexts_test.shape) # 输出矩阵大小plt.figure(figsize=(10, 8))
# plt.spy 函数用于绘制稀疏矩阵的非零元素分布,其中横坐标表示矩阵的列索引,纵坐标表示矩阵的行索引。
plt.spy(xtexts_train, markersize=0.1, aspect='auto')
plt.xlabel('Features (Words)')
plt.ylabel('Documents')
plt.title('xtexts_test')
plt.show()
(51696, 66591) (12924, 66591)

在这里插入图片描述

  • 密集的区域通常表示在相应的列(特征)上有许多非零元素
  • 稀疏的区域则表示在那些列上非零元素较少或没有
  • 在文本数据的情境中,每一列对应于一个词汇或特征。因此,密集的竖线表示在这些词汇中有一些在文档中频繁出现,而稀疏的区域表示在这些词汇中有些在文档中很少或根本不出现。

6 模型训练

分别对邮件头和正文两部分进行训练,再最后根据一定权重合并两个模型

6.1 邮件头训练

# 使用Multinomial Naive Bayes对邮件头进行训练
model_heads = MultinomialNB()
model_heads.fit(xheads_train, y_train)

获取 邮件头 中垃圾邮件与非垃圾邮件中权重最高的十个词汇,并利用柱状图显示

# 获取类别为垃圾邮件和非垃圾邮件的特征对数概率
spam_class_prob = model_heads.feature_log_prob_[1]
non_spam_class_prob = model_heads.feature_log_prob_[0]# 将对数概率转换为概率
prob_spam = np.exp(spam_class_prob)
prob_non_spam = np.exp(non_spam_class_prob)# 获取词汇表
vocab = np.array(vectorizer.get_feature_names_out())# 获取最大权重的索引(即对数概率最大的特征)
top_spam_words = np.argsort(spam_class_prob)[::-1][:10]
top_non_spam_words = np.argsort(non_spam_class_prob)[::-1][:10]# 输出垃圾邮件和非垃圾邮件的主要影响词汇
print("Top words for spam:")
print(vocab[top_spam_words])
print(prob_spam[top_spam_words])print("\nTop words for non-spam:")
print(vocab[top_non_spam_words])
print(prob_non_spam[top_non_spam_words])# 画图
plt.figure(figsize=(12, 6))
plt.rcParams['font.family'] = 'Microsoft YaHei'plt.subplot(1, 2, 1)
plt.barh(range(10), prob_spam[top_spam_words], color='blue')
plt.yticks(range(10), vocab[top_spam_words])
plt.gca().invert_yaxis()
plt.title('Top Words for Spam')plt.subplot(1, 2, 2)
plt.barh(range(10), prob_non_spam[top_non_spam_words], color='green')
plt.yticks(range(10), vocab[top_non_spam_words])
plt.gca().invert_yaxis()
plt.title('Top Words for Non-Spam')plt.tight_layout()
plt.show()
Top words for spam:
['0760' '21rgypq' '723' '86619861' '052' '00' '5628517' '64755262' '330''3126050']
[0.01191201 0.01068648 0.00904696 0.00887319 0.00887307 0.008534430.00790591 0.00784224 0.00745457 0.00742007]Top words for non-spam:
['87583640' '5468' '5kg' '34006833' '86545574' '039' '050810' '21rgypq''040969' '259']
[0.01826745 0.01589548 0.01152334 0.01011742 0.00788216 0.007837550.00766951 0.00763002 0.00701033 0.00699732]

在这里插入图片描述

6.2 邮件正文训练

# 使用Multinomial Naive Bayes对邮件正文进行训练
model_texts = MultinomialNB()
model_texts.fit(xtexts_train, y_train)

获取 邮件正文 中垃圾邮件与非垃圾邮件中权重最高的十个词汇,并利用柱状图显示

# 获取类别为垃圾邮件和非垃圾邮件的特征对数概率
spam_class_prob = model_texts.feature_log_prob_[1]
non_spam_class_prob = model_texts.feature_log_prob_[0]# 将对数概率转换为概率
prob_spam = np.exp(spam_class_prob)
prob_non_spam = np.exp(non_spam_class_prob)# 获取词汇表
vocab = np.array(vectorizer.get_feature_names_out())# 获取最大权重的索引(即对数概率最大的特征)
top_spam_words = np.argsort(spam_class_prob)[::-1][:10]
top_non_spam_words = np.argsort(non_spam_class_prob)[::-1][:10]# 输出垃圾邮件和非垃圾邮件的主要影响词汇
print("Top words for spam:")
print(vocab[top_spam_words])
print(prob_spam[top_spam_words])print("\nTop words for non-spam:")
print(vocab[top_non_spam_words])
print(prob_non_spam[top_non_spam_words])# 画图
plt.figure(figsize=(12, 6))
plt.rcParams['font.family'] = 'Microsoft YaHei'plt.subplot(1, 2, 1)
plt.barh(range(10), prob_spam[top_spam_words], color='blue')
plt.yticks(range(10), vocab[top_spam_words])
plt.gca().invert_yaxis()
plt.title('Top Words for Spam')plt.subplot(1, 2, 2)
plt.barh(range(10), prob_non_spam[top_non_spam_words], color='green')
plt.yticks(range(10), vocab[top_non_spam_words])
plt.gca().invert_yaxis()
plt.title('Top Words for Non-Spam')plt.tight_layout()
plt.show()
Top words for spam:
['公司' '发票' 'com' '合作' '优惠' 'http' '有限公司' '我司' '代开' 'www']
[0.00820575 0.0065605  0.00384043 0.00351144 0.00315074 0.002892910.00286094 0.0027401  0.00266387 0.00265106]Top words for non-spam:
['一个' '自己' '没有' '我们' '觉得' '时候' 'mm' '什么' '知道' '这个']
[0.00305769 0.00296125 0.00271538 0.00211419 0.00210244 0.002068280.00206705 0.00205005 0.00203816 0.00193213]

在这里插入图片描述

6.3 模型预测

分别根据邮件头模型与邮件正文模型进行预测

# 根据邮件头预测测试集
yheads_pred = model_heads.predict(xheads_test)# 根据邮件头训练模型评估模型性能
accuracy = accuracy_score(y_test, yheads_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")print("Classification Report:")
print(classification_report(y_test, yheads_pred, zero_division=1))
Accuracy: 99.85%
Classification Report:precision    recall  f1-score   support0       1.00      1.00      1.00      42631       1.00      1.00      1.00      8661accuracy                           1.00     12924macro avg       1.00      1.00      1.00     12924
weighted avg       1.00      1.00      1.00     12924

# 根据邮件正文预测测试集
ytexts_pred = model_texts.predict(xtexts_test)# 根据邮件正文训练模型评估模型性能
accuracy = accuracy_score(y_test, ytexts_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")print("Classification Report:")
print(classification_report(y_test, ytexts_pred, zero_division=1))
Accuracy: 97.76%
Classification Report:precision    recall  f1-score   support0       0.97      0.97      0.97      42631       0.98      0.98      0.98      8661accuracy                           0.98     12924macro avg       0.97      0.97      0.97     12924
weighted avg       0.98      0.98      0.98     12924

结果显示通过邮件头对垃圾邮件预测的准确率高于根据正文预测

6.4 根据不同权重融合两个模型

  • 权重选择

    • 因为上述结果显示根据邮件头预测的准确率更高,选择了将邮件头权重给到0.6,邮件正文权重给到0.4
    • 将根据不同权重结合邮件头与正文的预测结果,得到一个新的预测值
  • 阈值选择

    • 因为新的预测值由权重算来的,是一个0~1之间的浮点数,所以需要一个阈值来确定其是否判断为垃圾邮件
    • 通过绘制阈值与准确率的曲线找到最好的阈值与最高准确率
# 定义权重weight,根据权重分配预测参数
weight = 0.6
y_pred_avr = weight * yheads_pred + (1 - weight) * ytexts_pred# 定义阈值threshold ,根据阈值确定是否预测为
thresholds = np.arange(0, 1, 0.01)
accuracies = []for t in thresholds:y_pred = (y_pred_avr > t).astype(int)# 计算accuracyaccuracy = accuracy_score(y_test, y_pred)accuracies.append(accuracy)# 绘制阈值与accuracy关系曲线
plt.plot(thresholds, accuracies)
plt.xlabel('Threshold')
plt.ylabel('Accuracy') 
plt.title('Threshold vs Accuracy')
plt.show()# 找到最高accuracy的阈值    
best_threshold = thresholds[np.argmax(accuracies)]   
print("Best Threshold: ", best_threshold) 
print("Best Accuracy: ", max(accuracies))

在这里插入图片描述

Best Threshold:  0.4
Best Accuracy:  0.998529866914268
y_pred = (y_pred_avr > best_threshold).astype(int)# 评估模型性能
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")print("Classification Report:")
print(classification_report(y_test, y_pred, zero_division=1))
Accuracy: 99.85%
Classification Report:precision    recall  f1-score   support0       1.00      1.00      1.00      42631       1.00      1.00      1.00      8661accuracy                           1.00     12924macro avg       1.00      1.00      1.00     12924
weighted avg       1.00      1.00      1.00     12924

7 模型调优

7.1 确定最好的权重与阈值

通过嵌套for循环,选择最好的权重与阈值

# 定义权重和阈值的范围
weights = np.arange(0, 1, 0.01)
thresholds = np.arange(0, 1, 0.01)best_accuracy = 0
best_params = {'weight': None, 'threshold': None}# 遍历权重和阈值
for weight in weights:for threshold in thresholds:# 计算加权平均预测值y_pred_avr = weight * yheads_pred + (1 - weight) * ytexts_pred# 根据阈值确定二分类预测y_pred = (y_pred_avr > threshold).astype(int)# 计算准确率accuracy = accuracy_score(y_test, y_pred)# 更新最佳准确率和对应的参数if accuracy > best_accuracy:best_accuracy = accuracybest_params['weight'] = weightbest_params['threshold'] = threshold# 输出最佳参数和准确率
print("Best Weight:", best_params['weight'])
print("Best Threshold:", best_params['threshold'])
print("Best Accuracy:", best_accuracy)
Best Weight: 0.51
Best Threshold: 0.49
Best Accuracy: 0.998529866914268
y_pred = (y_pred_avr > best_threshold).astype(int)# 评估模型性能
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")print("Classification Report:")
print(classification_report(y_test, y_pred, zero_division=1))
Accuracy: 99.85%
Classification Report:precision    recall  f1-score   support0       1.00      1.00      1.00      42631       1.00      1.00      1.00      8661accuracy                           1.00     12924macro avg       1.00      1.00      1.00     12924
weighted avg       1.00      1.00      1.00     12924

通过添加权重的方法合并两个训练的模型,并选取最优的权重与阈值,结果与只对邮件头训练得到的模型预测值相同

说明在该方法中,正文内容对提升准确率没有帮助

7.2 词表大小对准确率影响分析

# 定义不同的词表大小
word_sizes = [100, 200, 500, 1000, 2000, 5000, 10000]# 存储结果的列表
accuracies = []# 循环实验
for word_size in tqdm(word_sizes):# 配置文本向量化器vectorizer = CountVectorizer(max_features=word_size)# 数据处理xheads_train = vectorizer.fit_transform(x_train['heads']) xheads_test = vectorizer.transform(x_test['heads'])xtexts_train = vectorizer.fit_transform(x_train['texts']) xtexts_test = vectorizer.transform(x_test['texts'])# 模型训练model_heads = MultinomialNB()model_heads.fit(xheads_train, y_train)model_texts = MultinomialNB()model_texts.fit(xtexts_train, y_train)# 模型预测yheads_pred = model_heads.predict(xheads_test)ytexts_pred = model_texts.predict(xtexts_test)# 模型融合weights = 0.6thresholds = 0.4y_pred_avr = weight * yheads_pred + (1 - weight) * ytexts_predy_pred = (y_pred_avr > threshold).astype(int)# 评估性能accuracy = accuracy_score(y_test, y_pred)# 存储结果accuracies.append(accuracy)# 绘制折线图
plt.plot(word_sizes, accuracies, marker='o')
plt.title('Model Performance vs. Word Size')
plt.xlabel('Word Size')
plt.ylabel('Accuracy')
plt.show()
████████████████████████████████████████████████████████████████████████████████████| 7/7 [02:46<00:00, 23.80s/it]

在这里插入图片描述

由图分析可见,词表越大,准确率越高,前期增长幅度较大,当词表大到一定程度增长减缓

7.3 通过遍历参数,选择最佳模型

通过遍历不同的vector类型、朴素贝叶斯算法、max_df范围、min_df范围等来确定最好的模型参数

# 设置参数范围
Vector_Types = ['CV','TV']          # vector类型
NBs = [BernoulliNB(), MultinomialNB(), ComplementNB()]  # 
max_dfs = np.arange(0.1, 1, 0.1)    # max_df范围
min_dfs = np.arange(1, 10, 1)     # min_df范围
weights = np.arange(0, 1, 0.1)     # 权重范围
thresholds = np.arange(0, 1, 0.1)  # 阈值范围best_accuracy_head = 0
best_params_head = {'Vector_Type': None, 'NB_Type': None, 'max_df': None, 'min_df': None}best_accuracy_text = 0
best_params_text = {'Vector_Type': None, 'NB_Type': None, 'max_df': None, 'min_df': None}total = len(Vector_Types) * len(NBs) * len(max_dfs) * len(min_dfs)
pbar = tqdm(total=total) for Vtype in Vector_Types:for NB in NBs:for max_df in max_dfs:for min_df in min_dfs:# print(f'Vtype:{Vtype},NB:{NB},max_df:{max_df},min_df:{min_df}')vectorizer = Create_Vec(Vtype,max_df,min_df)# 邮件头格式转换xheads_train = vectorizer.fit_transform(xt_train['heads']) xheads_test = vectorizer.transform(xt_test['heads'])NB.fit(xheads_train, yt_train) # 在训练集上训练yheads_pred = NB.predict(xheads_test) # 在测试集上预测,获得预测值accuracy_heads = accuracy_score(yt_test, yheads_pred) # 将测试预测值与测试集标签对比获得准确率# 迭代求最佳参数if accuracy_heads > best_accuracy_head:best_accuracy_head = accuracy_headsbest_params_head['Vector_Type'] = Vtypebest_params_head['NB_Type'] = NBbest_params_head['max_df'] = max_dfbest_params_head['min_df'] = min_dfpbar.update(1)pbar.close()print("Best Vector Type :", best_params_head['Vector_Type'])
print("Best NB Type:", best_params_head['NB_Type'])
print("Best max_df:", best_params_head['max_df'])
print("Best min_df:", best_params_head['min_df'])
print("Best Accuracy:", best_accuracy_head)
████████████████████████████████████████████████████████████████████████████████| 486/486 [50:57<00:00,  6.29s/it]Best Vector Type : TV
Best NB Type: MultinomialNB()
Best max_df: 0.1
Best min_df: 1
Best Accuracy: 0.9979690522243714


最终,确定了最佳参数为TfidfVectorizer(max_df = 0.1,min_df = 1)格式下训练出来的模型,用MultinomialNB()进行贝叶斯分析得到的准确率最高。

vectorizer = Create_Vec('TV',0.1,1)# 数据处理
xheads_train = vectorizer.fit_transform(x_train['heads']) 
xheads_test = vectorizer.transform(x_test['heads'])
xtexts_train = vectorizer.fit_transform(x_train['texts']) 
xtexts_test = vectorizer.transform(x_test['texts'])# 模型训练
model_heads = MultinomialNB()
model_heads.fit(xheads_train, y_train)
model_texts = MultinomialNB()
model_texts.fit(xtexts_train, y_train)# 模型预测
yheads_pred = model_heads.predict(xheads_test)
ytexts_pred = model_texts.predict(xtexts_test)# 模型调优
# 定义权重和阈值的范围
weights = np.arange(0, 1, 0.01)
thresholds = np.arange(0, 1, 0.01)best_accuracy = 0
best_params = {'weight': None, 'threshold': None}# 遍历权重和阈值
for weight in weights:for threshold in thresholds:# 计算加权平均预测值y_pred_avr = weight * yheads_pred + (1 - weight) * ytexts_pred# 根据阈值确定二分类预测y_pred = (y_pred_avr > threshold).astype(int)# 计算准确率accuracy = accuracy_score(y_test, y_pred)# 更新最佳准确率和对应的参数if accuracy > best_accuracy:best_accuracy = accuracybest_params['weight'] = weightbest_params['threshold'] = threshold# 输出最佳参数和准确率
print("Best Weight:", best_params['weight'])
print("Best Threshold:", best_params['threshold'])
print("Best Accuracy:", best_accuracy)
Best Weight: 0.51
Best Threshold: 0.49
Best Accuracy: 0.9986072423398329

最终选择了权重为0.6,阈值为0.4

best_weight = 0.6
best_threshold = 0.4y_pred_avr = best_weight * yheads_pred + (1 - best_weight) * ytexts_pred
y_pred = (y_pred_avr > best_threshold).astype(int)# 评估模型性能
best_acc = accuracy_score(y_test, y_pred) # 将测试预测值与测试集标签对比获得准确率
best_precision = precision_score(y_test, y_pred) # 精准率,判断为1的邮件中有多少真的为垃圾邮件,垃圾邮件分类任务中的重要指标,因为不希望将非垃圾邮件判为垃圾邮件
best_recall = recall_score(y_test, y_pred) # 召回率,真的垃圾邮件中有多少被识别出来
print(f'accuracy: {best_acc * 100:.4f}%, precision: {best_precision * 100:.4f}%, recall: {best_recall * 100:.4f}%') # 输出评价指标
accuracy: 99.8607%, precision: 100.0000%, recall: 99.7922%

8 总结

经过本次实验,我又学习到了更多的机器学习相关知识,也遇到了一些问题,收获丰富。

  1. 这次数据集量比以往都大,所以读取数据、训练上花的时间更长,为了方便展示进展,我使用了tqdm来显示进度条。
  2. 本次实验中含有许多的参数,为了找到最佳的参数,我进行了模型调优,要注意的是,调优时不能直接用测试集,不然会造成数据污染,应该在训练集上再进行划分进行数据调优。
  3. 本次实验中我分别训练了根据邮件头和正文的模型,并将两种模型以不同的权重结合起来,以一定的阈值确定最后的预测结果。然而,我发现两个模型结合后的结果并不比单用邮件头更好。

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

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

相关文章

【ADI 知识库】X 波段相控阵开发平台 硬件 2

ADAR1000EVAL1Z (Stingray) ADAR1000-EVAL1Z评估板是一款模拟波束成形前端&#xff0c;设计用于测试ADAR1000和ADTR1107的性能。ADAR1000 是一款 8 GHz 至 16 GHz、4 通道、X 波段和 Ku 波段波束成形器 IC。ADTR1107是 6 GHz 至 18 GHz 前端发送/接收模块。 ADAR1000-EVAL1Z板…

网络异常案例五_SYN被丢弃

问题现象 公司同事使用的时候&#xff0c;反馈系统不稳定&#xff0c;访问的时候&#xff0c;有时候会出现白屏&#xff08;连接超时&#xff09;&#xff0c;或者系统页面点击没有响应&#xff0c;过一会之后刷新系统又可以正常展示了。之前未收到过类似反馈&#xff0c;一直…

Axure 动态面板初使用 - 实现简单的Banner图轮播效果

使用工具版本 Axure 9 实现的效果 步骤过程 1、打开Axure工具&#xff0c;从元件库拖个动态面板到空白页&#xff1b; 2、给面板设置一个常用的banner尺寸&#xff0c;举个栗子&#xff1a;343151(移动端我常用的banner尺寸)&#xff0c;顺便给它起个名字&#xff0c;就叫…

QT学习日记 | 信号与槽

目录 前言 一、初始信号与槽 1、信号与槽的本质 2、信号与槽的使用 3、内置信号、内置槽函数与自定义信号、自定义槽函数 &#xff08;1&#xff09;文档查询 &#xff08;2&#xff09;自定义信号与内置槽函数的使用 4、信号与槽函数关联关系 5、带参数的信号与槽函数…

【软件设计师笔记】程序语言设计考点

【考证须知】IT行业高含金量的证书(传送门)&#x1f496; 【软件设计师笔记】计算机系统基础知识考点(传送门)&#x1f496; 【软件设计师笔记】操作系统考点(传送门)&#x1f496; &#x1f413; 编程语言之间的翻译形式 汇编 高级程序不能直接在计算机上执行&#xff0c;…

yolov8训练自己的关键点检测模型

参考&#xff1a; https://blog.csdn.net/weixin_38807927/article/details/135036450 标注数据集 安装labelme pip install labelme -i https://pypi.tuna.tsinghua.edu.cn/simple如果报错 $ labelme 2024-01-31 03:16:20,636 [INFO ] __init__:get_config:67- Loading …

YOLOv5改进系列(29)——添加DilateFormer(MSDA)注意力机制(中科院一区顶刊|即插即用的多尺度全局注意力机制)

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

面试宝典之深谈JVM

面试宝典之深谈JVM 1.为什么需要JVM&#xff0c;不要JVM可以吗&#xff1f; 1.JVM可以帮助我们屏蔽底层的操作系统 一次编译&#xff0c;到处运行 2.JVM可以运行Class文件 2.JDK&#xff0c;JRE以及JVM的关系 3.我们的编译器到底干了什么事&#xff1f; 仅仅是将我们的 .ja…

深入理解 Golang 的 crypto/elliptic:椭圆曲线密码学的实践指南

深入理解 Golang 的 crypto/elliptic&#xff1a;椭圆曲线密码学的实践指南 引言crypto/elliptic 库概览基本使用教程高级应用案例性能与安全考量结论 引言 在当今数字时代&#xff0c;数据安全和加密技术成为了信息技术领域的重中之重。特别是在网络通信和数据存储领域&#…

如何系统的自学Python?通义千问、讯飞星火、文心一言及ChatGPT的回答

如何系统的自学Python&#xff1f;来看看通义千问、讯飞星火、文心一言及ChatGPT的回答. 第一个是马老师的通义千问 系统地自学Python是一个循序渐进的过程&#xff0c;从基础语法到实践项目&#xff0c;再到专业领域的深入学习。下面是一个详细的步骤指南&#xff1a; 了解Py…

控制台npm start终止不了?

控制台npm start终止不了&#xff1f; 在开发的过程中我遇到了这样的问题&#xff0c;想结束控制台3002端口运行&#xff0c;但是ControlC不起作用&#xff0c;不管我敲多少遍&#xff0c;依旧没有任何动静&#xff1a; 再次启动的时候它又会自动启动3003端口&#xff0c;300…

Kotlin 协程:深入理解 ‘async { }‘

Kotlin 协程&#xff1a;深入理解 ‘async { }’ Kotlin 协程是一种强大的异步编程工具&#xff0c;它提供了一种简洁、易读的方式来处理并发和异步操作。在 Kotlin 协程库中&#xff0c;async {} 是一个关键的函数&#xff0c;它允许我们启动一个新的协程&#xff0c;并返回一…

【大厂AI课学习笔记】1.4 算法的进步(2)

关于感知器的兴衰。 MORE&#xff1a; 感知器的兴衰 一、感知器的发明与初期振动 在人工智能的历史长河中&#xff0c;感知器&#xff08;Perceptron&#xff09;无疑是一个里程碑式的存在。它最初由心理学家Frank Rosenblatt在1950年代提出&#xff0c;并在随后的几年中得到…

【数字电子技术课程设计】多功能数字电子钟的设计

目录 摘要 1 设计任务要求 2 设计方案及论证 2.1 任务分析 2.1.1 晶体振荡器电路 2.1.2 分频器电路 2.1.3 时间计数器电路 2.1.4 译码驱动电路 2.1.5 校时电路 2.1.6 整点报时/闹钟电路 2.2 方案比较 2.3 系统结构设计 2.4 具体电路设计 3 电路仿真测试及结…

京东物流基于 StarRocks 的数据分析平台建设

作者&#xff1a;京东物流 数据专家 刘敬斌 小编导读&#xff1a; 京东集团 2007 年开始自建物流&#xff0c;2017 年 4 月正式成立京东物流集团&#xff0c;截至目前&#xff0c;京东物流已经构建了一套全面的智能物流系统&#xff0c;实现服务自动化、运营数字化及决策智能化…

开源编辑器:ONLYOFFICE文档又更新了!

办公软件 ONLYOFFICE文档最新版本 8.0 现已发布&#xff1a;PDF 表单、RTL、单变量求解、图表向导、插件界面设计等更新。 什么是 ONLYOFFICE 文档 ONLYOFFICE 文档是一套功能强大的文档编辑器&#xff0c;支持编辑处理文本文档、电子表格、演示文稿、可填写的表单、PDF&#…

Java基础学习:System类和Static方法的实际使用

一、System类 1.在程序开发中&#xff0c;我们需要对这个运行的结果进行检验跟我们预判的结果是否一致&#xff0c;就会用到打印结果在控制台中显示出来使用到了System类。System类定义了一些和系统相关的属性和方法&#xff0c;它的属性和方法都是属于静态的&#xff0c;想使用…

数字孪生 三维建模方式以及细节步骤流程

对于数字孪生这个概念&#xff0c;三维建模不同行业认知都不尽相同。有的行业认为数字孪生重点在于建模&#xff0c;有的行业认为在于物联感知&#xff0c;还有部分认为是虚拟仿真。今天重点从建模角度和大家谈谈数字孪生技术常用的三维建模方式以及精细度分级。 数字孪生平台…

钉钉群机器人-发送群消息

1、钉钉群创建机器人 添加完成后&#xff0c;要记住 Webhook 路径&#xff1b; 2、机器人接入文档网址 自定义机器人接入 - 钉钉开放平台 3、JAVA代码 import com.dingtalk.api.DefaultDingTalkClient; import com.dingtalk.api.DingTalkClient; import com.dingtalk.api.re…

【靶场实战】Pikachu靶场XSS跨站脚本关卡详解

Nx01 系统介绍 Pikachu是一个带有漏洞的Web应用系统&#xff0c;在这里包含了常见的web安全漏洞。 如果你是一个Web渗透测试学习人员且正发愁没有合适的靶场进行练习&#xff0c;那么Pikachu可能正合你意。 Nx02 XSS跨站脚本概述 Cross-Site Scripting 简称为“CSS”&#xff…