文本分类问题

文本分类问题

下面我们来看一个文本分类问题,经典的新闻主题分类,用朴素贝叶斯怎么做。

In [193]:

1
2
3
4
5
6
import jieba
seg_list = jieba.cut("他来到东海识别区",cut_all=False)
print("/" .join(seg_list) )
jieba.add_word('东海识别区')
seg_list = jieba.cut("他来到东海识别区",cut_all=False)
print("/ " .join(seg_list) )
1
2
他/来到/东海/识别区
他/ 来到/ 东海识别区

In [175]:

1
2
3
4
5
6
7
8
9
10
11
12
#coding: utf-8
import os
import time
import random
import jieba #处理中文
import nltk #处理英文
import sklearn
from sklearn.naive_bayes import MultinomialNB
import numpy as np
import pylab as pl
import matplotlib.pyplot as plt
import nltk

文本处理

1、把训练样本划分为训练集和测试集

2、统计了词频,按词频降序生成词袋

In [137]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 文本处理,也就是样本生成过程
def text_processing(folder_path, test_size=0.2):
folder_list = os.listdir(folder_path)
# os.listdir 方法用于返回路径下包含的文件或文件夹的名字的列表
# folder_list = ['C000008', 'C000014', 'C000013', 'C000022', 'C000023', 'C000024', 'C000010', 'C000020', 'C000016']

data_list = []
class_list = []

# 遍历文件夹,每个文件夹里是一个新闻的类别
for folder in folder_list:
new_folder_path = os.path.join(folder_path, folder)
# os.path.join就是把两个路径拼接
# new_folder_path = ./Database/SogouC/Sample/C000008

files = os.listdir(new_folder_path)
# 读取new_folder_path路径下的文件名
# ['15.txt', '14.txt', '16.txt', '17.txt', '13.txt', '12.txt', '10.txt', '11.txt', '19.txt', '18.txt']

# 读取文件
j = 1
for file in files:
if j > 100:
# 怕内存爆掉,只取100个样本文件,你可以注释掉取完,
# 这里每个类别下只有10个样本,没事
break
with open(os.path.join(new_folder_path, file), 'r') as fp:
raw = fp.read()
# read() 返回值为str,每次读取整个文件,将文件所有内容放到一个字符串变量中
# readline() 返回值为str,每次只读取一行,每行的内容放在一个字符串变量中
# readlines() 返回值为list,一次读取整个文件,每行的内容放在一个字符串变量中作为列表的一个元素。

## 是的,随处可见的jieba中文分词
jieba.enable_parallel(4) # 开启并行分词模式,参数为并行进程数,不支持windows
word_cut = jieba.cut(raw, cut_all=False) # 精确模式,返回的结构是一个可迭代的genertor
word_list = list(word_cut) # genertor转化为list,每个词unicode格式
jieba.disable_parallel() # 关闭并行分词模式

data_list.append(word_list) #训练集list
#class_list.append(folder.decode('utf-8')) #类别,str.decode会报错
class_list.append(folder) #训练集的标签类别
j += 1



## 下面手动粗暴地划分训练集和测试集
data_class_list = zip(data_list, class_list) # zip 函数返回一个zip对象
data_class_list = list(data_class_list) # 需要用list转换成列表

random.shuffle(data_class_list) # shuffle随机打乱样本
index = int(len(data_class_list)*test_size)+1
train_list = data_class_list[index:]
test_list = data_class_list[:index]

train_data_list, train_class_list = zip(*train_list)
# 解压缩,文本和类别分开返回的是元组格式,可以在用list转换
test_data_list, test_class_list = zip(*test_list)
#以上划分训练集和测试集其实可以用sklearn自带的部分做
#train_data_list, test_data_list, train_class_list, test_class_list = \
#sklearn.model_selection.train_test_split(data_list, class_list, test_size=test_size)



