13-垃圾邮件分类2
1.读取
1 import nltk 2 from nltk.corpus import stopwords 3 from nltk.stem import WordNetLemmatizer 4 import csv 5 import numpy as np 6 from sklearn.naive_bayes import MultinomialNB 7 from sklearn.naive_bayes import GaussianNB 8 from sklearn.metrics import confusion_matrix 9 from sklearn.metrics import classification_report 10 11 # 读取数据集 12 file_path = r'E:\SMSSpamCollection' 13 sms = open(file_path, 'r', encoding='utf-8') 14 sms_data = [] # 字符串的列表 15 sms_label = [] 16 csv_reader=csv.reader(sms,delimiter='\t') 17 for line in csv_reader: 18 sms_label.append(line[0]) 19 sms_data.append(preprocessing(line[1])) # 对每封邮件做预处理,生成有效词的字符串 20 sms.close() 21 # print(sms_data)
2.数据预处理
1 def get_wordnet_pos(treebank_tag): # 根据词性,生成还原参数pos 2 if treebank_tag.startswith('J'): 3 return nltk.corpus.wordnet.ADJ 4 elif treebank_tag.startswith('V'): 5 return nltk.corpus.wordnet.VERB 6 elif treebank_tag.startswith('N'): 7 return nltk.corpus.wordnet.NOUN 8 elif treebank_tag.startswith('R'): 9 return nltk.corpus.wordnet.ADV 10 else: 11 return nltk.corpus.wordnet.NOUN 12 13 14 def preprocessing(text): # 预处理 15 # text=text.decode("utf-8") 16 tokens = [word for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)] # 分词 17 stops = stopwords.words('english') 18 tokens = [token for token in tokens if token not in stops] # 停用词 19 20 tokens = [token.lower() for token in tokens if len(token) >= 3] # 大小写,短词 21 22 tag = nltk.pos_tag(tokens) # 词性 23 lmtzr = WordNetLemmatizer() 24 tokens = [lmtzr.lemmatize(token, pos=get_wordnet_pos(tag[i][1])) for i, token in enumerate(tokens)] # 词性还原 25 # tokens = [lmtzr.lemmatize(token) for token in tokens] # 复数 26 # tokens = [lmtzr.lemmatize(token, pos='a') for token in tokens] # 比较级 27 # tokens = [lmtzr.lemmatize(token, pos='v') for token in tokens] # 时态 28 preprocessed_text = ' '.join(tokens) 29 return preprocessed_text
3.数据划分—训练集和测试集数据划分
1 # 3.数据划分—训练集和测试集数据划分 2 from sklearn.model_selection import train_test_split 3 4 x_train, x_test, y_train, y_test = train_test_split(sms_data, sms_label, test_size=0.2, random_state=0, stratify=sms_label) 5 6 len(sms_label) 7 len(x_train) 8 len(y_test)
运行结果:
4.文本特征提取
将文本数据转化为特征向量的过程,比较常用的文本特征表示法为词袋法
词集:0、1
词袋模型:
- 不考虑词语出现的顺序,每个出现过的词汇单独作为一列特征,这些不重复的特征词汇集合为词表
- 每一个文本都可以在很长的词表上统计出一个很多列的特征向量
- 如果每个文本都出现的词汇,一般被标记停用词,不计入特征向量
主要有两个api来实现:
sklearn.feature_extraction.text.CountVectorizer # 只考虑词汇在文本中出现的频率
sklearn.feature_extraction.text.TfidfVectorizer # 除了考虑某词汇在本文本出现的频率,还关注包含这个词汇的其他文本的数量
# 能够削减高频没意义的词汇出现带来的影响,挖掘更有意义的特征
TfidfVectorizer:把原始文本转化为tf-idf的特征矩阵
1 from sklearn.feature_extraction.text import TfidfVectorizer 2 vectorizer = TfidfVectorizer() 3 4 X_train = vectorizer.fit_transform(x_train) 5 X_test = vectorizer.transform(x_test)
观察邮件与向量的关系
vectorizer.vocabulary_ # 生成词袋-(单词, 位置)
向量还原为邮件
X_train.toarray()[0] # 邮件0的向量
a = np.nonzero(X_train.toarray()[0]) # 该函数输入一个矩阵,返回扁平化后矩阵中非零元素的位置(index)
a
X_train.toarray()[0][a] # 向量的非零元素的值
b = vectorizer.vocabulary_ # 词汇表
key_list = []
for key, value in b.items():
if value in a:
key_list.append(key) # 非零元素对应的单词
key_list # 向量非零元素对应的单词
x_train[0] # 向量化之前的邮件
4.模型选择
from sklearn.naive_bayes import GaussianNB
from sklearn.naive_bayes import MultinomialNB
1 # 4.模型选择 2 # 1)模型一: 3 gnb = GaussianNB() 4 gnb.fit(X_train, y_train) 5 y_gnb = gnb.predict(X_test) 6 # 2)模型二: 7 mnb = MultinomialNB() 8 mnb.fit(X_train, y_train) 9 y_mnb = mnb.predict(X_test)
说明选择多项式模型的原因:
多项式模型以单词为粒度,常用于文本分类,特征是单词,值是单词的出现次数。
而高斯模型是将特征为连续型变量的值转化为离散型变量的值。
5.模型评价:混淆矩阵,分类报告
说明混淆矩阵的含义
混淆矩阵是数据科学、数据分析和机器学习中总结分类模型预测结果的情形分析表,以矩阵形式将数据集中的记录按照真实的类别与分类模型作出的分类判断两个标准进行汇总。
说明准确率、精确率、召回率、F值分别代表的意义
关于评价指标的定义:
准确率(正确率)= 所有预测正确的样本/总的样本 (TP+TN)/总
精确率 = 将正类预测为正类 / 所有预测为正类 TP/(TP+FP)
召回率 = 将正类预测为正类 / 所有正真的正类 TP/(TP+FN)
F值 = 精确率 * 召回率 * 2 / ( 精确率 + 召回率) (F 值即为精确率和召回率的调和平均值)
# 5.模型评价:混淆矩阵-confusion_matrix cm = confusion_matrix(y_test, y_mnb) print(cm) cr = classification_report(y_test, y_mnb) print(cr) (cm[0][0]+cm[1][1])/np.sum(cm)
6.比较与总结
如果用CountVectorizer进行文本特征生成,与TfidfVectorizer相比,效果如何?
从混淆矩阵的对比中可以看出:用CountVectorizer误报的情况增加,正确预测的数据更低
从分类报告的对比中可以看出:用CountVectorizer准确率更高、精确率总的更低(被分为正例的示例中实际为正例的比例较低)、召回率更高(分类器对正例的识别能力较强)、F值更高(试验方法比较有效)
两者结合来看,用CountVectorizer虽在总样本中表现看似优秀,但其实际对样本个体预测的误差要高于使用TfidfVectorizer。因为TfidfVectorizer能够过滤掉一些常见的却无关紧要本的词语,同时保留影响整个文本的重要字词,更适用于垃圾邮件分类。
额外补充:如果采用先特征提取再划分数据的方法,获取x_train的长度时,会报错"TypeError:稀疏矩阵长度不明确"
如果按提示添加"getnnz()"或"shape[0]"会提示"int类型没有len()方法"
解决方案: