L4 垃圾邮件数据集分类延申 - NB/KNN/SVC/随机森林

server/2024/11/15 5:57:08/

目录

背景

代码

1. 数据准备-1阶段

2. 数据探索

3. 数据准备-2阶段

4. 矢量化(向量化)

5. 建立模型

6. 模型评价

模型评价

1. 分别评价

2. 总体评价


背景

基于前文 (《【机器学习】Lesson 4 - 朴素贝叶斯(NB)文本分类》)对于垃圾邮件数据集的分类处理,增加 K邻居分类器(KNN)/支持向量机(SVC)/随机森林 算法模型,并通过生成准确性报告和绘制混淆矩阵,对不同模型的性能进行比较。同时丰富数据预处理部分,增加词云展现等,完整代码及跑通效果见文档绑定资源。

本文在代码解释部分会跳过与前文雷同部分,详细可以在本文绑定资源中下载完整跑通源码查看。

代码

用大白话来讲的话,就是:

我们需要用着 5572 条邮件训练一个预测模型,以达到对邮件【是否】属于【垃圾邮件】进行预测分析。为了达到这一目的,我们选取的数据集是已经人为标注为非垃圾邮件("ham")和垃圾邮件("spam")过的。随后将数据分为一大半(训练集)和一小半(测试集),用训练集训练一个模型,然后用模型预测测试集,再用得到的结果与测试集已有标注进行比对,然后得到召回率、正确率等等评估模型性能的评分结果数据。

具体按照一定步骤如下:

1. 数据准备-1阶段

  1. 导包、导数据
  2. 预处理:即根据得到的数据集情况,删除多余的列、填补空值等等
  3. 数据转化:如果是中文标签的话,换成数字型,机器才看得懂。如本例中将 'spam' 转化为 1,'ham' 转化为 0。
## 数据处理类
import numpy as np 
import pandas as pd 
import warnings## 数据可视化类
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
#from matplotlib.colors import ListedColormap #创建颜色映射表import wordcloud
from wordcloud import WordCloud## 自然语言处理类
import re #处理文本字符串,如查找、替换、分割和匹配
import nltk #文本预处理(如分词、词干提取、停用词过滤)、词性标注、命名实体识别、情感分析等各种自然语言处理任务
from nltk.corpus import stopwords #文本预处理
from nltk.tokenize import word_tokenize #分割文本
from nltk.stem.porter import PorterStemmer #词干提取算法
from nltk.stem import WordNetLemmatizer #去标点符号
from collections import Counterfrom sklearn.preprocessing import MinMaxScaler,LabelEncoder 
# MinMaxScaler 能将类别型数据转换为数字
# LabelEncoder 进行特征放缩
from sklearn.feature_extraction.text import TfidfVectorizer #将原生文档转化为txt矩阵## 分析算法类
from sklearn.naive_bayes import MultinomialNB #朴素贝叶斯
from sklearn.ensemble import RandomForestClassifier # 随机森林
from sklearn.neighbors import KNeighborsClassifier # KNN
from sklearn.svm import SVC # 支持向量机## 算法优化类
from sklearn.pipeline import Pipeline #流水线操作
from sklearn.model_selection import GridSearchCV,cross_val_score,train_test_split 
# GridSearchCV 网格搜索,cross_val_score 交叉验证,train_test_split 数据划分## 算法评分类
from sklearn import metrics
from sklearn.metrics import precision_score, recall_score, plot_confusion_matrix, classification_report, accuracy_score, f1_score# 加载数据集
data = pd.read_csv("D:/project/Jupyter/csdn/AI_ML/datasets/Lesson4_SMS.csv")data.info()# 去除后面三列无意义列
to_drop = ["Unnamed: 2","Unnamed: 3","Unnamed: 4"]
data = data.drop(data[to_drop], axis=1)# 重命名前两列 
data.rename(columns = {"v1":"Target", "v2":"Text"}, inplace = True)
data.head()

2. 数据探索

根据已有数据的情况,对数据进行一些统计探索,必要时可以进行数据可视化,使探索更加直观,根据展现出的情况对信息进行处理。

  1. 数据可视化:初步,从种类记数、高频词排序、占比等角度绘制图形。
  2. 特征工程:目的是最大限度地从原始数据中提取特征以供算法和模型使用。本例中将邮件内容按照字符数、词数、句子数,对文本内容进行拆分,并进行可视化碳素两两之间的数量关系。
  3. 异常值处理:此时出现一些明显不对劲的数据,为异常值,如下图红框部分,则需要根据其所在范围进行剔除,提高模型的准确性。