# 统计词频放入all_words_dict
all_words_dict = {}
for word_list in train_data_list:
for word in word_list:
#if all_words_dict.has_key(word):已删除此方法
if word in all_words_dict:
all_words_dict[word] += 1
else:
all_words_dict[word] = 1
# all_words_dict={'\n': 1257, '\u3000': 1986, '有': 175, '江湖': 1,.....}
# 同样以上有现成的统计词频的API可以调用
# from collections import Counter
# Counter(train_data_list)

all_words_tuple_list = sorted(all_words_dict.items(), key=lambda f:f[1], reverse=True)
# 内建函数sorted第一个参数需为list,all_words_dict.items()转化为列表,键和值为元组
# key函数代表按元组的的词频排序,并降序返回结果
# all_words_tuple_list = [(',', 3424), ('的', 2527), ('\u3000', 1734), ('。', 1482),.....]

#all_words_list = list(zip(*all_words_tuple_list)[0]) 报错,需要修改
all_words_list,_ = zip(*all_words_tuple_list) # 解压缩
all_words_list = list(all_words_list)
# all_words_list = [',', '的', '\u3000', '。', '\n', '在', ' ', '、', '了', '“',.....]

return all_words_list, train_data_list, test_data_list, train_class_list, test_class_list

In [138]:

1
2
3
4
5
6
7
8
9
10
11
12
print ("start")

## 文本预处理
folder_path = './Database/SogouC/Sample'
all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = \
text_processing(folder_path, test_size=0.2)
print(len(all_words_list)) # 9748个不重复单词
print(all_words_list[:100])
print(len(train_data_list)) # 71个训练集样本
print(len(test_data_list)) # 19个测试集样本
print(len(train_class_list)) # 71个训练集标签
print(len(test_class_list)) # 19个测试集标签
1
2
3
4
5
6
7
start
9481
[',', '的', '\u3000', '。', '\n', ' ', ';', '&', 'nbsp', '、', '在', '了', '“', '是', '”', '\x00', ':', '和', '中国', '有', '也', '我', '对', '就', '将', '—', '上', '这', '游客', '都', '旅游', '中', '不', '为', '要', '与', '年', '等', '而', ';', '可以', '月', '(', ')', '导弹', '大陆', '一个', '从', '人', '3', '到', '但', '你', '公司', '说', '火炮', '日', '(', ')', '他', '考生', '台军', '认为', '北京', '时', '多', '还', '个', '1', '.', '能', '《', '》', '已经', '解放军', '一种', '会', '时间', '自己', '来', '新', '各种', '大', '0', '5', '进行', '市场', '主要', '我们', '以', '后', '美国', '五一', '让', '支付', '黄金周', '增长', '并', '成为', '最']
71
19
71
19

停用词文件去重

这个停用词文件不是很官方,所以需要清洗下

In [140]:

1
2
3
4
5
6
7
8
9
10
# 粗暴的词去重
def make_word_set(words_file):
words_set = set() # 集合格式
with open(words_file, 'r') as fp:
for line in fp.readlines(): # 循环取出每一行
word = line.strip()
# line.strip() 当()为空时,默认删除空白符(包括'\n','\r','\t',' ')
if len(word)>0 and word not in words_set: # 去重
words_set.add(word)
return words_set

In [145]:

