Machine Learning学习笔记之中文文本分类

文本挖掘与文本分类的概念

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

文本分类项目

文本分类的一般步骤:

  (1)预处理:去除文本的噪声信息,例如HTML标签、文本的格式转换、检测句子边界等。

  (2)中文分词:使用中文分词器为文本分词,并去除停用词。

  (3)构建词向量空间:统计文本词频,生成文本的词向量空间。

  (4)权重策略—TF-IDF方法:使用TF-IDF发现特征词,并抽取为反应文档主题的特征。

  (5)分类器:使用算法训练分类器。

  (6)评价分类结果:分类器的测试结果分析

文本预处理

  1.选择处理的文本的范围

  2.建立分类文本语料库

  中文文本分类语料库下载地址为:点击这里(但我阅读本书的时候,此网址已失效,我找的这个
  3.文本格式转换
  Python去除HTML标签,一般使用lxml

1
2
3
4
5
6
7
8
9
10
11
12
13
#coding:utf-8
from lxml import etree,html
import chardet
#HTML文件路径,以及读取文件
path = '1.html'
content = open(path,"rb").read()
print type(content)
page = html.document_fromstring(content)#解析文件
text = page.text_content()#去除所有标签
# print type(text)
# print chardet.detect(text)
print text #输出去除标签后的解析结果 ```

2.2.2 中文分词介绍

文本的结构化表示简单分为四大类:词向量空间模型、主题模型、依存句法的树表示、RDF的图表示

jieba分词简单的样例代码:

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
#jieba分词
#coding:utf-8
import sys
import os
import jieba
#设置UTF-8 Unicode环境
reload(sys)
sys.setdefaultencoding('utf-8')
seg_list = jieba.cut("小明1995年毕业于北京清华大学",cut_all=False)
print "Default Mode:"," ".join(seg_list)#默认切分
seg_list = jieba.cut("小明1995年毕业于北京清华大学",cut_all=True)
print "Full Mode:"," /".join(seg_list)#默认切分
#搜索引擎模式
seg_list = jieba.cut_for_search("小明1995年毕业于北京清华大学")
print "search:"," /".join(seg_list)#默认切分
#词性标注
import jieba.posseg as pseg
words =pseg.cut("我爱北京天安门")
for w in words:
print w.word,w.flag
Prefix dict has been built succesfully.
小明 1995 年 毕业 于 北京 清华大学
Full Mode: 小 /明 /1995 /年 /毕业 /于 /北京 /清华 /清华大学 /华大 /大学
search: 小明 /1995 /年 /毕业 /于 /北京 /清华 /华大 /大学 /清华大学
我 r
爱 v
北京 ns
天安门 ns

本项目创建分词后,语料路径为Root\train_corpus_seg.
1)设置字符集,并导入jieba分词包

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
#coding:utf-8
import sys
import os
import jieba
#设置UTF-8 Unicode环境
reload(sys)
sys.setdefaultencoding('utf-8')
#定义两个函数读取和保存文件
def savefile(savepath,content):#保存至文件
fp = open(savepath,"wb")
fp.write(content)
fp.close()
def readfile(path):
fp = open(path,"rb")
content = fp.read()
fp.close()
return content
#整个语料库分词的主程序
#未分词分类语料库路径
#分词后的分类语料库路径
corpus_path = "WorkSpace/TextClassification/train_corpus_small/"
seg_path = "WorkSpace/TextClassification/train_corpus_seg/"
#获取corpus_path下的所有子目录
catelist = os.listdir(corpus_path)
for mydir in catelist:
#拼出分类子目录的路径
class_path = corpus_path+mydir+"/"
#拼出分词后的语料分类目录
seg_dir = seg_path+mydir+"/"
#是否存在目录,如果没有则创建
if not os.path.exists(seg_dir):
os.makedirs(seg_dir)
#获得类别目录下的所有文件
file_list = os.listdir(class_path)
#遍历类别目录下的所有文件
for file_path in file_list:
#拼出文件名的全路径
fullname = class_path + file_path
#读取文件的内容
content = readfile(fullname).strip()
#删除换行和多余的空格
content = content.replace("\r\n","").strip()
#为文件的内容分词
content_seg = jieba.cut(content)
#将处理后的文件保存到分词后的语目录
savefile(seg_dir+file_path,"".join(content_seg))
print u"中文语料分析结束!!!"

