垃圾邮件检测.第1部分

2021-11-10 17:27:47 浏览数 (1)


磐创AI分享

作者 | Md Sohel Mahmood 编译 | VK 来源 | Towards Data Science

垃圾邮件检测是机器学习算法在过滤垃圾邮件方面的一个重要应用。在自然语言处理领域,有几种算法可用于此类分类。通常垃圾邮件都有一些典型的词语。

在本文中,我们将使用nltk软件包详细介绍垃圾邮件和非垃圾邮件的文本处理。特别是我们将看到NLP的词干分析和词形还原过程。我们还将实现NB分类器以及SVC和随机森林分类器来检测垃圾邮件,并比较分类器的准确性。让我们开始吧。

根据nltk文档,“nltk是构建Python程序以处理人类语言数据的领先平台”。使用nltk处理和标记文本非常简单,例如词干分析和词形还原,我们将在后面看到。

首先,我们需要导入必要的包。

代码语言:javascript复制
import numpy as np 
import pandas as pd 
import nltk
from nltk.corpus import stopwords
import matplotlib.pyplot as plt

from wordcloud import WordCloud, ImageColorGenerator
from PIL import Image
import re
from nltk.tokenize import word_tokenize as wt
from nltk.stem.porter import PorterStemmer
from nltk.stem import WordNetLemmatizer

数据标签

在导入包含垃圾邮件和非垃圾邮件标签文本的csv文件后,我创建了两个数据帧:一个用于真实电子邮件,另一个用于垃圾邮件,我们将利用它们进行分析。

代码语言:javascript复制
spam_data = spam[spam['label'] == 'spam']
real_data = spam[spam['label'] == 'ham']

词干分析和词形还原

让我们先进行词干分析,然后把它们词形还原。根据斯坦福NLP小组的说法,“词干分析通常指的是一种粗糙的启发式过程,即切掉词尾,希望在大多数情况下都能正确实现这一目标,通常包括去除派生词缀。

词形还原通常是指通过使用词汇表和词形分析正确地处理事情,通常目的只是去除词形变化的词尾,并返回一个单词的基本形式或字典形式,称为词形。”在这里,词干分析分别应用于所有数据、垃圾邮件数据和真实数据。

代码语言:javascript复制
all_data_stem = []
spam_data_stem = []
real_data_stem = []
stemmer = PorterStemmer()
for i in range(spam.shape[0]):
    sms = spam.iloc[i, 1]
    sms = re.sub('[^A-Za-z]', ' ', sms)
    sms = sms.lower()
    tokenized_sms = wt(sms)
    sms_processed = []
    for word in tokenized_sms:
        if word not in set(stopwords.words('english')):
            sms_processed.append(stemmer.stem(word))
    sms_text = " ".join(sms_processed)
    all_data_stem.append(sms_text)

for i in range(spam_data.shape[0]):
    sms = spam_data.iloc[i, 1]
    sms = re.sub('[^A-Za-z]', ' ', sms)
    sms = sms.lower()
    tokenized_sms = wt(sms)
    sms_processed = []
    for word in tokenized_sms:
        if word not in set(stopwords.words('english')):
            sms_processed.append(stemmer.stem(word))
    sms_text = " ".join(sms_processed)
    spam_data_stem.append(sms_text)

for i in range(real_data.shape[0]):
    sms = real_data.iloc[i, 1]
    sms = re.sub('[^A-Za-z]', ' ', sms)
    sms = sms.lower()
    tokenized_sms = wt(sms)
    sms_processed = []
    for word in tokenized_sms:
        if word not in set(stopwords.words('english')):
            sms_processed.append(stemmer.stem(word))
    sms_text = " ".join(sms_processed)
    real_data_stem.append(sms_text)
all_data_stem[0]

然后将词形还原分别应用于所有数据、垃圾邮件数据和真实数据。

代码语言:javascript复制
all_data_lemma = []
spam_data_lemma = []
real_data_lemma = []
lemmatizer = WordNetLemmatizer()
for i in range(spam.shape[0]):
    sms = spam.iloc[i, 1]
    sms = re.sub('[^A-Za-z]', ' ', sms)
    sms = sms.lower()
    tokenized_sms = wt(sms)
    sms_processed = []
    for word in tokenized_sms:
        if word not in set(stopwords.words('english')):
            sms_processed.append(lemmatizer.lemmatize(word))
    sms_text = " ".join(sms_processed)
    all_data_lemma.append(sms_text)

for i in range(spam_data.shape[0]):
    sms = spam_data.iloc[i, 1]
    sms = re.sub('[^A-Za-z]', ' ', sms)
    sms = sms.lower()
    tokenized_sms = wt(sms)
    sms_processed = []
    for word in tokenized_sms:
        if word not in set(stopwords.words('english')):
            sms_processed.append(lemmatizer.lemmatize(word))
    sms_text = " ".join(sms_processed)
    spam_data_lemma.append(sms_text)

for i in range(real_data.shape[0]):
    sms = real_data.iloc[i, 1]
    sms = re.sub('[^A-Za-z]', ' ', sms)
    sms = sms.lower()
    tokenized_sms = wt(sms)
    sms_processed = []
    for word in tokenized_sms:
        if word not in set(stopwords.words('english')):
            sms_processed.append(lemmatizer.lemmatize(word))
    sms_text = " ".join(sms_processed)
    real_data_lemma.append(sms_text)
