(一)实验目的与背景

        随着电子邮件的普及,垃圾邮件的数量急剧增加,严重影响了用户的使用体验。为了提高邮件分类的准确性和效率,本实验基于kaggle上的垃圾邮件数据集,采用朴素贝叶斯算法对垃圾邮件进行分类,构建一个能够准确预测邮件是否为垃圾邮件的模型,并通过数据预处理、模型优化和可视化分析提升分类效果。

        房屋价格数据集来源: Email Classification ; 分别包含 “test” 和 “target” 2个列,其中 “test” 列为邮件文本,“target” 列对应是否为垃圾邮件;数据集共包含5826个样本。

        朴素贝叶斯分类器原理为基于特征条件独立假设,利用贝叶斯定理计算后验概率,将样本分类到概率最大的类别中,适用于分类任务。

(二)实验过程

1. 数据加载与数据划分:

        数据加载:使用pandas读取CSV文件,并将数据集分为训练集(80%)和测试集(20%)。

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report, accuracy_score

df = pd.read_csv('C:/Users/ASUS/Desktop/机器学习文件/机器学习实践文件/实验5/spam_assassin.csv')# 下载到本地的数据集

X = df['text']  # 取出数据中的文本内容列
y = df['target']  # 取出数据中的标签列(0=正常邮件,1=垃圾邮件)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=7) # 数据切分成为训练集和测试集

2. 文本向量化

        初始向量化:使用CountVectorizer移除英语停用词,统计词频

# 文本向量化 - 词频统计
vectorizer = CountVectorizer(stop_words='english') # 移除英语停用词 自动忽略"the", "and", "is"这些没区分度的词
X_train_counts = vectorizer.fit_transform(X_train) # 先学习训练集的词汇表(建立词典)然后把训练集文本转成数字矩阵
X_test_counts = vectorizer.transform(X_test) # 用训练好的字典转换测试集
# 统计
print("Vocabulary size:", len(vectorizer.get_feature_names_out())) # 统计词汇表大小
print("Sample features:", vectorizer.get_feature_names_out()[:10]) # 显示前十个词

        输出结果为: 

        可以看出,初步向量化后的文本仍包含数字与符号等无意义的字段,需要重新进行停用词移除进行优化。

        优化向量化:过滤无意义词汇,保留长度≥3的纯英文词,并忽略出现次数少于5次的词。

# 词汇表中出现大量无意义词汇,需要过滤
vectorizer = CountVectorizer(
    token_pattern=r'(?u)\b[A-Za-z]{3,}\b',  # 只保留3个字母以上的纯英文词
    stop_words='english',                   # 移除英语停用词
    min_df=5                                # 忽略出现少于5次的词
)
X_train_counts = vectorizer.fit_transform(X_train) # 先学习训练集的词汇表(建立词典)然后把训练集文本转成数字矩阵
X_test_counts = vectorizer.transform(X_test) # 用训练好的字典转换测试集
# 统计
print("Vocabulary size:", len(vectorizer.get_feature_names_out())) # 统计词汇表大小
print("Sample features:", vectorizer.get_feature_names_out()[:10]) # 显示前十个词

        输出结果为:

        可以看出,优化·向量化后的文本数量较优化前大幅下降,且均为有效字段,使得数据能较好的被模型所利用。

3. 模型训练与评估

# 初始化并训练朴素贝叶斯分类器
nb_classifier = MultinomialNB() # 初始化朴素贝叶斯分类器
nb_classifier.fit(X_train_counts, y_train) # 训练模型

# 在测试集上进行预测
y_pred = nb_classifier.predict(X_test_counts)

# 评估模型性能
print("Accuracy:", accuracy_score(y_test, y_pred))
print("\nClassification Report:")
print(classification_report(y_test, y_pred))

        输出结果为:

        可以看出,初始模型的预测效果十分好,后续尝试通过网格搜索寻找最优参数进一步优化模型。

4. 模型优化(网格搜索最优参数)

# 模型优化 (网格搜索)
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV

# 创建pipeline(管道)
pipeline = Pipeline([
    ('vect', CountVectorizer()), # 切词
    ('clf', MultinomialNB())     # 分类
]) 

# 定义超参数搜索空间
parameters = {
    'vect__ngram_range': [(1, 1), (1, 2)],  # 分词粒度 (只使用单个词或者使用单+双词)
    'vect__max_df': [0.5, 0.75, 1.0],       # 忽略高频词 (0.5/0.75/1.0:忽略在50%/75%/不忽略以上文档中出现的词)
    'vect__min_df': [1, 2],                 # 忽略低频词 (保留至少出现1次的词或者保留至少出现2次的词)
    'clf__alpha': [0.1, 0.5, 1.0]           # 平滑强度    (0.1强平滑(对罕见词更敏感) 0.5中等平滑 1.0	默认平滑强度)
} 