#first of all let us evaluate the target and find out if our data is imbalanced or not
plt.figure(figsize=(6,4))
fg = sns.countplot(x= data["Target"])
fg.set_title("Count Plot of Classes")
fg.set_xlabel("Classes")
fg.set_ylabel("Number of Data points")## 词云
# 数据编码
data['spam'] = data['Target'].map( {'spam': 1, 'ham': 0} ).astype(int)
data.head(5)# 提取出内容列的字符数
data["No_of_Characters"] = data["Text"].apply(len)# 分离好坏类的短信,并以句子字符数为横轴绘图
data.hist(column='No_of_Characters', by='Target', bins=60, figsize=(12, 4))
plt.xlim(-40, 950)
plt.xlabel('Length')  # 设置横轴标签
plt.ylabel('Count')   # 设置纵轴标签plt.show()data_ham  = data[data['spam'] == 0].copy()
data_spam = data[data['spam'] == 1].copy()def show_wordcloud(data_spam_or_ham, title):Text = ' '.join(data_spam_or_ham['Text'].astype(str).tolist())stopwords = set(wordcloud.STOPWORDS)fig_wordcloud = wordcloud.WordCloud(stopwords=stopwords,background_color='lightgrey',colormap='viridis', width=800, height=600).generate(Text)plt.figure(figsize=(10,7), frameon=True)plt.imshow(fig_wordcloud)  plt.axis('off')plt.title(title, fontsize=20 )plt.show()show_wordcloud(data_ham, "Ham messages")show_wordcloud(data_spam, "Spam messages")## 特征工程
#添加列:字符数、单词数和句子数# data["No_of_Characters"] = data["Text"].apply(len) 
# 字符数前面做词云的时候已经加了,遂注释掉data["No_of_Words"]=data.apply(lambda row: nltk.word_tokenize(row["Text"]), axis=1).apply(len)
# 创建新列"No_of_Words",apply 将一个函数应用到 DataFrame 的每一行(axis=1),定义了一个匿名函数lambda接受行作为输入
# 使用 word_tokenize 函数对该行中的 Text 列进行分词,返回一个单词列表,使用 apply(len) 计算分词结果的长度,即单词的数量data["No_of_sentence"]=data.apply(lambda row: nltk.sent_tokenize(row["Text"]), axis=1).apply(len)
# sent_tokenize 函数返回的是句子列表data.describe().T # .T 是 DataFrame 的转置操作,将行和列互换plt.figure(figsize=(12,8))
fg = sns.pairplot(data=data, hue="Target")
plt.show(fg)

3. 数据准备-2阶段

如果是数据型数据的话,这一步应该在第一步的数据准备里的,但是由于本篇代码属于自然语言处理,数据清洗需要放置于数据探索流程之后。其他的数据准备工作也相对来说复杂一些。

  1. 数据清洗:本例中,需要删除标点符号和数字,将所有字母改为小写。由于本篇目的为判断文本的属性,所以着重分析的是实词的感情色彩,数量含义可忽略。
  2. 文本分解:“token” 是指文本中的一个基本单位,文本通常需要转换成数字表示才能作为输入传递给算法进行处理。将文本句子分解为 token,方便后续将文本转换为数字化的向量,便于读取。
  3. 删除停用词:停用词是经常出现的单词(例如 Few、is、an 等)。这些词在句子结构中有意义,但对 NLP 中的语言处理贡献不大。为了消除处理中的冗余,需要将删除这些内容。标准选用 NLTK 库内的默认停用词组。
  4. 词形还原:词干提取是获取单词词根形式的过程。单词的词干是通过删除单词的前缀或后缀而创建的,词干提取会将其带回词根。词形还原还将单词转换为其词根形式。
# 查看数据
print("\033[1m\u001b[45;1m The First 5 Texts:\033[0m",*data["Text"][:5], sep = "\n")# 编写数据清理函数
def Clean(Text):sms = re.sub('[^a-zA-Z]', ' ', Text) #Replacing all non-alphabetic characters with a spacesms = sms.lower() #converting to lowecasesms = sms.split()sms = ' '.join(sms)return smsdata["Clean_Text"] = data["Text"].apply(Clean)# 查看清理后数据
print("\033[1m\u001b[45;1m The First 5 Texts after cleaning:\033[0m",*data["Clean_Text"][:5], sep = "\n")data["Tokenize_Text"]=data.apply(lambda row: nltk.word_tokenize(row["Clean_Text"]), axis=1)print("\033[1m\u001b[45;1m The First 5 Texts after Tokenizing:\033[0m",*data["Tokenize_Text"][:5], sep = "\n")# 删除停用词
def remove_stopwords(text):stop_words = set(stopwords.words("english"))filtered_text = [word for word in text if word not in stop_words]return filtered_textdata["Nostopword_Text"] = data["Tokenize_Text"].apply(remove_stopwords)print("\033[1m\u001b[45;1m The First 5 Texts after removing the stopwords:\033[0m",*data["Nostopword_Text"][:5], sep = "\n")lemmatizer = WordNetLemmatizer()def lemmatize_word(text):#word_tokens = word_tokenize(text)# provide context i.e. part-of-speechlemmas = [lemmatizer.lemmatize(word, pos ='v') for word in text]return lemmasdata["Lemmatized_Text"] = data["Nostopword_Text"].apply(lemmatize_word)
print("\033[1m\u001b[45;1m The First 5 Texts after lemitization:\033[0m",*data["Lemmatized_Text"][:5], sep = "\n")

