ML:sklearn中文文本分类

一、基本概念

文本分类是文本挖掘中的一个重要领域,所谓文本分类,事实上指的是为用户给出每个文档,文本片段等所要归属于哪一类(类别,主题等)的问题。

文本挖掘

文本挖掘(Text Mining)是从非结构化文本信息中获取用户感兴趣或者有用的模式 的过程。其中被普遍认可的文本挖掘定义如下:文本挖掘是指从大量文本数据中抽取事先未知的、可理解的、最终可用的知识的过程,同时运用这些知识更好地组织信息以便将来参考。
  简言之,文本挖掘就是从非结构化的文本中寻找知识的过程。 文本挖掘的七个主要领域:
  (1)搜索和信息检索(IR):存储和文本文档的检索,包括搜索引擎和关键字搜 索。
  (2)文本聚类:使用聚类方法,对词汇,片段,段落或文件进行分组和归类。
  (3)文本分类:对片段,段落或文件进行分组和归类,使用数据挖掘分类方法的 基础上,经过训练的标记示例模型。
  (4)Web 挖掘:在互联网上进行数据和文本挖掘,并特别关注在网络的规模和相 互联系。
  (5)信息抽取(IE):从非结构化文本中识别与提取有关的事实和关系;从非结构 化和半结构化文本制作的结构化数据的过程。
  (6)自然语言处理(NLP):将语言作为一种有意义、有规则的符号系统,在底 层解析和理解语言的任务(例如,词性标注);目前的技术主要从语法、语义 的角度发现语言最本质的结构和所表达的意义。
  (7)概念提取:把单词和短语按语义分组成意义相似的组。

文本分类的应用比较广泛,如:垃圾邮件检测,文件归档,网页分层等等。

二、基本步骤

1.预处理
2.词向量处理
3.TF-IDF权重策略
4.建立分类器
5.评价分类结果

中文特殊情况

1.预处理
2.分词处理
3.词向量处理
4.TF-IDF权重策略
5.建立分类器
6.评价分类结果

1.预处理

主要实现对文本内容的降噪,划分训练集、测试集等过程。
比如,抓取了200篇类别划分明确的百度百科,首先要去除内容中的一些html标签,留下需要用到的内容,删除不必要的内容等等。然后将这些内容进行训练集于测试集的划分。

2.分词处理

相对于英文而言,中文分词有一定的难点,将连续的汉字序列切分成一个个的单词要考虑很多因素,比如句法,精度等等。

比如:火车上有人
正确分词结果:火车 上 有 人
有歧义的结果:火 车上 有 人

分词的精度将直接导致文本的关键信息以至于对最后的分类结果造成影响。

目前已经有比较成熟的分词工具被提供使用:
具体可以参照一些比较好用的中文分词器

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

# 获取分类
data_list = os.listdir('./data') #语料数据
seg_path = './segment' #分词后的数据
seg_train_path = './segment/train' #分词后的训练数据
seg_test_path = './segment/test'#分词后的测试数据

# 获取各个分类内容,并进行切词保存在segment目录下
def segment_content():
for data_cate in data_list:
content = ""
if not os.path.isdir(data_cate) and not data_cate == '.DS_Store':
path = './data/%s'%(data_cate)
files= os.listdir(path)
for file in files:
file_path = './data/%s/%s'%(data_cate,file)
print(file_path)
if file == ".DS_Store":
os.remove(file_path)
else:
f = open(file_path, mode='r', encoding='GB18030')
try:
content = f.read()
except Exception:
print('无法读取')
continue
if not os.path.exists('%s/%s'%(seg_train_path,data_cate)):
os.mkdir('%s/%s'%(seg_train_path,data_cate))
output = open('%s/%s/%s'%(seg_train_path,data_cate,file), 'w')
content_seg = jieba.cut(content) # 使用jieba进行切词
output.write(" ".join(content_seg))
output.close()