# 网格搜索配置
grid_search = GridSearchCV(
    pipeline,          # 要优化的流程
    parameters,        # 参数搜索空间
    cv=3,             # 3折交叉验证
    n_jobs=-1         # 使用所有CPU核心并行计算
)

# 执行网格搜索
grid_search.fit(X, y)  # X=文本数据,y=标签

# 获取最佳结果
print("最佳参数:", grid_search.best_params_)
print("最佳得分:", grid_search.best_score_)

        输出结果为:

        得到最优参数,随后进行预测并与初始模型的预测效果进行对比。

5. 模型评估可视化分析

        进行最优参数模型与初始模型的性能对比可视化

# 结果分析比较
from sklearn.metrics import roc_curve, auc
from sklearn.metrics import confusion_matrix
# 最佳模型评估
best_clf = grid_search.best_estimator_
best_clf.fit(X_train, y_train)
y_pred_optimized = best_clf.predict(X_test)

# 比较基准模型和优化模型
print("Baseline Accuracy:", accuracy_score(y_test, y_pred))
print("Optimized Accuracy:", accuracy_score(y_test, y_pred_optimized))

# 计算基准模型的预测概率
y_pred_proba_baseline = nb_classifier.predict_proba(X_test_counts)[:, 1]

# 计算优化模型的预测概率
y_pred_proba_optimized = best_clf.predict_proba(X_test)[:, 1]

# 计算基准模型的ROC曲线和AUC
fpr_baseline, tpr_baseline, _ = roc_curve(y_test, y_pred_proba_baseline)
roc_auc_baseline = auc(fpr_baseline, tpr_baseline)

# 计算优化模型的ROC曲线和AUC
fpr_optimized, tpr_optimized, _ = roc_curve(y_test, y_pred_proba_optimized)
roc_auc_optimized = auc(fpr_optimized, tpr_optimized)