1
2
3
4
5
# 生成stopwords_set
stopwords_file = './stopwords_cn.txt' # 停用词列表文件
stopwords_set = make_word_set(stopwords_file) # 首先停用词去重
print(len(stopwords_set))
print(stopwords_set)
1
2
428
{'得了', '还是', '所在', '为此', '如同下', '并且', '许多', '但', '不尽然', '无', '却', '所', '据此', '分别', '向', '遵照', '多会', '而后', '如下', '再有', '的确', '此外', '距', '而已', '何处', '在于', '说来', '不料', '且', '于', '亦', '不单', '而是', '本人', '正如', '前者', '别', '才能', '啦', '只因', '受到', '甚至于', '另一方面', '此', '只有', '可是', '您', '别的', '别处', '些', '或', '不论', '这会', '其它', '况且', '有', '此次', '因此', '去', '毋宁', '它们', '根据', '基于', '当地', '依据', '然而', '虽然', '因为', '从而', '对比', '怎', '以上', '诸如', '倘若', '一些', '否则', '所以', '那边', '每', '并非', '之', '另', '简言之', '只限', '连同', '反之', '这般', '几', '往', '是', '既', '各自', '该', '因之', '得', '来', '不然', '么', '甚至', '为', '处在', '全部', '如上', '按照', '加之', '介于', '正巧', '好', '似的', '不过', '有些', '那样', '此地', '凡是', '每当', '不是', '万一', '由于', '曾', '而', '照着', '依照', '彼时', '就要', '然后', '以为', '只需', '为何', '谁', '到', '不仅仅', '既然', '当然', '起', '嗡', '此间', '果然', '与否', '随', '因', '值此', '即便', '格里斯', '趁着', '要不然', '那个', '至于', '乃', '随后', '有时', '何况', '当', '使', '只是', '为着', '可见', '即使', '以来', '着', '吧', '截至', '个', '为什么', '用', '除外', '他们', '随时', '这些', '还', '了', '还有', '啥', '甚而', '他', '但是', '并不', '某某', '如果说', '从', '各', '别人', '趁', '这里', '别说', '以', '不光', '其次', '就算', '沿着', '如果', '大家', '朝着', '正是', '对待', '自己', '不尽', '虽', '有关', '替代', '哟', '对于', '以及', '这儿', '加以', '一', '不外乎', '尽管如此', '个别', '再则', '哪', '她', '除此', '也', '自身', '用来', '不', '什么', '人们', '便于', '又', '此处', '非但', '如何', '哇', '那里', '只消', '既是', '凭', '的', '故而', '打', '嘻嘻', '对方', '谁人', '乃至', '以至', '再', '什么样', '何', '任何', '最', '由此', '而且', '自', '直到', '为了', '固然', '除了', '假如', '人', '才是', '据', '的话', '来自', '有的', '我们', '从此', '关于', '向着', '那么', '给', '或者', '某个', '等等', '光是', '怎么样', '嘿嘿', '如此', '只', '多少', '来说', '那般', '哪个', '那时', '首先', '赖以', '这边', '我', '于是', '另外', '她们', '已', '不如', '哪儿', '及', '及至', '很', '多么', '哪些', '又及', '其', '还要', '既往', '以致', '或者说', '如是', '不仅', '为止', '本着', '鉴于', '什么的', '而外', '譬如', '那儿', '咱们', '只要', '凭借', '后者', '则', '比如', '一切', '个人', '何以', '那', '咱', '上', '在', '如若', '他人', '一旦', '哪怕', '这样', '只限于', '仍', '之所以', '所有', '或是', '诸位', '总之', '怎么办', '其中', '怎么', '若', '作为', '怎样', '本身', '凡', '连带', '由', '不管', '不但', '只怕', '和', '看', '同', '把', '宁可', '那些', '彼此', '不只', '唯有', '继而', '呵呵', '正值', '至', '你们', '下', '跟', '针对', '并', '以免', '不至于', '经过', '你', '就是', '虽说', '小', '与其', '至今', '一来', '让', '们', '即', '诸', '要不', '沿', '出来', '两者', '此时', '遵循', '如', '这', '这么', '出于', '较之', '比', '嘛', '某些', '以便', '可以', '若非', '各位', '今', '逐步', '这个', '它', '例如', '其他', '反而', '就是说', '随着', '致', '同时', '可', '被', '接着', '靠', '除非', '某', '后', '尔', '其余', '与', '全体', '仍旧', '进而', '儿', '自从', '开外', '拿', '要是', '无论', '要么', '若是', '因而', '本地', '尽管', '何时'}

词袋中选取有代表性的特征词

第一步生成的词袋里有很多通用的、无意义的词语,需要去掉。
有代表性的词语很大概率是一些对最终类别区分有作用的词语。并且后面这些词语会作为特征作为模型的输入。

