郑捷《机器学习算法原理与编程实践》学习笔记(第二章 中文文本分类(一))
2.1 文本挖掘与文本分类的概念
文本挖掘是指从大量的文本数据中抽取事先未知的、可理解的、最终可用的知识的过程,同时运用这些知识更好的组织信息以便将来参考。
- 搜索和信息检索(IR):存储和文本文档的检索,包括搜索引擎个关键字搜索
- 文本聚类:使用聚类方法,对词汇、片段、段落或文件进行分组和归类
- 文本分类:对片段、段落或文件进行分组和归类,在使用数据挖掘分类方法的基础上,经过训练的标记示例模型。
- Web挖掘:在互联网上进行数据和文本的挖掘,并特别关注网络的规模和相互的联系。
- 信息抽取(IE):从非结构化文本中识别与提取有关的事实和关系:从非结构化或半结构化文本中抽取结构化数据的过程。
- 自然语言处理(NLP):将语言作为一种有意义、有规则的符号系统,从底层解析和理解语言的任务(例如词性的标注);目前的技术方法主要从语法、语义的角度发现语言最本质的结构和所表达的意义。
- 概念的提取:把单词和短语按语义分成意义相似的组
2.2 文本分类项目
文本分类的一般步骤:
(1)预处理:去除文本的噪声信息,例如HTML标签、文本的格式转换、检测句子边界等。
(2)中文分词:使用中文分词器为文本分词,并去除停用词。
(3)构建词向量空间:统计文本词频,生成文本的词向量空间。
(4)权重策略—TF-IDF方法:使用TF-IDF发现特征词,并抽取为反应文档主题的特征。
(5)分类器:使用算法训练分类器。
(6)评价分类结果:分类器的测试结果分析
2.2.1 文本预处理
1.选择处理的文本的范围
2.建立分类文本语料库
中文文本分类语料库下载地址为:http://www.datatang.com/datares/go.aspx?dataid=602146
3.文本格式转换
Python去除HTML标签,一般使用lxml
#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 #输出去除标签后的解析结果
4.检测句子边界:标记句子的结束
2.2.2 中文分词介绍
文本的结构化表示简单分为四大类:词向量空间模型、主题模型、依存句法的树表示、RDF的图表示
jieba分词简单的样例代码
#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分词包
#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的数据结构:
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"构建文本对象结束!!!"
2.2.3 Scikit-Learn库简介
2.2.4 向量空间模型
可以从http://www.threedweb.cn/thread-1294-1-1.html
读取停用词:
#读取停用词列表代码 stopword_path = "WorkSpace/TextClassification/train_word_bag/hlt_stop_words.txt" stpwrdlst = readfile(stopword_path).splitlines()
2.2.5 权重策略:TD-IDF方法
含义:如果某个词或短语在一篇文章中出现的频率越高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
词频(Term Frequency,TF)指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数(Term Count)的归一化,以防止它偏向长的文件。对于在某一特定文件里的词语来说,它的重要性可以表示为:
其中,分子是该词在文件中出现的次数,分母是文件中所有字词的出现次数之和:
逆向文件频率(Inverse Document Frequency,IDF)是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到:
- 其中|D|:语料库中的文件总数。
- j:包含词语的文件数目。如果该词语不在语料库中,就会导致分母为零,因此一般情况下使用1+j作为分母
TF-IDF = TF *IDF
2.代码的实现
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)
2.2.6 使用朴素贝叶斯分类模块
最常用的文本分类方法有KNN最近邻算法、朴素贝叶斯算法和支持向量机算法。一般来说,KNN最近邻算法的原理最简单,分类精度尚可,但速度最慢,朴素贝叶斯算法对于短文文本分类效果最好,精度最高;支持向量机算法的优势是支持线性不可分的情况,精度上取中。
测试集随机抽取子训练集中的文档集合,每个分类取10个文档,过滤掉1KB以下的文档。
训练步骤与训练集相同,首先是分词,之后生成文件词向量文件,直至生成词向量模型。不同的是,在训练词向量模型时,需要加载训练集词袋,将测试集产生的词向量映射到训练集词袋的词典中,生成向量空间模型。
#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)
测试集数据的处理:
# (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"构建文本对象结束!!!"
执行朴素贝叶斯训练:
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),"%" 输出结果: 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),"%"
输出结果:
......
../WorkSpace/TextClassification/test_corpus_seg/sport/996.txt :实际类别: sport -->预测类别: art
../WorkSpace/TextClassification/test_corpus_seg/sport/997.txt :实际类别: sport -->预测类别: art
error rate: 13.7777777778 %
2.2.7 分类结果评估
(1)召回率(Recall Rate,也叫查全率):是检索出相关文档数和文档库中所有相关文档的比率,衡量的是检索系统的查全率
召回率(Recall) = 系统检索到的相关文件/系统所有相关的文件的总数
(2)准确率(Precision,也成称为精度):是检索出的相关文档数与检索出的文档总数的比率,衡量的是检索系统的查准率。
准确率(Precision) = 系统检索到的相关文件/系统所有检索到的文档总数
(3)Fβ-Mesure(又称为F-Score):是机器学习领域常用的评价标准,计算公式:
其中,β是参数,p是准确率,R是召回率
当β=1时,就是最常见的F1-Mesure了:
文本分类结果评估,代码如下:
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
资料来源及相关版权所有:《机器学习算法原理与编程实践》 郑捷