# 绘制ROC曲线
plt.figure(figsize=(10, 6))
plt.plot(fpr_baseline, tpr_baseline, color='darkorange', lw=2, label=f'基准模型ROC曲线(area = {roc_auc_baseline:.2f})')
plt.plot(fpr_optimized, tpr_optimized, color='blue', lw=2, label=f'优化后模型ROC曲线(area = {roc_auc_optimized:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('基准模型与优化后模型的ROC曲线')
plt.legend(loc="lower right")
plt.show()

conf_matrix_optimized = confusion_matrix(y_test, y_pred_optimized)
plt.figure(figsize=(8, 6))
plt.imshow(conf_matrix_optimized, interpolation='nearest', cmap=plt.cm.Blues)
plt.title('优化后模型的混淆矩阵')
plt.colorbar()
tick_marks = [0, 1]
plt.xticks(tick_marks, ['正常邮件', '垃圾邮件'], rotation=45)
plt.yticks(tick_marks, ['正常邮件', '垃圾邮件'])
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()

# 混淆矩阵
conf_matrix_baseline = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
plt.imshow(conf_matrix_baseline, interpolation='nearest', cmap=plt.cm.Blues)
plt.title('基准模型的混淆矩阵')
plt.colorbar()
tick_marks = [0, 1]
plt.xticks(tick_marks, ['正常邮件', '垃圾邮件'], rotation=45)
plt.yticks(tick_marks, ['正常邮件', '垃圾邮件'])
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()

         输出结果为:

 

         可以看出,优化后模型相较初始模型有所提升,因为数据集类别对应文本内容区分度较大,使得无论初始模型效果还是优化后模型效果都十分优秀。

(三)总代码实现

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report, accuracy_score
from sklearn.metrics import roc_curve, auc
from sklearn.metrics import confusion_matrix

df = pd.read_csv('C:/Users/ASUS/Desktop/机器学习文件/机器学习实践文件/实验5/spam_assassin.csv')

X = df['text']  # 取出数据中的文本内容列
y = df['target']  # 取出数据中的标签列(0=正常邮件,1=垃圾邮件)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=7) # 数据切分成为训练集和测试集

# 文本向量化 - 词频统计
vectorizer = CountVectorizer(stop_words='english') # 移除英语停用词 自动忽略"the", "and", "is"这些没区分度的词
X_train_counts = vectorizer.fit_transform(X_train) # 先学习训练集的词汇表(建立词典)然后把训练集文本转成数字矩阵
X_test_counts = vectorizer.transform(X_test) # 用训练好的字典转换测试集

# 统计
print("Vocabulary size:", len(vectorizer.get_feature_names_out())) # 统计词汇表大小
print("Sample features:", vectorizer.get_feature_names_out()[:10]) # 显示前十个词

# 词汇表中出现大量无意义词汇,需要过滤
vectorizer = CountVectorizer(
    token_pattern=r'(?u)\b[A-Za-z]{3,}\b',  # 只保留3个字母以上的纯英文词
    stop_words='english',                   # 移除英语停用词
    min_df=5                                # 忽略出现少于5次的词
)

X_train_counts = vectorizer.fit_transform(X_train) # 先学习训练集的词汇表(建立词典)然后把训练集文本转成数字矩阵
X_test_counts = vectorizer.transform(X_test) # 用训练好的字典转换测试集

# 初始化并训练朴素贝叶斯分类器
nb_classifier = MultinomialNB() # 初始化朴素贝叶斯分类器
nb_classifier.fit(X_train_counts, y_train) # 训练模型

# 在测试集上进行预测
y_pred = nb_classifier.predict(X_test_counts)

# 评估模型性能
print("Accuracy:", accuracy_score(y_test, y_pred))
print("\nClassification Report:")
print(classification_report(y_test, y_pred))

# 模型优化 (网格搜索)
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV

# 创建pipeline(管道)
pipeline = Pipeline([
    ('vect', CountVectorizer()), # 切词
    ('clf', MultinomialNB())     # 分类
]) 

# 定义超参数搜索空间
parameters = {
    'vect__ngram_range': [(1, 1), (1, 2)],  # 分词粒度 (只使用单个词或者使用单+双词)
    'vect__max_df': [0.5, 0.75, 1.0],       # 忽略高频词 (0.5/0.75/1.0:忽略在50%/75%/不忽略以上文档中出现的词)
    'vect__min_df': [1, 2],                 # 忽略低频词 (保留至少出现1次的词或者保留至少出现2次的词)
    'clf__alpha': [0.1, 0.5, 1.0]           # 平滑强度    (0.1强平滑(对罕见词更敏感) 0.5中等平滑 1.0	默认平滑强度)
} 

# 网格搜索配置
grid_search = GridSearchCV(
    pipeline,          # 要优化的流程
    parameters,        # 参数搜索空间
    cv=3,             # 3折交叉验证
    n_jobs=-1         # 使用所有CPU核心并行计算
)

# 执行网格搜索
grid_search.fit(X, y)  # X=文本数据,y=标签

# 获取最佳结果
print("最佳参数:", grid_search.best_params_)
print("最佳得分:", grid_search.best_score_)

# 结果分析比较

# 最佳模型评估
best_clf = grid_search.best_estimator_
best_clf.fit(X_train, y_train)
y_pred_optimized = best_clf.predict(X_test)

# 比较基准模型和优化模型
print("Baseline Accuracy:", accuracy_score(y_test, y_pred))
print("Optimized Accuracy:", accuracy_score(y_test, y_pred_optimized))

# 计算基准模型的预测概率
y_pred_proba_baseline = nb_classifier.predict_proba(X_test_counts)[:, 1]

# 计算优化模型的预测概率
y_pred_proba_optimized = best_clf.predict_proba(X_test)[:, 1]

# 计算基准模型的ROC曲线和AUC
fpr_baseline, tpr_baseline, _ = roc_curve(y_test, y_pred_proba_baseline)
roc_auc_baseline = auc(fpr_baseline, tpr_baseline)

# 计算优化模型的ROC曲线和AUC
fpr_optimized, tpr_optimized, _ = roc_curve(y_test, y_pred_proba_optimized)
roc_auc_optimized = auc(fpr_optimized, tpr_optimized)

# 绘制ROC曲线
plt.figure(figsize=(10, 6))
plt.plot(fpr_baseline, tpr_baseline, color='darkorange', lw=2, label=f'基准模型ROC曲线(area = {roc_auc_baseline:.2f})')
plt.plot(fpr_optimized, tpr_optimized, color='blue', lw=2, label=f'优化后模型ROC曲线(area = {roc_auc_optimized:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('基准模型与优化后模型的ROC曲线')
plt.legend(loc="lower right")
plt.show()

from sklearn.metrics import confusion_matrix
conf_matrix_optimized = confusion_matrix(y_test, y_pred_optimized)
plt.figure(figsize=(8, 6))
plt.imshow(conf_matrix_optimized, interpolation='nearest', cmap=plt.cm.Blues)
plt.title('优化后模型的混淆矩阵')
plt.colorbar()
tick_marks = [0, 1]
plt.xticks(tick_marks, ['正常邮件', '垃圾邮件'], rotation=45)
plt.yticks(tick_marks, ['正常邮件', '垃圾邮件'])
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()

# 混淆矩阵
conf_matrix_baseline = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
plt.imshow(conf_matrix_baseline, interpolation='nearest', cmap=plt.cm.Blues)
plt.title('基准模型的混淆矩阵')
plt.colorbar()
tick_marks = [0, 1]
plt.xticks(tick_marks, ['正常邮件', '垃圾邮件'], rotation=45)
plt.yticks(tick_marks, ['正常邮件', '垃圾邮件'])
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.show()

(四)总结 

        本实验基于朴素贝叶斯原理,通过文本向量化与网格搜索等步骤成功构造了一个可以识别垃圾邮件的分类模型, 未来可以的改进方向有:尝试其他文本表示方法(如TF-IDF),或进一步优化停用词表和特征选择策略。

 

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