In [125]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def words_dict(all_words_list, deleteN, stopwords_set=set()):
# 选取特征词
feature_words = []
n = 1
for t in range(deleteN, len(all_words_list), 1):
# 循环时从第20个开始,也就是舍弃前20个词语
if n > 1000: # feature_words的维度1000
break

if not all_words_list[t].isdigit() and \
all_words_list[t] not in stopwords_set and \
1<len(all_words_list[t])<5:
# isdigit() 方法检测字符串是否只由数字组成,返回True和False
# 满足三个条件:不是数字;不在停用词表;长度2~4
feature_words.append(all_words_list[t])
n += 1
return feature_words

In [149]:

1
2
3
4
5
deleteN = 20 
# 删除前20个词语,可以调整这个数值
# 越靠前的词语出现的越频繁,有可能所有类别中都出现很多次,这类词语是可以去掉的。
feature_words = words_dict(all_words_list, deleteN, stopwords_set)=
print(feature_words[:100])
1
['游客', '旅游', '导弹', '大陆', '一个', '公司', '火炮', '考生', '台军', '认为', '北京', '已经', '解放军', '一种', '时间', '各种', '进行', '市场', '主要', '美国', '五一', '支付', '黄金周', '增长', '成为', '复习', '很多', '目前', '没有', '记者', '问题', '分析', '远程', '万人次', '射程', '接待', '基础', '部分', '部署', '作战', '一定', '选择', '辅导班', '考试', '词汇', '技术', '比赛', '文章', '完全', '可能', '收入', '工作', '时候', '今年', '表示', '期间', '企业', 'VS', '能力', '达到', '毕业生', '上海', '表现', '影响', '比较', '人数', '用户', '相对', '专家', '服务', '重要', '拥有', '需要', '训练', '开始', '销售', '通过', '阵地', '资料', '情况', '要求', '阅读', '老师', '新浪', '坦克', '网络', '军事', '英语', '项目', '历史', '设计', '几乎', '这是', '写作', '日本', '考古', '不同', '提高', '活动', '公里']

训练和测试集生成固定长度的词向量特征

这步为后面数据输入进贝叶斯模型训练做准备。

因为文本长度不一,所以每个样本需要固定好维度,才能喂给模型训练。

In [153]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 文本特征
def text_features(train_data_list, test_data_list, feature_words, flag='nltk'):
def text_features(text, feature_words): # text的定义在下面
text_words = set(text) # 样本去重
## -----------------------------------------------------------------------------------
if flag == 'nltk':
## nltk特征 dict
features = {word:1 if word in text_words else 0 for word in feature_words}
# 遍历每个样本词语,凡是样本的词语出现在1000个特征词里,就记录下来,保存为字典格式,键为词语,值为1,否则值为0。

elif flag == 'sklearn':
## sklearn特征 list
features = [1 if word in text_words else 0 for word in feature_words]
# 同上,遍历每个样本词语,结果不是字典,出现即为1,不出现为0

else:
features = []
## -----------------------------------------------------------------------------------
return features
train_feature_list = [text_features(text, feature_words) for text in train_data_list]
# text为每一个训练的样本,返回值是二维列表

test_feature_list = [text_features(text, feature_words) for text in test_data_list]
# train为每一个测试样本,返回值是二维列表

return train_feature_list, test_feature_list

In [169]:

1
2
3
4
5
6
7
flag = 'sklearn'
train_feature_list, test_feature_list = \
text_features(train_data_list, test_data_list, feature_words, flag)
print(len(train_feature_list)) #
print(len(test_feature_list)) #
print(len(test_feature_list[5])) # 每个样本的维度都是1000
print(test_feature_list[5][0:100]) # 打印测试集的第5个样本的前100个值
1
2
3
4
71
19
1000
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]

贝叶斯模型开始训练和预测

