最近在学习自然语言处理相关的知识,这里记录一下使用scikit-learn库中的SVM(支持向量机)分类算法实现中文邮件的分类。
1.文本分类简介
文本分类是指在一定的规则下,根据内容自动确定文本类别这一过程。文本分类在实际场景中有诸多方面的应用,比如常见的有垃圾邮件分类,情感分析,新闻分类等。
文本分类主要可以分为二分类,多分类,多标签分类三大类。
1.二分类:顾名思义是将文本归为两种类别,比如将邮件划分问题,垃圾邮件或者正常邮件。一段影评,判断是好评还是差评的问题。
2.多分类:是将文本划分为多个类别,比如将新闻归为政治类,娱乐类,生活类等等。
3.多标签分类:是给文本贴上多个不同的标签,比如一部小说可以同时被划分为多个主题,可能既是修仙小说,又是玄幻小说。
文本分类主要有两种方法:传统机器学习文本分类算法、深度学习文本分类算法。
1.传统机器学习方法:特征提取 + 分类器。就是将文本转换成固定维度的向量,然后送到分类器中进行分类。
2.深度学习方法:可以自动提取特征,实现端到端的训练,有较强的特征表征能力,这也是深度学习进行文本分类的效果往往要好于传统的方法。
2.中文邮件分类
这里就不具体介绍SVM算法的基本思想了,想要了解SVM的算法原理可以自行百度搜索或者查看相关书籍,这里只需知道怎么使用scikit-learn库提供的SVM模型就可以了。
2.1 基本步骤
中文邮件分类
1.导入数据,划分训练集和测试集。
2.文本预处理。
3.将文本数据转化为数字特征数据。
4.构建分类器。
5.训练分类器。
6.测试分类器。
2.2 准备数据集
本次用到的数据包含 3 个文件, ham_data.txt 文件里面包含 5000 条正常邮件样本,spam_data.txt 文件里面包含 5001 个垃圾邮件样本,stopwords 是停用词表。
先来看一下中文邮件的具体内容,这里打开垃圾邮件。
1.读取数据样本
path1 = 'ham_data.txt' # 正常邮件存放地址
path2 = 'spam_data.txt' # 垃圾邮件地址
#下面打开正常样本
h = open(path1, encoding='utf-8')
#另外因为准备的数据是每一行一封邮件,这里我们要用 readlines() 来以行来读取文本的内容。
h_data = h.readlines()
h_data[0:3] # 显示前3封正常邮件
#同样方式处理垃圾样本
s = open(path2, encoding='utf-8')
s_data = s.readlines()
s_data[0:3] # 显示前3个封垃圾邮件
读取样本之后,h_data 是由 5000 条邮件字符串组成的正常邮件样本集, s_data 是由 5001 条邮件字符串组成的垃圾邮件样本集。下面我们需要将两个样本组合起来,并贴上标签,将正常邮件的标签设置为 1,垃圾邮件的标签设置为 0。
2. 为样本集打上标签
使用numpy库生成一个 len(h_data) 长的的一维全 1 列表。
import numpy as np
h_labels = np.ones(len(h_data)).tolist() # 生成一个len(h_data)长的的一维全1列表
h_labels[0:10] # 显示前10个数据
再生成一个 len(h_data) 长的的一维全 0 列表。
s_labels = np.zeros(len(s_data)).tolist()
s_labels[0:10] # 我们显示前10个数据
为了得到一个由所有邮件样本组合起来的样本集 datas 以及一个标签集 labels,我们需要拼接样本集和标签集。
datas = h_data + s_data # 将正常样本和垃圾样本整合到datas当中
labels = h_labels + s_labels
由于没有提前准备测试集,所以这里需要在 10001 个样本当中,随机划出 25% 个样本和标签来作为我们的测试集,剩下的 75% 作为训练集来进行训练我们的分类器。这里可以使用 scikit-learn 工具里面的 train_test_split 类。
sklearn.model_selection.train_test_split(datas, labels, test_size=0.25, random_state=5 )
datas : 样本集
labels: 标签集
train_test_split:划分到测试集的比例
random_state:随机种子,取同一个的随机种子那么每次划分出的测试集是一样的。
from sklearn.model_selection import train_test_split
train_d, test_d, train_y, test_y = train_test_split(
datas, labels, test_size=0.25, random_state=5)
#观察一下随机抽取之后的前 20 个标签
train_y[0:20]
调用这个函数之后,就可以得到: train_d:样本集、test_d:测试集、train_y:样本标签、test_y:测试标签。
2.3 文本预处理
分词
# 现在对文本进行分词
import jieba
def tokenize_words(corpus):
tokenized_words = jieba.cut(corpus)
tokenized_words = [token.strip() for token in tokenized_words]
return tokenized_words
去除停用词
因为有一些词是没有实际意义的,比如:【的】【了】等,因此需要将其剔除。
首先加载停用词表。这里也可以自行在网上下载,编码格式为 utf-8,每行一个停用词。
def remove_stopwords(corpus): # 函数输入为样本集
sw = open('stop_word.txt', encoding='utf-8') # stopwords 停词表
sw_list = [l.strip() for l in sw] # 去掉文本中的回车符,然后存放到 sw_list 当中
# 调用前面定义好的分词函数返回到 tokenized_data 当中
tokenized_data = tokenize_words(corpus)
# 过滤停用词,对每个在 tokenized_data 中的词 data 进行判断
# 如果 data 不在 sw_list 则添加到 filtered_data 当中
filtered_data = [data for data in tokenized_data if data not in sw_list]
# 用''将 filtered_data 串起来赋值给 filtered_datas
filtered_datas = ' '.join(filtered_data)
return filtered_datas # 返回去停用词之后的 datas
# 接下来,完成分词与去停用词
from tqdm.notebook import tqdm
def preprocessing_datas(datas):
preprocessed_datas = []
# 对 datas 当中的每一个 data 进行去停用词操作
# 并添加到上面刚刚建立的 preprocessed_datas 当中
for data in tqdm(datas):
data = remove_stopwords(data)
preprocessed_datas.append(data)
return preprocessed_datas # 返回去停用词之后的新的样本集
通过分词与去停用词这两步,我们得到了分词过后并且去除停用词了的样本集 pred_train_d 和 测试集 pred_test_d。
#对训练集进行预处理
pred_train_d = preprocessing_datas(train_d)
pred_train_d[0]
#对测试集进行预处理
pred_test_d = preprocessing_datas(test_d)
pred_test_d[0]
2.4 特征提取
这里使用的是IF-IDF算法来进行文本特征提取的。同样,此处不会细讲该算法的原理细节,想要深入了解该算法原理可以自行百度搜索或者查看相关书籍,通过 scikit-learn 来实现 TF-IDF 模型。
sklearn.feature_extraction.text.TfidfVectorizer(min_df=1,norm=‘l2’,smooth_idf=True,use_idf=True,ngram_range=(1,1))
min_df: 忽略掉词频严格低于定阈值的词。
norm :标准化词条向量所用的规范。
smooth_idf:添加一个平滑 IDF 权重,即 IDF 的分母是否使用平滑,防止 0 权重的出现。
use_idf: 启用 IDF 逆文档频率重新加权。
ngram_range:同词袋模型
首先加载 TfidfVectorizer 类,并定义 TF-IDF 模型训练器 vectorizer,对预处理过后的样本进行特征提取。
#首先加载 TfidfVectorizer 类,并定义 TF-IDF 模型训练器 vectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(
min_df=1, norm='l2', smooth_idf=True, use_idf=True, ngram_range=(1, 1))
#对预处理过后的 pred_train_d 进行特征提取
tfidf_train_features = vectorizer.fit_transform(pred_train_d)
tfidf_train_features
tfidf_test_features = vectorizer.transform(pred_test_d)
tfidf_test_features
通过这一步,我们得到了 7500 个 28335 维数的向量作为我们的训练特征集, 2501 个 28335 维数的向量作为我们的测试特征集。
注意:用训练集训练好特征后的 vectorizer 来提取测试集的特征: 注意这里不能用 vectorizer.fit_transform() 要用 vectorizer.transform()否则,将会对测试集单独训练 TF-IDF 模型,而不是在训练集的词数量基础上做训练。这样词总量跟训练集不一样多,排序也不一样,将会导致维数不同,最终无法完成测试。
2.5 分类
在获得 TF-IDF 特征之后,我们可以调用 SGDClassifier() 类来训练 SVM 分类器。
sklearn.linear_model.SGDClassifier(loss=‘hinge’)
SGDClassifier 是一个多个分类器的组合,当参数 loss=‘hinge’ 时是一个支持向量机分类器。
加载 SVM 分类器,并调整 loss = ‘hinge’,然后我们将之前准备好的样本集和样本标签送进 SVM 分类器进行训练。接下来我们用测试集来测试一下分类器的效果。
#加载 SVM 分类器,并调整 loss = 'hinge'
from sklearn.linear_model import SGDClassifier
svm = SGDClassifier(loss='hinge')
#然后我们将之前准备好的样本集和样本标签送进 SVM 分类器进行训练。
svm.fit(tfidf_train_features, train_y)
#接下来我们用测试集来测试一下分类器的效果
predictions = svm.predict(tfidf_test_features)
predictions
计算分类准确率
from sklearn import metrics
accuracy_score = np.round(metrics.accuracy_score(test_y, predictions), 2)
accuracy_score
至此,一个中文邮件分类的任务就告一段落了,这里总结一下:本文使用的机器学习库为scikit-learn,代码编写的环境为jupyter。中间也用到了一些python的第三方库:如jieba等。
demo完整代码与样本数据下载地址:demo完整代码与样本数据下载地址