4. 矢量化(向量化)

在 NLP 中,清理后的数据需要转换为数字格式,其中每个单词都由一个矩阵表示。这也称为词嵌入或词向量化。

  1. 创建词形还原文本语料库
  2. 将语料库转换为向量形式
  3. 对 Target 中的类进行标签编码
#词频 (TF) = (文档中某个词的频率)/(文档中词的总数) 
#逆文档频率 (IDF) = log( (文档总数)/(包含词 t 的文档数) 
#I将使用 TfidfVectorizer() 对预处理后的数据进行矢量化。#创建文本特征语料库
corpus= []
for i in data["Lemmatized_Text"]:msg = ' '.join([row for row in i])corpus.append(msg)corpus[:5]
print("\033[1m\u001b[45;1m The First 5 lines in corpus :\033[0m",*corpus[:5], sep = "\n")# 矢量化
tfidf = TfidfVectorizer()
X = tfidf.fit_transform(corpus).toarray()
# 查看特征
X.dtype# 对目标进行标签编码并将其用作 y
label_encoder = LabelEncoder()
data["Target"] = label_encoder.fit_transform(data["Target"])

5. 建立模型

本文使用了 4 种不同的模型分别对数据集进行分类,均是从 sklearn 中提取的已经封装好的模型,故可以直接构建函数,实现分别训练和交叉验证,并得到正确率。

  1. 将特征和目标设置为 X 和 y,拆分测试集和训练集
  2. 为四个不同的分类构建模型:朴素贝叶斯(NB);K邻居分类器(KNN);支持向量机(SVC);随机森林
  3. 将所有模型拟合到训练数据集上
  4. 对所有模型的训练集进行交叉验证以确保准确性
y = data["Target"] 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 将他们投入不同分类模型
classifiers = [MultinomialNB(), RandomForestClassifier(),KNeighborsClassifier(), SVC()]
for cls in classifiers:cls.fit(X_train, y_train)# 创建模型的对应字典,便于后续的函数读取
pipe_dict = {0: "NaiveBayes", 1: "RandomForest", 2: "KNeighbours",3: "SVC"}# 交叉验证
for i, model in enumerate(classifiers):cv_score = cross_val_score(model, X_train,y_train,scoring="accuracy", cv=10)print("%s: %f " % (pipe_dict[i], cv_score.mean()))

6. 模型评价

对训练好的模型进行性能评估,以了解模型在未见过的新数据上的表现。本文中使用的评价方法为:生成准确性报告以及混淆矩阵。

  1. 准确性报告:列出表格,从 准确率、召回率、F1 分数、测试集准确率、训练集准确率 5个维度得出数据结论,便于一目了然地纵向对比。
  2. 混淆矩阵:比较模型时,TN和TP对角线两数值越大,FP和FN对角线两数值越小,模型性能越好。分类越准确混淆矩阵分为四块,分别为:
名称(位置)含义
TN(左上)非垃圾短信被正确地预测为非垃圾短信
FP(右上)非垃圾短信被错误地预测为垃圾短信(误报)
FN(左下)垃圾短信被错误地预测为非垃圾短信(漏报)
TP(右下)垃圾短信被正确地预测为垃圾短信
precision =[]
recall =[]
f1_score = []
trainset_accuracy = []
testset_accuracy = []for i in classifiers:pred_train = i.predict(X_train)pred_test = i.predict(X_test)prec = metrics.precision_score(y_test, pred_test)recal = metrics.recall_score(y_test, pred_test)f1_s = metrics.f1_score(y_test, pred_test)train_accuracy = model.score(X_train,y_train)test_accuracy = model.score(X_test,y_test)#评分precision.append(prec)recall.append(recal)f1_score.append(f1_s)trainset_accuracy.append(train_accuracy)testset_accuracy.append(test_accuracy)# 初始化一个列表
data = {'Precision':precision,
'Recall':recall,
'F1score':f1_score,
'Accuracy on Testset':testset_accuracy,
'Accuracy on Trainset':trainset_accuracy}
# 创建表格
Results = pd.DataFrame(data, index =["NaiveBayes", "RandomForest", "KNeighbours","SVC"])#在同一图中绘制多个分类模型的混淆矩阵,以便比较分类模型的性能
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12,8))for cls, ax in zip(classifiers, axes.flatten()):plot_confusion_matrix(cls, X_test, y_test, ax=ax)ax.title.set_text(type(cls).__name__)
plt.tight_layout()  
plt.show()

