『零基础+1』来做个垃圾短信分类吧!


0.引言

垃圾短信(邮件)检测是机器学习在现今互联网领域的主要应用之一。几乎所有大型电子邮箱服务提供商都内置了垃圾邮件检测系统,能够自动将此类邮件分类为“垃圾邮件”。

垃圾短信(邮件)作为自然语言处理的经典数据集,从经典的贝叶斯方法到SVM再到深度学习方法,许多的方法都在该数据集上进行了实现。

作为经典数据集的自然语言处理入门项目,本项目通过对垃圾短信数据集进行介绍和不同分类方法的实现,介绍自然语言处理领域的经典分类模型。

该项目灵感来自CACL高校AI算法联赛,仅用作学习交流


1.数据集介绍

本项目使用来自 UCI 机器学习资源库中的垃圾短信数据集,包括ham:非垃圾短信 spam:垃圾短信,文件以txt格式给出。已经分割好训练和测试集。作为入门学习数据集使用。

第一列是标签,第二列是被分类的信息的文本内容,中间通过制表符分隔。

数据样例:

# 解压数据集
!unzip  -o work/dataSet.zip -d /home/aistudio/work/

2. TF-IDF向量化 + Logistics回归 + 十折验证

本节介绍的第一种实现100%准确率的方法包括三个步骤:

  • 文本使用 TF-IDF 进行文本特征提取,将单词转为向量

  • 使用Logistics回归创建分类器,使用生成的向量进行训练

  • 对模型进行十折交叉验证


2.1 TF-IDF向量化

对于一个文本来说,计算机是无法直接理解单词或者汉字,第一步需要考虑的是将其转化为计算机能读懂方式,即生成一串编码来代替一个单词或汉字。

这部分最经典的方法是One-Hot编码,即用0,1对语料进行编码。比如“我爱飞桨”,可以编码为一个 4 * 4的矩阵。四个字分别编码为 [1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]。

这种方法需要存储一个稀疏矩阵,并且不能表示语料之间的关系和重要程度,因此后续出现许多新的方法代替了它。

TF-IDF(Term Frequency-Inverse Document Frequency, 词频-逆文件频率)。TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。

字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。

上述引用总结就是, 一个词语在一篇文章中出现次数越多, 同时在所有文档中出现次数越少, 越能够代表该文章.

词频 (term frequency, TF) 指的是某一个给定的词语在该文件中出现的次数。这个数字通常会被归一化(一般是词频除以文章总词数), 以防止它偏向长的文件。(同一个词语在长文件里可能会比短文
件有更高的词频,而不管该词语重要与否。)

逆向文件频率 (inverse document frequency, IDF) IDF的主要思想是:如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。

某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。


2.2 Logistics回归

logistic回归又称logistic回归分析,是一种广义的线性回归分析模型,常用于数据挖掘,疾病自动诊断,经济预测等领域。

Logistic回归是针对因变量为分类变量的回归分析方法,属于概率型非线性回归。非线性问题要通过对数变换转换为线性问题。分类变量有二分类(有两个分类状态)和多分类(有多个类别的状态)。二分类Logistic回归的因变量只有两个分类值:0和1。

关于Logistics回归的更多内容可以查看参考资料[4]


2.3 十折交叉验证

在机器学习里,通常来说我们不能将全部用于数据训练模型,否则我们将没有数据集对该模型进行验证,从而评估我们的模型的预测效果。为了解决这一问题,我们可以采用交叉验证的方法。

可以把有标签训练集分成两部分,一部分用于训练,一部分用于验证,这也就是我们经常提到的训练集(train set)和验证集(eval set)。

至于K折交叉验证(K-fold Cross Validation)具体是通过如下方法实现:

  1. 将所有数据集分成K份

  2. 不重复地每次取其中一份做测试集,用其他K-1份做训练集训练模型

对所有的测试集结果求平均作为最终的模型表现,这样能有效避免因为数据划分出现的偏差。

import re
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer as TF
from sklearn.linear_model import LogisticRegression as LR
from sklearn.model_selection import cross_val_score
import numpy as np
print("处理训练数据:...\n")
train_txt = pd.read_table('work/dataSet/train.txt',sep='\t',header=None)  
train_txt.columns = ['label', 'text']
label_map = {'ham': 0, 'spam': 1 }#1为垃圾短信
train_txt['label'] = train_txt['label'].map(label_map)