all_data_lemma[0]

如果我们查看第一个数据文本的词干分析,我们将得到:

'go jurong point crazi avail bugi n great world la e buffet cine got amor wat'.

词形还原提供:

'go jurong point crazy available bugis n great world la e buffet cine got amore wat'

从第一批数据可以明显看出,词干分析和词形还原以不同的方式工作。例如,“availability”一词的词干是“avail”,但词形是“available”。

Wordcloud

在用数值标记之后,让我们创建wordcloud来查看最常见的单词。

代码语言:javascript复制
spam['num_label'] = spam['label'].map({'ham': 0, 'spam': 1})
spam_words = ' '.join(list(spam[spam['num_label'] == 1]['text']))
spam_wc = WordCloud(width = 600,height = 512).generate(spam_words)
plt.figure(figsize = (12, 8), facecolor = 'k')
plt.imshow(spam_wc)
plt.axis('off')
plt.tight_layout(pad = 0)
plt.show()

垃圾邮件中有很多吸引人的词语,而真实电子邮件中的文本非常随机,如下所示。

频率分布

我们可能有兴趣看看垃圾邮件中最常用的单词。可通过如下频率分布获得

代码语言:javascript复制
from nltk import FreqDist
spam_token = nltk.tokenize.word_tokenize(spam_words)
spam_freq = FreqDist(spam_token)
spam_freq

FreqDist({'.': 1004, 'to': 608, '!': 542, ',': 371, 'a': 358, 'you': 189, 'call': 187, 'your': 187, 'or': 185, '&': 178, …})

在其他时候,我们可能会有兴趣关注垃圾邮件中重复的句子中最常见的部分。

代码语言:javascript复制
FreqDist(spam_data_lemma).most_common(5)

很明显,像“私人账户声明”或“秘密仰慕者”这样的句子部分是垃圾邮件中最吸引人的词组之一。

离散图

我们可以获得目标词的分散图来查看分布。它将根据单词总数提供特定单词出现的信息。我选择了“免费”、“私人”、“帐户”、“联系”等词作为演示词。

分类器

在这里,我从scikit-learn库创建了一个分类器。我们需要将文本转换为token计数矩阵,scikit learn的CountVectorizer非常方便。

我们将首先尝试NaiveBayes函数,该函数易于实现,并且训练时间更短。为了便于训练,我选择了80%的数据。

代码语言:javascript复制
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import train_test_split

matrix = CountVectorizer(max_features=1000)
X = matrix.fit_transform(all_data_lemma).toarray()
y = spam.iloc[:, 0]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

from sklearn.naive_bayes import GaussianNB

classifier = GaussianNB()
classifier.fit(X_train, y_train)

from sklearn.metrics import confusion_matrix, classification_report, accuracy_score

y_pred = classifier.predict(X_test)
cm = confusion_matrix(y_test, y_pred)
rep = classification_report(y_test, y_pred)

精确度是TP(真阳性)与TP和FP(假阳性)之和的比率。召回率是TP与TP和FN之和的比率(假阴性)。如果一封真正的垃圾邮件被错误地识别为真正的电子邮件,那就是误报。另一方面,如果一封真正的电子邮件被识别为垃圾邮件,那就是假阴性。

报告显示,该模型在检测真实邮件方面表现良好,但在检测垃圾邮件方面表现不佳。SPAM的精度为~0.38,表明从模型中获得了大量误报。

虽然模型的准确度为0.79,但可能存在误导,垃圾邮件的召回率较高,而准确度较低。这表明该模型偏向于垃圾邮件。它能够正确识别大多数垃圾邮件,但也错误地将一些正常邮件识别为垃圾邮件。

代码语言:javascript复制
array([[744, 224],
       [ 12, 135]], dtype=int64)

混淆矩阵也显示了类似的场景。对角线没有大的数字。这意味着对于朴素贝叶斯来说,性能还不够好。

让我们尝试一下支持向量分类和随机森林算法。

代码语言:javascript复制
from sklearn.svm import SVC

classifier = SVC()
classifier.fit(X_train, y_train)

y_pred = classifier.predict(X_test)
cm = confusion_matrix(y_test, y_pred)
rep = classification_report(y_test, y_pred)

模型性能良好。正常和垃圾邮件的准确率和召回率都很高。最后,让我们尝试使用随机林作为分类器。

代码语言:javascript复制
from sklearn.ensemble import RandomForestClassifier

classifier = RandomForestClassifier()
classifier.fit(X_train, y_train)

y_pred = classifier.predict(X_test)
cm = confusion_matrix(y_test, y_pred)
rep = classification_report(y_test, y_pred)

在这种垃圾邮件检测案例中,Random Forest也是一种很好的分类器,能够为真实和垃圾邮件提供高精度和召回率。

结论

本文使用nltk库演示了NLP的词干分析和词形还原过程,并比较了几种二进制分类器算法。

朴素贝叶斯的准确率较低,而SVC和随机森林提供了更高的准确率、召回率和准确率。

交叉验证技术可以用来评估这些分类器的技能。现在,有许多开源平台可以进行训练,也可以进行交叉验证,而无需任何代码,我将在另一篇文章中讨论。

Github提供了代码块:https://mdsohelmahmood.github.io/2021/06/23/Spam-email-classification-using-NB-SVC-Random-Forest.html

感谢阅读!

0 人点赞