模型评价

结合代码生成的准确性报告,以及混淆矩阵,对本文使用的四种模型进行评价分析。

1. 分别评价

  1. 朴素贝叶斯 (NB)

    • 准确率(Precision): 完美(1.0)。
    • Recall: 较低(0.705882),说明漏检率较高,有一定比例的垃圾短信未被识别(40个FN)。
    • F1 Score: 0.827586,主要是召回率拖累了整体分数,但是依然是较高的。
    • 总结: 适合不在意漏检的情况,适合需要更高精度的应用。
  2. 随机森林分类

    • 准确率(Precision): 完美(1.0)。
    • Recall: 较高(0.823529),FN较少(24个),说明对垃圾短信识别较好。
    • F1 Score: 0.903226,最高的F1分数,整体效果最佳。
    • 总结: 精确率和召回率平衡良好,适合对精度和召回率要求较高的任务。
  3. K 近邻分类 (KNN)

    • 准确率(Precision): 0.977778,相对最低,但是依然是很高的准确率。
    • Recall: 非常低(0.323529),FN较多(92个),说明漏检率非常高。
    • F1 Score: 0.486188,表现较差,尤其召回率影响较大。
    • 总结: 效果最差,不适合垃圾短信检测等需要较高召回率的任务。
  4. 支持向量机 (SVC)

    • 准确率(Precision): 0.990909,几乎完美。
    • Recall: 较高(0.801471),FN较少(27个),较好地识别了垃圾短信。
    • F1 Score: 0.886179,表现较为平衡,次于随机森林
    • 总结: 效果不错,仅次于随机森林,在较高精度和召回率的情况下表现良好。

2. 总体评价

  • 最佳模型: 随机森林 (Random Forest),兼具高精度和高召回率。
  • 次优模型: SVC,次于随机森林,表现均衡。
  • 表现较差: KNN,由于召回率低导致漏检率高,不适合该场景。

http://www.ppmy.cn/server/141404.html

相关文章

代码随想录算法训练营第二十二天| leetcode77. 组合、leetcode216.组合总和III、leetcode17.电话号码的字母组合

1 leetcode77. 组合 题目链接:77. 组合 - 力扣(LeetCode) 文章链接:代码随想录 视频链接:带你学透回溯算法-组合问题(对应力扣题目:77.组合)| 回溯法精讲!哔哩哔哩bil…

react->Antd->Table调整checkbox默认样式

checkbox默认不展示,hover此行时,出现checkbox,选中后不消失: hover前,设置透明边框; hover时,checkbox出现 选中后 代码块: .ant-checkbox {.ant-checkbox-inner {border: transparent;}}.ant…

Jenkins应用详解(Detailed Explanation of Jenkins Application)

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

【Linux进程篇3】说白了,Linux创建进程(fork父子进程)也就那样!!!

--------------------------------------------------------------------------------------------------------------------------------- 每日鸡汤:没人可以好运一生,只有努力才是一生的护身符,不放弃、不辜负。 -----------------------…

YOLO11改进 | 融合改进 | C3k2融合 Context Anchor Attention 【两个版本融合-独家创新】

秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 本文给大家带来的教程是将YOLO11的C3k2替换为融合结构来提取特征。文章在介绍主要的原理后,将手把手教学如何进行模块的…

2024最新版JavaScript逆向爬虫教程-------基础篇之Chrome开发者工具学习

目录 一、打开Chrome DevTools的三种方式二、Elements元素面板三、Console控制台面板四、Sources面板五、Network面板六、Application面板七、逆向调试技巧7.1 善用搜索7.2 查看请求调用堆栈7.3 XHR 请求断点7.4 Console 插桩7.5 堆内存函数调用7.6 复制Console面板输出 工欲善…

华为eNSP:RSTP

一、什么是RSTP? RSTP(Rapid Spanning Tree Protocol)是快速生成树协议的简称,是一种网络协议,用于在局域网中消除数据链路层物理环路,实现路径冗余,同时将环路网络修剪成无环路的树型网络结构…

扫雷游戏代码分享(c基础)

hi , I am 36. 代码来之不易👍👍👍 创建两个.c 一个.h 1:test.c #include"game.h"void game() {//创建数组char mine[ROWS][COLS] { 0 };char show[ROWS][COLS] { 0 };char temp[ROWS][COLS] { 0 };//初始化数…