本次实验中我使用到的是I. Androutsopoulos, J. Koutsias, K.V. Chandrinos, George Paliouras和 C.D. Spyropoulos的 "An Evaluation of Naive Bayesian Anti-Spam Filtering"中使用到的垃圾邮件语料库:lingspam_public。
其中包含有四个子目录bare、lemm、lemm_stop和stop。这4个目录中的每一个都包含10个子目录(第1部分,…第10部分),其中9部分用作训练,1部分用作测试。
下面开始实验:
读取文件
我们的实验数据集共11572封邮件,包括9648封非垃圾邮件,1924封垃圾邮件,其中每封垃圾邮件文件名含有"spmsg"。由于邮件的文本中从第三行开始为正文,并且我们只在正文内容上进行文本分析,因此,读取邮件从第三行开始,并对每封邮件进行文本预处理。
def read_file(position):
print(position)
rea=""
with open(position, "r",encoding='utf-8') as f:
next(f)
next(f)
rea = f.read()
f.close()
return rea
邮件文本预处理
- 去除非字母符号。
使用正则表达式将非文字类的符号,如标点符号、数字或特殊字符等去除。
data=re.sub(r'[^A-Za-z]', ' ', data)
- 将字母转换为小写
将字母转换为小写,同时由于英文单词之间以空格作为自然分界符,因此以空格为间隔直接进行分词。
str_list = data.lower().split()
- 去除停用词
将介词等对垃圾邮件判断无关的词移除,并在停词表中增加人名等使词典改进。
stop_words = set(stopwords.words('english'))
filtered_sentence = [w for w in str_list if not w in stop_words]
- 词形还原
词形还原,使得单词含义被保留。英语分词后,根据其词性将单词还原为字典中原型词汇。先使用nltk.pos_tag()获取单词在句子中的词性,再使用wnl.lemmatize()函数可以进行词形还原。
# 获取单词词性
def get_wordnet_pos(tag):
if tag.startswith('J'):
return wordnet.ADJ
elif tag.startswith('V'):
return wordnet.VERB
elif tag.startswith('N'):
return wordnet.NOUN
elif tag.startswith('R'):
return wordnet.ADV
else:
return None
tagged_sent = pos_tag(filtered_sentence)
wnl = WordNetLemmatizer()
lemmas_sent = []
for tag in tagged_sent:
wordnet_pos = get_wordnet_pos(tag[1]) or wordnet.NOUN
lemmas_sent.append(wnl.lemmatize(tag[0], pos=wordnet_pos)) # 词形还原
创建词典
对清理后的邮件文本单词列表进行词频统计,取出出现频率前3000个单词作为词典保存。(当然也可以分别取垃圾邮件和正常邮件中词频前2000单词,合并组成一个词典)
特征提取
词典完成后,对数据集中每一封邮件提取维度是3001的词数向量,其中第一列存放邮件类别,0表示此邮件为垃圾邮件,1表示此邮件为正常邮件,之后的3000列存放词典对应的词在此邮件中的出现次数。
最后生成一个11572*3001的特征向量矩阵。
模型训练预测
使用scikit-learn机器学习库训练分类器,输入样本数据和结构化的输出结果,运行k-近邻算法(KNN)判定输入数据属于哪一个分类。由于3000个单词属性的数值对于计算结果的影响是相等的,数据已经在同一尺度,因此可以不用进行数值归一化,而是直接将向量放入KNN分类器中,在试验中准确率无变化也说明其对结果无明显影响。
首先,提供数据集的90%作为训练样本来训练分类器,而使用其余的10%数据去测试分类器,检测分类器的正确率。
之后,在运行算法前,调整并指定超参数:使用交叉验证法,得到最优K值,此时K=4,即计算距离后,选取距离最小的前4个点,选择这4个最相似数据中出现次数最多的分类,作为新数据的分类。在多次试验后,选取weights = ‘uniform’,即不考虑距离权重这个超参数,所有的邻近点的权重都是相等的;选取p=1,使用曼哈顿距离计算两点距离,而非欧式距离。
最后算法结论得出,错误率在可接受范围内,则可以运行k-近邻算法进行分类。
最后结果
需要注意的地方
将词干提取改为词形还原后,准确率从98.88%提升到99.05%。说明在该实验中更适合使用词形还原。
说明k值过小时,例如取1时,是选取最近的一个点,这使得模型出现过拟合现象。
因此之后可以选用交叉验证法选择合适的k值,最后实验准确率提高到99.827%。