在实际应用中,为了后续的生成空间模型的方便,这些分词后的文本信息还要转化为文本向量信息并对象化需要引入Scikit-Learn库的Bunch的数据结构:

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
import sys
import os
import jieba
from sklearn.datasets.base import Bunch#Bunch类
import cPickle as pickle
#Bunch类提供了一种key,value的对象形式
#target_name:所有分类集名称列表
#label每个文件的分类标签列表
#filename:文件路径
#contents:分词后文件词向量形式
def readfile(path):
fp = open(path,"rb")
content = fp.read()
fp.close()
return content
bunch = Bunch(target_name = [],label = [],filename = [],contents = [])
#将分好词的文本文件转换并持久化为Bunch类形式的代码如下:
#分词语料Bunch对象持久化文件路径
wordbag_path = "WorkSpace/TextClassification/train_word_bag/train_set.dat"
seg_path = "WorkSpace/TextClassification/train_corpus_seg/" #分词后分类语料库路径
catelist = os.listdir(seg_path) #
bunch.target_name.extend(catelist) #按类别信息保存到Bunch对象中
for mydir in catelist:
class_path = seg_path + mydir + "/"
file_list = os.listdir(class_path)
for file_path in file_list:
fullname = class_path + file_path
bunch.label.append(mydir)#保存当前文件的分类标签
bunch.filename.append(fullname)#保存当前文件路径
bunch.contents.append(readfile(fullname).strip())#保存文件词向量
#Bunch对象的持久化
file_obj = open(wordbag_path,"wb")
pickle.dump(bunch,file_obj)
file_obj.close()
print u"构建文本对象结束!!!"

Scikit-Learn库介绍

点击这里

向量空间模型

可以从http://www.threedweb.cn/thread-1294-1-1.html下载
(同样,这里网址失效,我使用的是这个)
读取停用词:

1
2
3
#读取停用词列表代码
stopword_path = "WorkSpace/TextClassification/train_word_bag/hlt_stop_words.txt"
stpwrdlst = readfile(stopword_path).splitlines()

权重策略:TD-IDF方法

含义:如果某个词或短语在一篇文章中出现的频率越高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
词频(Term Frequency,TF)指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数(Term Count)的归一化,以防止它偏向长的文件。对于在某一特定文件里的词语来说,它的重要性可以表示为:TF
其中,分子是该词在文件中出现的次数,分母是文件中所有字词的出现次数之和:
逆向文件频率(Inverse Document Frequency,IDF)是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到:
IDF
其中
|D|:语料库中的文件总数。
j:包含词语的文件数目。如果该词语不在语料库中,就会导致分母为零,因此一般情况下使用1+j作为分母
TF-IDF = TF *IDF
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import sys
import os
from sklearn.datasets.base import Bunch
import cPickle as pickle
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer #TF-IDF向量转换类
from sklearn.feature_extraction.text import TfidfVectorizer
#配置utf-8输出环境
reload(sys)
sys.setdefaultencoding('utf-8')
def readfile(path):
fp = open(path,"rb")
content = fp.read()
fp.close()
return content
stopword_path = "WorkSpace/TextClassification/train_word_bag/hlt_stop_words.txt"
stpwrdlst = readfile(stopword_path).splitlines()
#1.读取和写入Bunch对象的函数
def readbunchobj(path):
file_obj = open(path,"rb")
bunch = pickle.load(file_obj)
file_obj.close()
return bunch
#写入Bunch对象
def writebunchobj(path,bunchobj):
file_obj = open(path,"wb")
pickle.dump(bunchobj,file_obj)
file_obj.close()
#从训练集生成TF-IDF向量词袋
#2.导入分词后的词向量Bunch对象
path = "WorkSpace/TextClassification/train_word_bag/train_set.dat"#词向量空间保存路径
bunch = readbunchobj(path)
#3.构建TF-IDF向量空间模型
tfidfspace = Bunch(target_name = bunch.target_name,label = bunch.label,\
filename = bunch.filename,tdm = [],vocabulary = {})
#使用TfidfVectorizer初始化向量空间模型
vectorizer = TfidfVectorizer(stop_words = stpwrdlst,sublinear_tf = True,max_df = 0.5)
transform = TfidfTransformer()#该类会统计每个词语放入Tf-IDF权重
#4.文本转化为词频矩阵:单独保存字典文件
tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
tfidfspace.vocabulary = vectorizer.vocabulary_
#5.创建词袋的持久化
space_path = "WorkSpace/TextClassification/train_word_bag/tfidfspace.dat"#词向量词袋的保存路径
writebunchobj(space_path,tfidfspace)

使用朴素贝叶斯分类模块