以上的代码,已经将文本进行分词并保存(PS.因为使用了交叉验证,所以没有划分测试集,测试集划分方法可以从已经分好词的文件中随机分出20%也可以先分出20%再进行分词)
如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#从切分的数据中分离出测试数据集
def cut_for_test():
seg_data_list = os.listdir(seg_train_path)
for data_dir in seg_data_list:

if os.path.isdir("%s/%s"%(seg_train_path,data_dir)):
file_dir_path = "%s/%s"%(seg_train_path,data_dir)
files = os.listdir(file_dir_path)
test_array = files[-10:]
for subfile in test_array:
if subfile == ".DS_Store":
os.remove("%s/%s"%(file_dir_path,subfile))
continue
if not os.path.exists("%s/%s"%(seg_test_path,data_dir)):
os.mkdir("%s/%s"%(seg_test_path,data_dir))
shutil.move("%s/%s"%(file_dir_path,subfile),"%s/%s/%s"%(seg_test_path,data_dir,subfile))
3.词向量处理

在这一步,会将已经分好词的训练集文本进行词频统计,并生成文本词向量空间,

词向量,顾名思义,就是使用向量来表达词。最常见的表达方式就是”one-hot”,其向量维度为整个语料库中词的总数,每一维代表语料库中的一个词(出现为1,不出现为0)
关于这一点可以参考

期间为了更好的进行下一步的TF-IDF的处理,可以将一些类似于(呢,啊,嗯)之类的停用词进行过滤。

4.TF-IDF权重策略

使用 TF-IDF 发现特征词,并抽取为反映文档主题 的特征

可以参照参考,主要工作就是提取文本的关键词信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#第三步,处理词向量,包括去除停用词
def content_vectorizer(data):

#载入停用词
# 语料向量化
#if not os.path.exists("./vectorizer_content.m"):
vectorizer_content__ = CountVectorizer(stop_words=stopword_list)
#vectorizer_content = joblib.load("./vectorizer_content.m")
x_array = vectorizer_content__.fit_transform(data)
#print(x_array.toarray())
# 计算各个分类内容的 tf-idf值
X_test = TfidfTransformer().fit_transform(x_array)
print('content_vectorizer')
print(vectorizer_content__)
joblib.dump(vectorizer_content__, "./vectorizer_content.m")
return X_test

如果需要使用测试集进行验证,则需要注意以下信息

  • 测试集在向量化时,需要使用和训练器相同的矢量器 否则会报错 ValueError dimension mismatch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def content_vectorizer_test(data):

#载入停用词
# 语料向量化
#if not os.path.exists("./vectorizer_content.m"):
#需要使用和训练器相同的矢量器 否则会报错 ValueError dimension mismatch
vectorizer_content = joblib.load("./vectorizer_content.m")
#vectorizer_content = joblib.load("./vectorizer_content.m")
x_array = vectorizer_content.transform(data)
#print(x_array.toarray())
# 计算各个分类内容的 tf-idf值
X = TfidfTransformer().fit_transform(x_array)
print('content_vectorizer_test')
print(vectorizer_content)
#joblib.dump(vectorizer_content__, "./vectorizer_content.m")
return X
5.建立分类器

通过上面这些步骤,训练集基本已经处理完成,现在只需要使用合适的分类器进行训练处一个合适的分类器模型(sklearn有很多已经封装好的分类器,只需要按照需求进行调用就好)。

6.评价分类结果

训练出来的模型必须进行测试验证,最简单的方式是使用sklearn自带的交叉验证进行评测,当然,也可以按照第一步划分出来的测试集进行测试和验证。当然分值越高说明训练出的模型在处理分类时更加精准。

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
# 创建分类器

def train():
from sklearn.naive_bayes import MultinomialNB

processed_textset,Y = loadtrainset()
#test_textset,Y_test = loadtestset()
X_train = content_vectorizer(processed_textset)
#X_test = content_vectorizer_test(test_textset)
# 多项式贝叶斯分类器
clf = MultinomialNB(alpha=0.001).fit(X_train,Y)