In [176]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 分类,同时输出准确率等
def text_classifier(train_feature_list, test_feature_list,
train_class_list, test_class_list, flag='nltk'):
## -----------------------------------------------------------------------------------
if flag == 'nltk':
## 使用nltk分类器
train_flist = zip(train_feature_list, train_class_list)
train_flist = list(train_flist)
test_flist = zip(test_feature_list, test_class_list)
train_flist = list(test_flist)
classifier = nltk.classify.NaiveBayesClassifier.train(train_flist)
test_accuracy = nltk.classify.accuracy(classifier, test_flist)

elif flag == 'sklearn':
## sklearn分类器
classifier = MultinomialNB().fit(train_feature_list, train_class_list)
# MultinomialNB()的使用方法和参数见:https://www.cnblogs.com/pinard/p/6074222.html

test_accuracy = classifier.score(test_feature_list, test_class_list)
else:
test_accuracy = []
return test_accuracy

In [177]:

1
2
3
4
flag='sklearn'
test_accuracy = text_classifier(train_feature_list, test_feature_list,
train_class_list, test_class_list, flag)
print(test_accuracy)
1
0.7368421052631579

可视化

这步调参,查看不同的deleteNs对模型效果的影响

In [179]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
print ("start")

## 文本预处理
folder_path = './Database/SogouC/Sample'


all_words_list, train_data_list, test_data_list, train_class_list, test_class_list = text_processing(folder_path, test_size=0.2)

# 生成stopwords_set
stopwords_file = './stopwords_cn.txt'
stopwords_set = make_word_set(stopwords_file)

## 文本特征提取和分类
# flag = 'nltk'
flag = 'sklearn'
deleteNs = range(0, 1000, 20)
test_accuracy_list = []
for deleteN in deleteNs:
# feature_words = words_dict(all_words_list, deleteN)
feature_words = words_dict(all_words_list, deleteN, stopwords_set)
train_feature_list, test_feature_list = text_features(train_data_list, test_data_list, feature_words, flag)
test_accuracy = text_classifier(train_feature_list, test_feature_list, train_class_list, test_class_list, flag)
test_accuracy_list.append(test_accuracy)
print (test_accuracy_list)

# 结果评价
#plt.figure()
plt.plot(deleteNs, test_accuracy_list)
plt.title('Relationship of deleteNs and test_accuracy')
plt.xlabel('deleteNs')
plt.ylabel('test_accuracy')
plt.show()
#plt.savefig('result.png')

print ("finished")
1
2
start
[0.6842105263157895, 0.6842105263157895, 0.7368421052631579, 0.7368421052631579, 0.7368421052631579, 0.7368421052631579, 0.7368421052631579, 0.6842105263157895, 0.6842105263157895, 0.6842105263157895, 0.6842105263157895, 0.6842105263157895, 0.6842105263157895, 0.7368421052631579, 0.7368421052631579, 0.7894736842105263, 0.7368421052631579, 0.7368421052631579, 0.7368421052631579, 0.7368421052631579, 0.7368421052631579, 0.7894736842105263, 0.7894736842105263, 0.7894736842105263, 0.7894736842105263, 0.7894736842105263, 0.7894736842105263, 0.7894736842105263, 0.7894736842105263, 0.7368421052631579, 0.6842105263157895, 0.6842105263157895, 0.6842105263157895, 0.7368421052631579, 0.7368421052631579, 0.6842105263157895, 0.7368421052631579, 0.6842105263157895, 0.6842105263157895, 0.7368421052631579, 0.6842105263157895, 0.631578947368421, 0.6842105263157895, 0.6842105263157895, 0.7368421052631579, 0.7894736842105263, 0.7894736842105263, 0.7368421052631579, 0.7368421052631579, 0.7894736842105263]

img

1
finished
文章目录
  1. 1. 文本分类问题
    1. 1.1. 文本处理¶
    2. 1.2. 停用词文件去重
    3. 1.3. 词袋中选取有代表性的特征词
    4. 1.4. 训练和测试集生成固定长度的词向量特征
    5. 1.5. 贝叶斯模型开始训练和预测
    6. 1.6. 可视化
|