#train_txt = pd.get_dummies(train_txt, columns=['label'])# 将标签onehot编码

def pre_clean_text(origin_text):
    # 去掉标点符号和非法字符
    text = re.sub("[^a-zA-Z]", " ", origin_text)
    # 将字符全部转化为小写,并通过空格符进行分词处理
    words = text.lower().split()

    # 将剩下的词还原成str类型
    cleaned_text = " ".join(words)
    
    return cleaned_text

#清理数据
train_txt['text'] = train_txt['text'].apply(lambda x: pre_clean_text(x))

#删去空值.测试时若无效词删去后为空则直接为垃圾信息(实际测试中没有)
#print(train_txt.shape)
train_txt = train_txt.loc[train_txt['text'] != '',:]
# 查看数据
print(train_txt.head())
#print(train_txt.shape)

处理训练数据:...

   label                                               text
0      0  go until jurong point crazy available only in ...
1      0                            ok lar joking wif u oni
2      1  free entry in a wkly comp to win fa cup final ...
3      0        u dun say so early hor u c already then say
4      0  nah i don t think he goes to usf he lives arou...
#描述样本数量:后面考虑样本加权
result = pd.DataFrame(train_txt.iloc[:,0]).apply(pd.value_counts)
# print(result)
#文本特征提取
train_txt = train_txt.sample(frac=1).reset_index(drop=True)
tfidf = TF(
    analyzer="word",
    tokenizer=None,
    preprocessor=None,
    stop_words=None,
    max_features=200)

# 数据向量化
print("创造训练 tfidf 向量...\n")
tfidf.fit(train_txt['text'])
x_train = tfidf.transform(train_txt['text'])
x_train = x_train.toarray()

#print(x_train.shape)

#使用logistics回归
y_train = train_txt['label']
model = LR(solver='liblinear')
model.fit(x_train, y_train)
print("logistics回归&&10折交叉验证:...")
print("训练accuracy为",np.mean(cross_val_score(model, x_train, y_train, cv=10, scoring="accuracy")),"\n")

print('处理测试数据:...\n')
test_txt = pd.read_table('work/dataSet/test.txt',sep='\t',header=None)
test_txt.columns = ['text']

test_txt['text'] = test_txt['text'].apply(lambda x: pre_clean_text(x))
# 数据向量化
print("创造测试 tfidf 向量...\n")
test_txt = tfidf.transform(test_txt['text'])
test_txt = test_txt.toarray()

preds = model.predict(test_txt)
preds = pd.DataFrame(preds)

print("生成提交...\n")
submit=preds.replace(0, 'ham')
submit=submit.replace(1, 'spam')
submission = pd.DataFrame(submit)
submission.to_csv("work/dataSet/submission.csv", index=False, header=False)

print("读取真实样本并评测...\n")
true_label = pd.read_table('work/dataSet/test_label.csv',header=None)  
true_label[0] = true_label[0].map(label_map)

assess =np.sum((preds - true_label)==0)/len(true_label)

print('提交的准确率为 %5.2f' % assess)
处理训练数据:...

创造训练 tfidf 向量...

logistics回归&&10折交叉验证:...
训练accuracy为 0.9726789168278529 

处理测试数据:...

创造测试 tfidf 向量...

生成提交...

读取真实样本并评测...

提交的准确率为  1.00

3.支持向量机算法+KNN算法+朴素贝叶斯分类

sklearn是scikit-learn的简称,是一个基于Python的第三方模块。sklearn库集成了一些常用的机器学习方法,在进行机器学习任务时,并不需要实现算法,只需要简单的调用sklearn库中提供的模块就能完成大多数的机器学习任务。强烈建议阅读官方文档进行学习:https://scikit-learn.org/stable/

本项目在演示了直接调用的三种算法性能:支持向量机算法,KNN算法和朴素贝叶斯分类


3.1 支持向量机算法

支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机;SVM还包括核技巧,这使它成为实质上的非线性分类器。SVM的的学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失函数的最小化问题。SVM的的学习算法就是求解凸二次规划的最优化算法。

关于SVM算法,强烈建议大家学习张皓老师写的《从零推导支持向量机》


3.2 KNN算法