最常用的文本分类方法有KNN最近邻算法、朴素贝叶斯算法和支持向量机算法。一般来说,KNN最近邻算法的原理最简单,分类精度尚可,但速度最慢,朴素贝叶斯算法对于短文文本分类效果最好,精度最高;支持向量机算法的优势是支持线性不可分的情况,精度上取中。
测试集随机抽取子训练集中的文档集合,每个分类取10个文档,过滤掉1KB以下的文档。
训练步骤与训练集相同,首先是分词,之后生成文件词向量文件,直至生成词向量模型。不同的是,在训练词向量模型时,需要加载训练集词袋,将测试集产生的词向量映射到训练集词袋的词典中,生成向量空间模型。

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
#coding:utf-8
import sys
import os
from sklearn.datasets.base import Bunch
import cPickle as pickle
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer #TF-IDF向量转换类
from sklearn.feature_extraction.text import TfidfVectorizer
#设置UTF-8 Unicode环境
reload(sys)
sys.setdefaultencoding('utf-8')
def readfile(path):
fp = open(path,"rb")
content = fp.read()
fp.close()
return content
stopword_path = "../WorkSpace/TextClassification/train_word_bag/hlt_stop_words.txt"
stpwrdlst = readfile(stopword_path).splitlines()
#1.读取和写入Bunch对象的函数
def readbunchobj(path):
file_obj = open(path,"rb")
bunch = pickle.load(file_obj)
file_obj.close()
return bunch
def writebunchobj(path,bunchobj):
file_obj = open(path,"wb")
pickle.dump(bunchobj,file_obj)
file_obj.close()
#2.导入分词后的词向量Bunch对象
path = "../WorkSpace/TextClassification/test_word_bag/test_set.dat"#词向量空间保存路径
bunch = readbunchobj(path)
#3.构建测试集TF-IDF向量空间
testspace = Bunch(target_name = bunch.target_name,label = bunch.label,filenames = \
bunch.filename,tdm = [],vocabulary = {})
#4.导入训练集词袋
trainbunch = readbunchobj("../WorkSpace/TextClassification/train_word_bag/tfidfspace.dat")
#5.使用TfidfVectorizer初始化向量空间模型
vectorizer = TfidfVectorizer(stop_words = stpwrdlst,sublinear_tf = True ,max_df = 0.5,\
vocabulary = trainbunch.vocabulary) #使用训练集词袋向量
transformer = TfidfTransformer()
testspace.tdm = vectorizer.fit_transform(bunch.contents)
testspace.vocabulary = trainbunch.vocabulary
#6.创建词袋的持久化
space_path = "../WorkSpace/TextClassification/test_word_bag/testspace.dat"
writebunchobj(space_path,testspace)
```
测试集数据的处理:
```python
#(1)设置字符集,并导入jieba分词包
# coding:utf-8
import sys
import os
import jieba
#设置UTF-8 Unicode环境
reload(sys)
sys.setdefaultencoding('utf-8')
#定义两个函数读取和保存文件
def savefile(savepath,content):#保存至文件
fp = open(savepath,"wb")
fp.write(content)
fp.close()
def readfile(path):
fp = open(path,"rb")
content = fp.read()
fp.close()
return content
#整个语料库分词的主程序
#未分词分类语料库路径
#分词后的分类语料库路径
corpus_path = "../WorkSpace/TextClassification/test_corpus/"
seg_path = "../WorkSpace/TextClassification/test_corpus_seg/"
#获取corpus_path下的所有子目录
catelist = os.listdir(corpus_path)
for mydir in catelist:
#拼出分类子目录的路径
class_path = corpus_path+mydir+"/"
#拼出分词后的语料分类目录
seg_dir = seg_path+mydir+"/"
#是否存在目录,如果没有则创建
if not os.path.exists(seg_dir):
os.makedirs(seg_dir)
#获得类别目录下的所有文件
file_list = os.listdir(class_path)
#遍历类别目录下的所有文件
for file_path in file_list:
#拼出文件名的全路径
fullname = class_path + file_path
#读取文件的内容
content = readfile(fullname).strip()
#删除换行和多余的空格
content = content.replace("\r\n","").strip()
#为文件的内容分词
content_seg = jieba.cut(content)
#将处理后的文件保存到分词后的语目录
savefile(seg_dir+file_path,"".join(content_seg))
print u"中文语料分析结束!!!"
import sys
import os
import jieba
from sklearn.datasets.base import Bunch#Bunch类
import cPickle as pickle
'''
Bunch类提供了一种key,value的对象形式
target_name:所有分类集名称列表
label每个文件的分类标签列表
filename:文件路径
contents:分词后文件词向量形式
'''
def readfile(path):
fp = open(path,"rb")
content = fp.read()
fp.close()
return content
bunch = Bunch(target_name = [],label = [],filename = [],contents = [])
#将分好词的文本文件转换并持久化为Bunch类形式的代码如下:
#分词语料Bunch对象持久化文件路径
wordbag_path = "../WorkSpace/TextClassification/test_word_bag/test_set.dat"
seg_path = "../WorkSpace/TextClassification/test_corpus_seg/" #分词后分类语料库路径
catelist = os.listdir(seg_path) #
bunch.target_name.extend(catelist) #按类别信息保存到Bunch对象中
for mydir in catelist:
class_path = seg_path + mydir + "/"
file_list = os.listdir(class_path)
for file_path in file_list:
fullname = class_path + file_path
bunch.label.append(mydir)#保存当前文件的分类标签
bunch.filename.append(fullname)#保存当前文件路径
bunch.contents.append(readfile(fullname).strip())#保存文件词向量
#Bunch对象的持久化
file_obj = open(wordbag_path,"wb")
pickle.dump(bunch,file_obj)
file_obj.close()
print u"构建文本对象结束!!!"

