使用 Python 和 Keras 进行垃圾邮件分类






4.36/5 (5投票s)
如何准备训练和测试数据,定义一个简单的神经网络模型,并进行训练和测试
引言
机器学习使我们能够利用基于数据的数学和统计概率来确定代码的结果。这使得我们能够创建能够随时间“进化”的代码,因为它基于数据的变化,而不是具有特定的硬编码值或存储在某处的特定值。
例如,客户的信用卡使用情况会随着时间的推移而变化和演变,这取决于他们的购买习惯以及银行卡公司继续识别欺诈交易的需求。如果代码或数据库中设置了“阈值”,那么该值将需要定期更新,而对于大量客户而言,确定该值是多少将是极其昂贵/困难的。定期训练机器学习模型来识别基于实际数据的欺诈活动要更容易维护。
在本文中,我们将使用“监督学习”来确定一条消息是“垃圾邮件
”还是“正常邮件
”(垃圾邮件或非垃圾邮件)。监督学习意味着我们有一组数据,其中包含已识别为“垃圾邮件
”或“正常邮件
”的消息,我们将使用这些数据来训练机器学习模型,使其能够识别新消息是垃圾邮件
还是正常邮件
。这种判断基于新消息与我们用于训练模型的那些消息的统计相似性。
背景
如果您对编程有相当的熟悉度,并且对机器学习感兴趣,您应该能够跟上本教程。CodeProject 提供的数据如下所示
# Spam training data
Spam,<p>But could then once pomp to nor that glee glorious of deigned. The vexed times
childe none native. To he vast now in to sore nor flow and most fabled.
The few tis to loved vexed and all yet yea childe. Fulness consecrate of it before his
a a a that.</p><p>Mirthful and and pangs wrong. Objects isle with partings ancient
made was are. Childe and gild of all had to and ofttimes made soon from to long youth
way condole sore.</p>
Spam,<p>His honeyed and land vile are so and native from ah to ah it like flash in not.
That gild by in basked they lemans passed way who talethis forgot deigned nor friends
his before strange. Found long little the. Talethis have soon of hellas had
一个指示“垃圾邮件
”或“正常邮件
”的初始值,后面跟着一个<p>
标签,然后是消息的内容。此外,文件被分成训练和测试部分(稍后会详细介绍)。
导入库
在这里,与许多语言一样,我们导入代码所需的各种库。稍后我们将详细介绍我们正在使用的内容
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.pipeline import FeatureUnion
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import LinearSVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.utils import shuffle
from sklearn.metrics import precision_score, classification_report, accuracy_score
import time
加载和解析数据
是的,虽然成为一名21世纪最性感的数据科学家可能很诱人,但这需要大量时间来执行那些不那么性感的数据解析/清理/理解过程。对于这个项目,我大概有 85% 的时间都花在了这个上面。
def get_data():
file_name = './SpamDetectionData.txt'
rawdata = open(file_name, 'r')
lines = rawdata.readlines()
lines = lines[1:] #get rid of "header"
spam_train = lines[0:1000]
ham_train = lines[1002:2002]
test_mix = lines[2004:]
return (spam_train, ham_train, test_mix)
在get_data()
函数中,我们从CodeProject提供的文件中获取训练和测试数据。我们从文件中读取原始数据并将其存储在一个数组中。在类似以下的行中
spam_train = lines[0:1000]
ham_train = lines[1002:2002]
test_mix = lines[2004:]
我们将数组的部分内容简单地分割到具有有意义名称的单独数组中。有关我们如何以及为何使用测试和训练数据的更多详细信息将在下面介绍。
创建 Pandas DataFrame
对于您在机器学习中将要进行的大多数数据/特征工程,您将使用Pandas,因为它提供了一套非常强大(但有时令人困惑)的工具来处理数据。在这里,我们正在创建一个DataFrame,您可以将其轻松地理解为内存中的“表
”,其中包含行和列,其中一列保存垃圾邮件
/正常邮件
消息的内容,另一列保存一个二进制标志(或数据科学术语中的类别),指示该消息是“垃圾邮件
”(1)还是“正常邮件
”(0)。
def create_dataframe(input_array):
spam_indcator = 'Spam,<p>'
message_class = np.array([1 if spam_indcator in item else 0 for item in input_array])
data = pd.DataFrame()
data['class'] = message_class
data['message'] = input_array
return data
这是我们在下一步清理数据之前的 DataFrame 的样子 - 包括我们刚刚添加的“class
”列。
数据清理前的 DataFrame
删除单词和打乱数据
在这里,根据我在数据中看到的情况,我们想删除任何没有意义的无关文本,例如<p>,
,或者,在本例中,CodeProject 提供的数据会提供明确的指示消息类型的明显数据,例如“ Ham,<p>
”,这些在我们将要分类的实际消息中不会出现。当我们找到这些时,我们将简单地用空字符串替换它们。
words_to_remove = ['Ham,<p>', 'Spam,<p>', '<p>', '</p>', '\n']
def remove_words(input_line, key_words=words_to_remove):
temp = input_line
for word in key_words:
temp = temp.replace(word, '')
return temp
在这里,我们将上述过滤应用于我们的 DataFrame,然后打乱数据。在打乱数据时,CodeProject 提供的这些数据不是必需的,因为它们已经被分成了训练数据和测试数据。如果您要对另一个数据集执行此过程,您应该始终在将数据分成训练集和测试集之前打乱数据,以确保每个集合中都有大致相等数量的样本(在此情况下,为垃圾邮件和正常邮件)。如果这些集合不平衡,它们很容易导致训练/测试过程中的偏差。
def remove_words_and_shuffle(input_dataframe, input_random_state=7):
input_dataframe['message'] = input_dataframe['message'].apply(remove_words)
messages, classes = shuffle(input_dataframe['message'], input_dataframe['class'],
random_state=input_random_state)
df_return = pd.DataFrame()
df_return['class'] = classes
df_return['message'] = messages
return df_return
这是我们清理后的 DataFrame
训练和测试我们的模型
这就是一切的意义所在——使用训练数据来训练我们的机器学习模型,然后使用测试数据来确定模型的准确性以及它的表现如何。
def test_models(X_train_input_raw, y_train_input, X_test_input_raw, y_test_input, models_dict):
return_trained_models = {}
return_vectorizer = FeatureUnion([('tfidf_vect', TfidfVectorizer())])
X_train = return_vectorizer.fit_transform(X_train_input_raw)
X_test = return_vectorizer.transform(X_test_input_raw)
for key in models_dict:
model_name = key
model = models_dict[key]
t1 = time.time()
model.fit(X_train, y_train_input)
t2 = time.time()
predicted_y = model.predict(X_test)
t3 = time.time()
output_accuracy(y_test_input, predicted_y, model_name, t2 - t1, t3 - t2)
return_trained_models[model_name] = model
return (return_trained_models, return_vectorizer)
这段代码涉及很多内容,所以我们将逐行进行。首先,让我们看看参数
X_train_input_data
- 这些是我们将用于训练模型的“原始”垃圾邮件
/正常邮件
消息y_train_input
- 这是X_train_input_data
参数的 0 或 1,表示正常邮件
或垃圾邮件
X_test_input_raw
- 我们将用于测试训练模型准确性的“原始”垃圾邮件
/正常邮件
消息y_test_input
- 这是X_test_input_raw
参数的 0 或 1,表示正常邮件
或垃圾邮件
return_trained_models = {}
是一个字典,将保存我们训练过的模型,以便以后使用
return_vectorizer = FeatureUnion([('tfidf_vect', TfidfVectorizer())])
设置了一个TfidfVectorizer,用于应用于传入的消息。本质上,我们正在将一串单词(消息)转换为一个向量(数组),其中包含这些单词的出现次数。
此外,TF-IDF(文本频率-逆文档频率)会对一个术语在源文档中出现的频率进行加权。
tf - idf 值随着一个词在文档中出现次数的增加而增加,并通过该词在语料库中的频率进行抵消,这有助于调整某些词总体上出现频率较高的事实。如今,tf-idf 是最流行的术语加权方案之一;在数字图书馆领域的基于文本的推荐系统中,有 83% 使用 tf - idf。(来源)
这意味着那些总体上出现频率较低,但在特定类型文档中出现频率较高的词将具有更高的权重。例如,“free”(免费)、“viagra”(伟哥)等词,它们在消息中(所有垃圾邮件
和正常邮件
消息的总和)总体出现频率不高,但在垃圾邮件消息中出现频率很高,因此这些词将被赋予更高的权重,以表明该文档是垃圾邮件
。
可以设置和调整大量参数来提高模型的准确性 - 您可以在此处找到这些参数的详细信息。
接下来,既然我们已经创建了向量化器,我们将用训练消息“训练”它,并使用它将我们的测试消息集转换为向量
X_train = return_vectorizer.fit_transform(X_train_input_raw)
X_test = return_vectorizer.transform(X_test_input_raw)
最后一步是循环遍历传入的模型字典,训练每个模型,使用模型预测测试数据,并输出每个模型的准确性。
输出训练模型的结果
当我们训练模型时,我们想看到模型的名称、训练模型所需的时间以及模型的准确性。此函数有助于做到这一点
def output_accuracy(actual_y, predicted_y, model_name, train_time, predict_time):
print('Model Name: ' + model_name)
print('Train time: ', round(train_time, 2))
print('Predict time: ', round(predict_time, 2))
print('Model Accuracy: {:.4f}'.format(accuracy_score(actual_y, predicted_y)))
print('Model Precision: {:.4f}'.format(precision_score(actual_y, predicted_y)))
print('')
print(classification_report(actual_y, predicted_y, digits=4))
print("=========================================================================")
创建要测试的模型字典
在这里,我们创建了我们要训练和测试准确性的模型字典。您可以在此处添加更多模型进行测试,删除性能较差的模型,或更改模型的参数以确定哪个模型最适合您的需求。
def create_models():
models = {}
models['LinearSVC'] = LinearSVC()
models['LogisticRegression'] = LogisticRegression()
models['RandomForestClassifier'] = RandomForestClassifier()
models['DecisionTreeClassifier'] = DecisionTreeClassifier()
return models
整合
在这里,我们将所有步骤汇总在一起
- 获取数据并创建数据框。
- 清理并打乱数据。
- 将数据分为训练集和测试集的输入 (X) 和输出 (y)。
- 创建模型。
- 将模型和数据传递给
test_models()
函数以查看它们的性能。
spam_train, ham_train, test_mix = get_data()
words_to_remove = ['Ham,<p>', 'Spam,<p>', '<p>', '</p>', '\n']
df_train_cleaned = remove_words_and_shuffle(df_train)
df_test_cleaned = remove_words_and_shuffle(df_test)
X_train_raw = df_train_cleaned['message']
y_train = df_train_cleaned['class']
X_test_raw = df_test_cleaned['message']
y_test = df_test_cleaned['class']
X_test_raw = df_test_cleaned['message']
y_test = df_test_cleaned['class']
models = create_models()
trained_models, fitted_vectorizer =
test_models(X_train_raw, y_train, X_test_raw, y_test, models)
输出
当我们运行它时,这里是输出
Model Name: LinearSVC
Train time: 0.01
Predict time: 0.0
Model Accuracy: 1.0000
Model Precision: 1.0000
precision recall f1-score support
0 1.0000 1.0000 1.0000 57
1 1.0000 1.0000 1.0000 43
avg / total 1.0000 1.0000 1.0000 100
======================================================
Model Name: LogisticRegression
Train time: 0.01
Predict time: 0.0
Model Accuracy: 0.4300
Model Precision: 0.4300
precision recall f1-score support
0 0.0000 0.0000 0.0000 57
1 0.4300 1.0000 0.6014 43
avg / total 0.1849 0.4300 0.2586 100
======================================================
Model Name: DecisionTreeClassifier
Train time: 0.02
Predict time: 0.0
Model Accuracy: 0.9800
Model Precision: 0.9556
precision recall f1-score support
0 1.0000 0.9649 0.9821 57
1 0.9556 1.0000 0.9773 43
avg / total 0.9809 0.9800 0.9800 100
======================================================
Model Name: RandomForestClassifier
Train time: 0.02
Predict time: 0.0
Model Accuracy: 0.9800
Model Precision: 0.9556
precision recall f1-score support
0 1.0000 0.9649 0.9821 57
1 0.9556 1.0000 0.9773 43
avg / total 0.9809 0.9800 0.9800 100
======================================================
我们可以看到训练模型所需的时间、预测测试数据所需的时间,以及每个模型的准确性、精确率和召回率。其中一些术语需要进一步解释
- 准确性 - 正确预测的观测值与总观测值的比率(对于我们来说,百分之多少的
垃圾邮件
/正常邮件
消息被正确检测到) - 精确率 - 正确预测为正的观测值与所有预测为正的观测值的比率(在我们标识为
垃圾邮件
的消息中,有多少被正确识别为垃圾邮件
) - 召回率 - 正确预测为正的观测值与实际类别的所有观测值的比率(在所有实际是
垃圾邮件
的消息中,我们正确识别了多少) - F1 分数 - 精确率和召回率的加权平均值
是的,这些是需要花些精力去理解的棘手概念,解释起来甚至更棘手。这些解释是从这里借用的:http://blog.exsilio.com/,并结合了我将其与我们项目相关联的解释。请参考该页面,因为它提供了对这些主题更深入的讨论。
尝试使用您的消息进行模型测试
最后,让我们尝试用您自己的消息来测试,看看它们是否被正确地识别为垃圾邮件
或正常邮件
。
#from the sample ham and spam
ham = 'door beguiling cushions did. Evermore from raven from is beak shall name'
spam = 'The vexed times childe none native'
test_messages = [spam, ham]
transformed_test_messages = fitted_vectorizer.transform(test_messages)
trained_models['DecisionTreeClassifier'].predict(transformed_test_messages)
输出是:
array([1, 0])
正确地识别了垃圾邮件
和正常邮件
。
结论
机器学习、深度学习和人工智能是未来,我们作为软件工程师需要理解并拥抱这些技术提供的力量,因为我们可以利用它们来更有效地解决我们工作中的公司和客户提出的并需要我们帮助解决的问题。
我有一个博客,专门帮助软件工程师理解和发展他们在机器学习、深度学习和人工智能领域的技能。如果您觉得从本文中学到了东西,欢迎访问我的博客 CognitiveCoder.com。
感谢您一直阅读到最后。
历史
- 2018 年 3 月 3 日 - 首次发布
- 2018 年 3 月 3 日 - 修复了损坏的图片链接