中文邮件分类

按照分类要求的不同,文本分类主要可以分为二分类,多分类,多标签分类三大类。

  • 二分类问题:也是最基础的分类,顾名思义是将文本归为两种类别,比如将正常邮件邮件划分问题,垃圾邮件或者正常邮件。一段影评,判断是好评还是差评的问题。
  • 多分类问题:是将文本划分为多个类别,比如将新闻归为政治类,娱乐类,生活类等等。
  • 多标签分类:是给文本贴上多个不同的标签,比如一部小说可以同时被划分为多个主题,可能既是修仙小说,又是玄幻小说。

文本分类主要有两种方法:传统机器学习文本分类算法、深度学习文本分类算法。

  • 传统方法:特征提取(也可以叫做特征工程)+ 分类器。就是将文本转换成固定维度的向量,然后送到分类器中进行分类。
  • 深度学习方法:可以自动提取特征,实现端到端的训练,有较强的特征表征能力,所以深度学习进行文本分类的效果往往要好于传统的方法。

基于SVM的垃圾邮件判断

# 本次我们用到的数据包含 3 个文件, `ham_data.txt` 文件里面包含 5000 条正常邮件样本,`spam_data.txt` 文件里面包含 5001 个垃圾邮件样本,`stopwords` 是停用词表。
wget -nc "http://labfile.oss.aliyuncs.com/courses/1208/ham_data.txt"
wget -nc "http://labfile.oss.aliyuncs.com/courses/1208/spam_data.txt"
wget -nc "http://labfile.oss.aliyuncs.com/courses/1208/stop_word.txt"# 给正常邮件和垃圾邮件贴上标签,我们用 `1` 代表正常邮件,`0` 代表垃圾邮件。
path1 = 'ham_data.txt'  # 正常邮件存放地址
path2 = 'spam_data.txt'  # 垃圾邮件地址

# 下面用 utf-8 编码模式打开正常样本:
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()
# 读取之后,我们的 h_data 数是由 5000 条邮件字符串组成的正常邮件样本集, s_data 是由 5001 条邮件字符串组成的垃圾邮件样本集。下面我们为将两个样本组合起来,并贴上标签。
# 生成一个 `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(s_data)` 长的的一维全 0 列表:
s_labels = np.zeros(len(s_data)).tolist()
s_labels[0:10]  # 我们显示前10个数据

# 拼接样本集和标签集:
datas = h_data + s_data  # 将正常样本和垃圾样本整合到datas当中
labels = h_labels + s_labels
# 这样我们得到一个由所有邮件样本组合起来的样本集 `datas`  以及一个标签集 `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_l, test_l = train_test_split(
    datas, labels, test_size=0.25, random_state=5)

# 调用这个函数之后,就可以得到:
# - `train_d`:样本集
# - `test_d`:测试集
# - `train_l`:样本标签
# - `test_l`:测试标签
# 为了代码简洁方便理解,我们将分词设计成 `tokenize_words` 函数,供后续直接调用。 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,每行一个停用词。为了方便调用,我们将去除停用词的操作放到 `remove_stopwords` 函数当中。 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 # 接下来,我们对分词结果进行处理。这里使用 `tqdm` 模块显示进度。安装 `tqdm` 模块: get_ipython().system('pip install tqdm') from tqdm import tqdm_notebook def preprocessing_datas(datas): preprocessed_datas = [] # 对 datas 当中的每一个 data 进行去停用词操作,并添加到上面刚刚建立的 preprocessed_datas 当中 for data in tqdm_notebook(datas): data = remove_stopwords(data) preprocessed_datas.append(data) return preprocessed_datas # 返回去停用词之后的新的样本集 # 然后用上面预处理函数对样本集进行处理。 pred_train_d = preprocessing_datas(train_d) pred_test_d = preprocessing_datas(test_d) # 通过上面两步,我们得到了分词过后并且去除停用词了的样本集 `pred_train_d` 和 测试集 `pred_test_d`。 # 在进行分词及去停用词处理过后,用 TF-IDF 模型特征来进行特征提取。 from sklearn.feature_extraction.text import TfidfVectorizer # 定义 TF-IDF 模型训练器 `vectorizer` 。 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 # 通过这一步,我们得到了 7500 个 5946 维数的向量作为我们的训练特征集。 # 用训练集训练好特征后的 `vectorizer` 来提取测试集的特征: # 注意这里不能用 `vectorizer.fit_transform()` 要用 `vectorizer.transform()`,否则,将会对测试集单独训练 TF-IDF 模型,而不是在训练集的词数量基础上做训练。这样词总量跟训练集不一样多,排序也不一样,将会导致维数不同,最终无法完成测试。 tfidf_test_features = vectorizer.transform(pred_test_d) tfidf_test_features # 完成之后,我们得到 2501 个 5946 维数的向量作为我们的测试特征集。 # 在获得 TF-IDF 特征之后,我们可以调用 `SGDClassifier()` 类来训练 SVM 分类器。 # sklearn.linear_model.SGDClassifier(loss='hinge') # `SGDClassifier` 是一个多个分类器的组合,当参数 `loss='hinge'` 时是一个支持向量机分类器。 # 加载 SVM 分类器: from sklearn.linear_model import SGDClassifier # 调整 `loss = 'hinge'` : svm = SGDClassifier(loss='hinge') # 然后我们将之前准备好的样本集和样本标签送进 SVM 分类器进行训练。 svm.fit(tfidf_train_features, train_l) # 接下来我们用测试集来测试一下分类器的效果。 predictions = svm.predict(tfidf_test_features) predictions # 为了直观显示分类的结果,我们用 scikit-learn 库中的 `accuracy_score` 函数来计算一下分类器的准确率 。 # sklearn.metrics.accuracy_score(test_l, prediction) # 这个函数的作用是为了计算 `test_l` 中与 `prediction` 相同的比例。即准确率。 # 加载 `metrics` 函数: from sklearn import metrics # 用测试标签和预测结果 计算分类准确率。`np.round(X,2)` 的作用是 X 四舍五入后保留小数点后2位数字。 accuracy_score = np.round(metrics.accuracy_score(test_l, predictions), 2) accuracy_score # 随机提取一个样本查看其预测结果: print('邮件类型:', test_l[20]) print('预测邮件类型:', predictions[20]) print('文本:', test_d[20])

 

posted @ 2019-02-14 15:47  飞末  阅读(1201)  评论(0编辑  收藏  举报