邻近算法,或者说K最邻近(KNN,K-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一。所谓K最近邻,就是K个最近的邻居的意思,说的是每个样本都可以用它最接近的K个邻近值来代表。近邻算法就是将数据集合中每一个记录进行分类的方法。

KNN的计算可以分为以下四步

  • 准备数据,对数据进行预处理

  • 计算测试样本点(也就是待分类点)到其他每个样本点的距离

  • 对每个距离进行排序,然后选择出距离最小的K个点

  • 对K个点所属的类别进行比较,根据少数服从多数的原则,将测试样本点归入在K个点中占比最高的那一类

更多KNN从0的实现可以查看参考资料


3.3 朴素贝叶斯分类

朴素贝叶斯分类是一种十分简单的分类算法,称为朴素贝叶斯分类是因为其朴素的思想基础:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。这就好比讲英语的金发白人我们会天然认为是外国人。

但朴素贝叶斯假设前提有两个第一个为:各特征彼此独立;第二个为对被解释变量的影响一致,不能进行变量筛选。但是很多时候独立性假设是无法得到的。这也是贝叶斯假设出现的原因。

朴素贝叶斯分类的表示为如下内容。

  • 1.设x={a1,a2…am}是一个待分类项,而每个a为x的一个属性特征。

  • 2.有类别集合C={y1,y2…yn}.

  • 3.计算p(y1|x),p(y2|x)…p(yn|x).

  • 4.如果p(yk|x)=max{p(y1|x),p(y2|x)…p(yn|x)}.,则x属于yk.

具体的推导讲解可见朴素贝叶斯的讲解

#使用svm回归
from sklearn.svm import SVC
from sklearn.naive_bayes import MultinomialNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import Ridge
y_train = train_txt['label']

models = [('KNN临近算法', KNeighborsClassifier(n_neighbors=2)),
          ('KNN带权重', KNeighborsClassifier(n_neighbors=4, weights='distance')),
          ('朴素贝叶斯分类', MultinomialNB()),
          ('支持向量机分类',SVC(kernel='rbf', C=6.0))
          ]
for name, model in models:

    model.fit(x_train, y_train)
    print(name,"训练accuracy为",np.mean(cross_val_score(model, x_train, y_train, cv=5, scoring="accuracy")))
    preds = model.predict(test_txt)
    preds = pd.DataFrame(preds)
    
    true_label = pd.read_table('work/dataSet/test_label.csv',header=None)  
    true_label[0] = true_label[0].map(label_map)
    assess =np.sum((preds - true_label)==0)/len(true_label)
    print(name,'提交的准确率为 %5.2f' % assess)


map)
    assess =np.sum((preds - true_label)==0)/len(true_label)
    print(name,'提交的准确率为 %5.2f' % assess)



KNN临近算法 训练accuracy为 0.9585342608643449
KNN临近算法 提交的准确率为  0.99
KNN带权重 训练accuracy为 0.9676409119220754
KNN带权重 提交的准确率为  1.00
朴素贝叶斯分类 训练accuracy为 0.9662850732044095
朴素贝叶斯分类 提交的准确率为  0.99
支持向量机分类 训练accuracy为 0.9808167675994508
支持向量机分类 提交的准确率为  0.99

4.总结

  • 主要对垃圾分类数据集进行分类,详细注释了文本分类任务代码。

  • 介绍了几个经典的分类算法,演示了其性能,并提供了详细的学习资料链接。

个人觉得,深入学习这些经典机器学习算法的原理,能培养良好的数学素养。


最后希望与大家多多交流,欢迎点赞,FORK!

5.参考资料

[1] https://segmentfault.com/a/1190000018717481?utm_source=tag-newest

[2] https://blog.csdn.net/zrc199021/article/details/53728499

[3] https://baike.baidu.com/item/logistic回归/2981575?fr=aladdin

[4] https://www.jiqizhixin.com/articles/2018-05-13-3

[5] https://zhuanlan.zhihu.com/p/24825503?refer=rdatamining

[6] https://zhuanlan.zhihu.com/p/31652569

[7] https://zhuanlan.zhihu.com/p/110066200

[8] https://zhuanlan.zhihu.com/p/54287889

[9] https://blog.csdn.net/weixin_37567451/article/details/81063217

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