在当今数字化时代,几乎每个人都在使用手机,并且每天都会收到大量的短信和电子邮件。然而,这些信息中很多都是垃圾信息,只有少数是需要的正常信息。本文将介绍如何使用长短期记忆网络(LSTM)来构建一个短信垃圾信息检测模型,帮助区分垃圾短信和正常短信。
使用的是公开的短信垃圾信息检测数据集,该数据集包含了短信文本及其对应的标签(垃圾信息或正常信息)。
首先,需要导入所有必要的库来进行数据预处理。
import pandas as pd
import numpy as np
import re
import collections
import contractions
import seaborn as sns
import matplotlib.pyplot as plt
plt.style.use('dark_background')
import nltk
from nltk.stem import WordNetLemmatizer
from nltk.corpus import stopwords
import warnings
warnings.simplefilter(action='ignore', category=Warning)
import keras
from keras.layers import Dense, Embedding, LSTM, Dropout
from keras.models import Sequential
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
import pickle
使用pandas库读取数据集,并查看数据集的前几行以及数据集的形状。
df = pd.read_csv("spam.csv", encoding='latin-1')
df.head()
df.shape # 输出 - (5572, 8674)
数据集中包含了一些对无用的列,因此需要删除这些列,并为了方便起见,重命名列名。
df.drop(["Unnamed: 2", "Unnamed: 3", "Unnamed: 4"], axis=1, inplace=True)
df.columns = ["SpamHam","Tweet"]
接下来,使用seaborn库绘制垃圾短信和正常短信的数量对比图。
sns.countplot(data["SpamHam"])
从图中可以看出,正常短信的数量多于垃圾短信。在进行数据预处理之前,先绘制数据集中不同单词的数量分布图。为此,创建了一个名为word_count_plot
的函数。
def word_count_plot(data):
# 寻找单词及其计数
word_counter = collections.Counter([word for sentence in data for word in sentence.split()])
most_count = word_counter.most_common(30) # 30个最常见单词
# 排序数据框
most_count = pd.DataFrame(most_count, columns=["Word", "Count"]).sort_values(by="Count")
most_count.plot.barh(x = "Word", y = "Count", color="green", figsize=(10, 15))
通过这个函数,可以清楚地看到数据集中的大部分单词都是停用词。因此,需要对数据集进行一些预处理技术。
lem = WordNetLemmatizer()
def preprocessing(data):
sms = contractions.fix(data) # 将缩写词转换为原始形式(例如:"I'm" 转换为 "I am")
sms = sms.lower() # 小写化短信
sms = re.sub(r'https?://S+|www.S+', "", sms).strip() # 移除URL
sms = re.sub("[^a-z ]", "", sms) # 移除符号和数字
sms = sms.split() # 分割
# 词形还原和停用词移除
sms = [lem.lemmatize(word) for word in sms if not word in set(stopwords.words("english"))]
sms = " ".join(sms)
return sms
对数据集应用预处理函数,然后再次绘制单词数量分布图,以查看除了停用词之外的最常见单词。
X = data["v2"].apply(preprocessing)
word_count_plot(X)
现在可以看到除了停用词之外的最常见单词。接下来,继续进行预处理。由于输出值(垃圾信息或正常信息)是分类值,需要将它们转换为数值形式。使用LabelEncoder进行编码。
from sklearn.preprocessing import LabelEncoder
lb_enc = LabelEncoder()
y = lb_enc.fit_transform(data["SpamHam"])
将输出特征转换为数值形式后,接下来,需要将输入特征也转换为数值形式,使用keras的Tokenizer,然后进行填充。
tokenizer = Tokenizer() # 初始化tokenizer
tokenizer.fit_on_texts(X)# 适应短信数据
text_to_sequence = tokenizer.texts_to_sequences(X) # 创建数值序列
可以查看一些文本及其对应的数值序列。
for i in range(5):
print("Text : ",X[i] )
print("Numerical Sequence : ", text_to_sequence[i])
还可以通过tokenizer.index_word找到对应单词的索引号。
tokenizer.index_word # 这将输出一个索引和单词的字典
这个字典包含了7774个单词,意味着数据集中有7774个唯一的单词。
如所见,在text_to_sequence中,所有的序列长度都不同,这不利于模型训练。因此,需要使所有句子长度相等。为此,用“0”填充序列。
max_length_sequence = max([len(i) for i in text_to_sequence])
padded_sms_sequence = pad_sequences(text_to_sequence, maxlen=max_length_sequence,
padding = "pre")
已经准备好了适合输入模型的输入数据。现在,来创建LSTM模型进行训练。
TOT_SIZE = len(tokenizer.word_index)+1
def create_model():
lstm_model = Sequential()
lstm_model.add(Embedding(TOT_SIZE, 32, input_length=max_length_sequence))
lstm_model.add(LSTM(100))
lstm_model.add(Dropout(0.4))
lstm_model.add(Dense(20, activation="relu"))
lstm_model.add(Dropout(0.3))
lstm_model.add(Dense(1, activation = "sigmoid"))
return lstm_model
lstm_model = create_model()
lstm_model.compile(loss = "binary_crossentropy", optimizer = "adam", metrics = ["accuracy"])
lstm_model.summary()
lstm_model.fit(padded_sms_sequence, y, epochs = 5, validation_split=0.2, batch_size=16)
pickle.dump(tokenizer, open("sms_spam_tokenizer.pkl", "wb"))
pickle.dump(lstm_model, open("lstm_model.pkl", "wb"))