执行朴素贝叶斯训练:

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
import cPickle as pickle
from sklearn.naive_bayes import MultinomialNB #导入多项式贝叶斯算法包
def readbunchobj(path):
file_obj = open(path,"rb")
bunch = pickle.load(file_obj)
file_obj.close()
return bunch
#导入训练向量空间
trainpath = "../WorkSpace/TextClassification/train_word_bag/tfidfspace.dat"
train_set = readbunchobj(trainpath)
#导入测试集向量空间
testpath = "../WorkSpace/TextClassification/test_word_bag/testspace.dat"
test_set = readbunchobj(testpath)
#应用朴素贝叶斯
#alpha:0.001 alpha越小,迭代次数越多,精度越高
clf = MultinomialNB(alpha = 0.001).fit(train_set.tdm,train_set.label)
#预测分类结果
predicted = clf.predict(test_set.tdm)
total = len(predicted)
rate = 0
for flabel,file_name,expct_cate in zip(test_set.label,test_set.filenames,predicted):
if flabel != expct_cate:
rate += 1
print file_name,u":实际类别:",flabel,u"-->预测类别:",expct_cate
#精度
print "error rate:",float(rate)*100/float(total),"%"

输出结果:

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
import cPickle as pickle
from sklearn.naive_bayes import MultinomialNB #导入多项式贝叶斯算法包
def readbunchobj(path):
file_obj = open(path,"rb")
bunch = pickle.load(file_obj)
file_obj.close()
return bunch
#导入训练向量空间
trainpath = "../WorkSpace/TextClassification/train_word_bag/tfidfspace.dat"
train_set = readbunchobj(trainpath)
#导入测试集向量空间
testpath = "../WorkSpace/TextClassification/test_word_bag/testspace.dat"
test_set = readbunchobj(testpath)
#应用朴素贝叶斯
#alpha:0.001 alpha越小,迭代次数越多,精度越高
clf = MultinomialNB(alpha = 0.001).fit(train_set.tdm,train_set.label)
#预测分类结果
predicted = clf.predict(test_set.tdm)
total = len(predicted)
rate = 0
for flabel,file_name,expct_cate in zip(test_set.label,test_set.filenames,predicted):
if flabel != expct_cate:
rate += 1
print file_name,u":实际类别:",flabel,u"-->预测类别:",expct_cate
#精度
print "error rate:",float(rate)*100/float(total),"%"

输出结果:

分类结果评估

(1)召回率(Recall Rate,也叫查全率):是检索出相关文档数和文档库中所有相关文档的比率,衡量的是检索系统的查全率

    召回率(Recall) = 系统检索到的相关文件/系统所有相关的文件的总数

(2)准确率(Precision,也成称为精度):是检索出的相关文档数与检索出的文档总数的比率,衡量的是检索系统的查准率。

    准确率(Precision) = 系统检索到的相关文件/系统所有检索到的文档总数
准确率

(3)Fβ-Mesure(又称为F-Score):是机器学习领域常用的评价标准,计算公式:
F-Score
  其中,β是参数,p是准确率,R是召回率
  当β=1时,就是最常见的F1-Mesure了:
F1-Mesure
  文本分类结果评估,代码如下:

1
2
3
4
5
6
7
8
9
10
import numpy as np
from sklearn import metrics
#定义分类精确度
def metrics_result(actual,predict):
print u"精度:{0:.3f}".format(metrics.precision_score(actual,predict))
print u"召回:{0:.3f}".format(metrics.recall_score(actual,predict))
print u"f1-score:{0:.3f}".format(metrics.f1_score(actual,predict))
metrics_result(test_set.label,predicted)

输出结果如下:

精度:0.881

召回:0.862

f1-score:0.860

参考资料:《机器学习算法原理与编程实践》 郑捷