# KNN分类器
#from sklearn.neighbors import KNeighborsClassifier
#clf = KNeighborsClassifier().fit(X_train,Y)
# 随机森林
#from sklearn.ensemble import RandomForestClassifier
#clf = RandomForestClassifier(n_estimators=8)
#clf.fit(X_train,Y)

#predict_result = clf.predict(X_test) #预测结果
print('交叉验证结果')
print(cross_val_score(clf,X_train,Y,cv=10,scoring='accuracy'))#交叉验证 #

#训练完成,保存分类器
joblib.dump(clf, "./分类器.model")

#预测
def predict(path):
check_file_data = file_tools(path)
check_file_vectorizer = content_vectorizer_test(check_file_data)
clf = joblib.load("./分类器.model")
predict_result = clf.predict(check_file_vectorizer)
print('------>预测结果<------')
print(predict_result)

#segment_content()
#cut_for_test()
#train()#如果没有训练过模型,需要先通过此方法进行训练
predict('./test2.txt') #test2为随机在网上复制下来的体育类新闻,预测结果为新闻

记录下完整代码:

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Nov 6 15:05:43 2017

@author: tywin
"""



import os
import shutil

import jieba

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.cross_validation import cross_val_score

from sklearn.externals import joblib

stopword_file = open('./stopword.txt')

stopword_content_string = stopword_file.read()

stopword_list = stopword_content_string.split('\n');


'''
第一步,对于中文,需要进行分词处理
'''


# 获取分类
data_list = os.listdir('./data') #语料数据
seg_path = './segment' #分词后的数据
seg_train_path = './segment/train' #分词后的训练数据
seg_test_path = './segment/test'#分词后的测试数据

# 获取各个分类内容,并进行切词保存在segment目录下
def segment_content():
for data_cate in data_list:
content = ""
if not os.path.isdir(data_cate) and not data_cate == '.DS_Store':
path = './data/%s'%(data_cate)
files= os.listdir(path)
for file in files:
file_path = './data/%s/%s'%(data_cate,file)
print(file_path)
if file == ".DS_Store":
os.remove(file_path)
else:
f = open(file_path, mode='r', encoding='GB18030')
try:
content = f.read()
except Exception:
print('无法读取')
continue
if not os.path.exists('%s/%s'%(seg_train_path,data_cate)):
os.mkdir('%s/%s'%(seg_train_path,data_cate))
output = open('%s/%s/%s'%(seg_train_path,data_cate,file), 'w')
content_seg = jieba.cut(content) # 使用jieba进行切词
output.write(" ".join(content_seg))
output.close()

#从切分的数据中分离出测试数据集
def cut_for_test():
seg_data_list = os.listdir(seg_train_path)
for data_dir in seg_data_list:

if os.path.isdir("%s/%s"%(seg_train_path,data_dir)):
file_dir_path = "%s/%s"%(seg_train_path,data_dir)
files = os.listdir(file_dir_path)
test_array = files[-10:]
for subfile in test_array:
if subfile == ".DS_Store":
os.remove("%s/%s"%(file_dir_path,subfile))
continue
if not os.path.exists("%s/%s"%(seg_test_path,data_dir)):
os.mkdir("%s/%s"%(seg_test_path,data_dir))
shutil.move("%s/%s"%(file_dir_path,subfile),"%s/%s/%s"%(seg_test_path,data_dir,subfile))



'''
第二步 获取训练集 返回训练集数据,类别列表
'''
#获取训练数据集
def loadtrainset():
allfiles = os.listdir(seg_train_path)
processed_textset =[]
allclasstags = []
for thisdir in allfiles:
if not thisdir == '.DS_Store':

content_list = os.listdir('%s/%s'%(seg_train_path,thisdir))

for file in content_list:
if not os.path.isdir('%s/%s/%s'%(seg_train_path,thisdir,file)) and not file == '.DS_Store':
path_name = '%s/%s/%s'%(seg_train_path,thisdir,file)
print(path_name)
f = open(path_name)
processed_textset.append(f.read())
allclasstags.append(thisdir)
return processed_textset,allclasstags

#获得测试数据集
def loadtestset():
allfiles = os.listdir(seg_test_path)
testset = []
testclass = []

for cate_dir in allfiles:
if not cate_dir == '.DS_Store':
test_content_list = os.listdir('%s/%s'%(seg_test_path,cate_dir))
for test_file in test_content_list:
if os.path.isfile('%s/%s/%s'%(seg_test_path,cate_dir,test_file)) and not test_file == '.DS_Store':
file_path_name = '%s/%s/%s'%(seg_test_path,cate_dir,test_file)
print('获得测试数据文件名')
print(file_path_name)
f = open(file_path_name)
testset.append(f.read())
testclass.append(cate_dir)
return testset,testclass


#第三步,处理词向量,包括去除停用词
def content_vectorizer(data):

#载入停用词
# 语料向量化
#if not os.path.exists("./vectorizer_content.m"):
vectorizer_content__ = CountVectorizer(stop_words=stopword_list)
#vectorizer_content = joblib.load("./vectorizer_content.m")
x_array = vectorizer_content__.fit_transform(data)
#print(x_array.toarray())
# 计算各个分类内容的 tf-idf值
X_test = TfidfTransformer().fit_transform(x_array)
print('content_vectorizer')
print(vectorizer_content__)
joblib.dump(vectorizer_content__, "./vectorizer_content.m")
return X_test

#第三步,处理词向量,包括去除停用词
def content_vectorizer_test(data):

#载入停用词
# 语料向量化
#if not os.path.exists("./vectorizer_content.m"):
#需要使用和训练器相同的矢量器 否则会报错 ValueError dimension mismatch
vectorizer_content = joblib.load("./vectorizer_content.m")
#vectorizer_content = joblib.load("./vectorizer_content.m")
x_array = vectorizer_content.transform(data)
#print(x_array.toarray())
# 计算各个分类内容的 tf-idf值
X = TfidfTransformer().fit_transform(x_array)
print('content_vectorizer_test')
print(vectorizer_content)
#joblib.dump(vectorizer_content__, "./vectorizer_content.m")
return X


def file_tools(path):
content_data = []
f = open(path)
content = f.read()
content_seg = jieba.cut(content)
string_content = " ".join(content_seg)
content_data.append(string_content)
return content_data

# 创建分类器

def train():
from sklearn.naive_bayes import MultinomialNB

processed_textset,Y = loadtrainset()
#test_textset,Y_test = loadtestset()
X_train = content_vectorizer(processed_textset)
#X_test = content_vectorizer_test(test_textset)
# 多项式贝叶斯分类器
clf = MultinomialNB(alpha=0.001).fit(X_train,Y)

# KNN分类器
#from sklearn.neighbors import KNeighborsClassifier
#clf = KNeighborsClassifier().fit(X_train,Y)
# 随机森林
#from sklearn.ensemble import RandomForestClassifier
#clf = RandomForestClassifier(n_estimators=8)
#clf.fit(X_train,Y)

#predict_result = clf.predict(X_test) #预测结果
print('交叉验证结果')
print(cross_val_score(clf,X_train,Y,cv=10,scoring='accuracy'))#交叉验证

#训练完成,保存分类器
joblib.dump(clf, "./分类器.model")

#预测
def predict(path):
check_file_data = file_tools(path)
check_file_vectorizer = content_vectorizer_test(check_file_data)
clf = joblib.load("./分类器.model")
predict_result = clf.predict(check_file_vectorizer)
print('------>预测结果<------')
print(predict_result)


#segment_content()
#cut_for_test()
#train()#如果没有训练过模型,需要先通过此方法进行训练
predict('./test2.txt')

github:https://github.com/ilioner/ML-Classifier

参考:

坚持原创技术分享,您的支持将鼓励我继续创作!