郑捷《机器学习算法原理与编程实践》学习笔记(第二章 中文文本分类(一))

  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 

资料来源及相关版权所有:《机器学习算法原理与编程实践》 郑捷

posted on 2016-12-28 22:08  金秀  阅读(2581)  评论(0编辑  收藏  举报

导航