文本挖掘(Text Mining)是从一个非机构化文本信息中获取用户感兴趣或者有用的模式过程。文本挖掘的 7 个主要领域如下:
文本分类步骤:
1)预处理:去除文本的噪声信息,例如 HTML 标签、文本格式转换、检测句子边界。
2)中文分词:使用中文分词器为文本分词,并去除停用词。
3)构建词向量空间:统计文本词频,生成文本的词向量空间。
4)权重策略 ——TF-IDF 方法:使用 TF-IDF 发现特征词,并抽取为反应文档主题的特征。
5)分类器:使用算法训练器分类。
6)评价分类结果:分类器的测试结果分析。
1. 选择处理的文本范围
选择适当的范围取决于文本挖掘任务的目标:对于分类或聚类的任务,往往把整个文档作为处理单位;对于情感分析、文档自动文摘或信息检索,段落或章节可能更合适。
2.建立分类文本语料库
1)训练集语料
复旦大学谭松波中文文本分类语料库
搜狗新闻分类语料库
相对于搜狗新闻分类而言,复旦大学的中文文本语料库小一些,但质量很高,是用于学习教育比较专业的语料。
2)测试语料集
所谓测试集就是待分类的文本语料,可以是训练集的一部分,也可以是外部来源的文本语料。待分类文本资源的获取方式有很多,如公司、图书馆、商业公司、互联网。
3)文本格式转换
不同格式的文本不论采用哪种处理方式,都要统一转换为纯文本文件,例如,网页文本、Word 或 PDF 文件都要转换为纯文本格式。
一般 Python 去除 HTML 标签,较多使用 lxml 库,这是一个 C 语言编写的 XML 扩展库,比使用 re 正则表达式库的标签去除方式性能高得多,适用于海量的网络文本格式转换。
样例代码:
from lxml import html
path = 'data/1.html'
content = open(path,'rb').read()
page = html.document_fromstring(content)
text = page.text_content()
print(text)
4)检测句子边界:标记句子结束
句子边界检测时分解整个文档,并转换成单独句子的过程,对于中文文本,它就是寻找“。”、“?”、“!”的过程,英文中有 “.” 等。
中文分词指的是将一个汉字序列切分成一个单独的词。分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。
中文分词难点:
一个著名的例子:“鸡蛋”、“牛肉”、“下雨”是词吗?如果是,那么“鸭蛋”、“驴肉”、“下雪”、“鸟蛋”、“鱼肉”、“下雾”也应该是词,按照这样的规则组合下去,会产生很多令人费解的结论,如果不是,这些字符串在我们日常生活中使用的频率非常高,而且都有独立的意义。
一般像这种到最后谁也想不明白的问题,最终都交给概率论。
分词是自然语言处理中最基本、最底层的模块,分词精度对后续应用模块影响很大。目前,文本的结构化表示简单分为四大类:词向量空间模型、主题模型、依存句法的树表示、RDF的图表示。
jieba 分词系统支持的分词模式包括默认切分、全切分、搜索引擎切分几种。中文文本分词之后,连续的字序列就变成了以词为单位的向量。向量中的每个分量都是一个有独立意义的词。通过分词,中文文本实现了最基础的结构化。
在实际应用中,为了后续生成词向量空间模型方便,还要引入 Scikit-Learn 库的 Bunch 数据结构将分词后的文本信息转换为文本向量信息并对象化。
from sklearn.datasets.base import Bunch # 导入Bunch类
import os
import pickle
# Bunch 类提供一种 Key,value的对象形式
# target_name:所有分类集名称列表
# label:每个文件的分类标签列表
# filenames:文件路径
# contents:分词后文件词向量形式
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
bunch = Bunch(target_name=[],label=[],filenames=[],contents=[])
wordbag_path = "train_word_bag/train_set.dat" # 分词语料 Bunch 对象持久化文件路径
seg_path = "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.filenames.append(fullname) # 保存当前文件的文件路径
bunch.contents.append(readfile(fullname).strip()) # 保存文件词向量
# Bunch 对象持久化
file_obj = open(wordbag_path,"wb")
pickle.dump(bunch,file_obj)
file_obj.close()
print("构建文本对象结束")
这样就在目录下生成了一个 train_set.dat 文件。此文件保存所有训练文件的所有分类信息,以及每个文件的文件名、文件所属分类信息和词向量。
Scikit-Learn 首页
1 .模块分类
2 . 主要特点
文本分类的结构化方法就是向量空间模型。虽然越来越多的实践证明,这种模型存在着局限性,但是迄今为止,它仍是在文本分类中应用最广泛、最为流行的数据结构,也是很多相关技术的基础,例如推荐系统、搜索引擎。
向量空间模型把文本表示为一个向量,该向量的每个特征表示为文本中出现的词。通常,把训练集中出现的每个不同的字符串都作为一个维度,包括停用词、专有词、词组和其他类型模式串,如电子邮件地址和 URL 。目前,大多数文本挖掘系统都把文本存储为向量空间表示,因为他便于运用机器学习算法。这类算法适用并能有效处理高维空间文本的情况。
由于文本在存储为向量空间时维度比较高,为节省存储空间和提高存储效率,在文本分类之前会自动过滤掉某些字或词,这些字或词即被称为停用词。这类词一般都是意义模糊的常用词,还有一些语气助词,通常它们对文本起不了分类特征的意义。这些停用词都是人工输入,非自动化生成的,生成后的停用词会形成一张停用词表。可以下载停用词表
读取停用词列表代码:
#读取文件
# 1. 读取停用词表
stopword_path = "train_word_bag/hlt_stop_words.txt"
stpwrdlst = readfile(stopword_path).splitines()
生成的词袋中不重复的词 9 个,这里增加词频信息:a(1),ate(3),cat(1),dolphin(1),dog(1),homework(2),my(3),sandwich(1),the(2).
二元表示法:
采用这种方式的忽略了一个句子中出现多个相同词的词频信息,采用整数型计数方式的词向量表示:
接下来,对整数型计数方式进行归一化。归一化可以避免句子长度不一致的问题,便于算法计算。而且对于基于概率的算法,词频信息就变为了概率分布,这就是文档的 TF 信息。
词条的文档频率:a(1/3),ate(3/3),cat(1/3),dolphin(1/3),dog(1/3),homework(2/3),my(3/3),sandwich(1/3),the(2/3).
词袋模型的 IDF 权重:
a log(3/1), ate log(3/3), cat log(3/1), dolphin log(3/1), dog log(3/1), homework log(3/2), my log(3/2), sandwich log(3/1), the log(3/2)。
1. TF-IDF权重策略
计算文本的权重向量,应该选择一个有效的权重方案。最流行的方案是 TF-IDF 权重策略。TF-IDF 的含义是词频逆文档频率,其含义是:如果某个词或短语在一篇文章中出现的频率高,并且在其他文档中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
“my” 这个词在文本中是经常出现的词汇之一。它不仅多次出现在单一的文本中,而且几乎出现在每个文档中。逆文档频率就是使用词条的文档频率来抵消该词的词频对权重的影响,从而得到一个较低的权重。
词频(Term Frequency, TF)指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数(Term Count)的归一化,以防止它偏向长的文件。它的重要性可表示为:
公式为:
∣ D ∣ : |D|: ∣D∣: 语料库中的文件总数。
j : j: j: 包含词语的文件数目。如果该词语不在语料库中,就会导致分母为零,因此一般情况下使用 1 + ∣ { d ∈ D : t ∈ d } ∣ 1+|\{d\in D:t\in d\}| 1+∣{d∈D:t∈d}∣
然后再计算 T F TF TF 与 I D F IDF IDF 的乘积。
某一特定文件内的高词频率,以及该词语在整个文件集合中的低文件频率( T F I D F i j = T F i j I × I D F i j TFIDF_{ij}=TF_{ij}I\times IDF_{ij} TFIDFij=TFijI×IDFij),可以产生出高权重的 T F − I D F TF-IDF TF−IDF 。因此, T F − I D F TF-IDF TF−IDF 倾向于过滤掉常见的词语,保留重要的词语。
import sys
import os
from sklearn.datasets.base import Bunch # 引入 Bunch 类
import pickle # 引入持久化类
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer # TF-IDF 向量转换类
from sklearn.feature_extraction.text import TfidfVectorizer # TF-IDF 向量生成类
# 读取 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()
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
# 从训练集生成 TF-IDF 向量词袋
# 导入分词后的词向量 Bunch 对象
path = "train_word_bag/train_set.dat" # 词向量空间保存路径
bunch = readbunchobj(path)
#读取停用词表
stopword_path = "train_word_bag/hlt_stop_words.txt"
stpwrdlst = readfile(stopword_path).splitines()
# 构建 TF-IDF 词向量对象
tfidfspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames, tdm=[], vocabulary={})
# 使用 TfidfVectorizer
vectorizer = TfidfVectorizer(stop_words=stpwrdlst,sublinear_tf=True,max_df=0.5)
transformer = TfidfTransformer() # 该类会统计每个词语的 TF-IDF 权值
# 文本转为词频矩阵,单独保存字典文件
tfidfspace.tdm = vectorizer.fit_transform(bunch.contents)
tfidfspace.vocabulary = vectorizer.vocabulary_
# 持久化 TF-IDF 向量词袋
space_path = "train_word_bag/tfdifspace.dat" # 词向量词袋保存路径
writebunchobj(space_path,tfidfspace)
最常用的文本分类方法有 KNN 最近邻算法、朴素贝叶斯算法和支持向量机算法。一般而言,KNN 最近邻的算法原理最简单,分类精度尚可,但是速度最慢;朴素贝叶斯算法对于短文本分类效果最好,精度较高;支持向量机的算法的优势是支持线性不可分的情况,精度上取中。
测试步骤与训练集相同,首先是分词,之后生成文件词向量文件,直至生成词向量模型。不同的是,在训练词向量模型时,需要加载训练集词袋,将测试集产生的词向量映射到训练集词袋的词典中,生成向量空间模型。
代码如下:
# 导入分词后的词向量 Bunch 对象
path = "test_word_bag/test_set.dat" # 词向量空间保存路径
bunch = readbunchobj(path)
# 构建测试集 TF-IDF 向量空间
testspace = Bunch(target_name=bunch.target_name, label=bunch.label, filenames=bunch.filenames,tdm=[],vocabulary={})
# 导入训练集的词袋
trainbunch = readbunchobj("tarin_word_bag/tfdifspace.dat")
# 使用 TfidfVectorizer 初始化向量空间模型
vectorizer = TfidfVectorizer(stop_words=stpwrdlst,sublinear_tf=True,max_df=0.5,vocabulary=tarinbunch.vocabulary) # 使用训练集词袋向量
transformer = TfidfTransformer()
testspace.tdm = vectorizer.fit_transform(bunch.contents)
testspace.vocabulary = trainbunch.vocabulary
# 创建词袋的持久化
space_path = "test_word_bag/testsapce.dat" # 词向量空间保存路径
writebunchobj(space_path,testspace)
# 导入多项式贝叶斯算法包
from sklearn.naive_bayes import MultinomialNB
# 执行预测
# 导入训练集向量空间
trainpath = "train_word_bag/tfdifspace.dat"
train_set = readbunchobj(trainpath)
# 导入测试集向量空间
testspath = "test_word_bag/testspace.bat"
test_set = readbunchobj(testspath)
# 应用朴素贝叶斯算法
# alpha:0.001 alpha 越小,迭代次数越多,精度越高
clf = MultinomialNB(alpha=0.001).fit(train_set.tdm,train_set.label)
# 预测分类结果
predicted = clf.prdict(test_set.tdm)
total = len(predicted);rate=0
for flabel,file_name,expect_cate in zip(test_set.label,test_set.filenames,predicted):
if flabel != expect_cate:
rate += 1
print(file_name,":实际类别:",flabel,"-->预测类别:",expect_cate)
# 精度
print("error rate:",float(rate)*100/float(total),"%")
机器学习领域的算法评估有三个基本的指标。
1)召回率(Recall Rate,也叫查全率):是检索出的相关文档数和文档库中所有的相关文档数的比率,衡量的是检查系统的查全率。
注意:准确率和召回率是互相影响的,理想情况下肯定是做到两者都高,但是一般情况下准确率高,召回率就低;召回率高,准确率就低。
3) F β − M e a s u r e F_{\beta}-Measure Fβ−Measure(又称为 F-Score):是机器学习领域常用的评价标准,计算公式为:
其中, β \beta β 是参数, P P P 是准确率, R R R 是召回率
当 β = 1 \beta=1 β=1 时,就是最常见的 F 1 − M e a s u r e F_1-Measure F1−Measure 了。
文本分类项目的分类结果评估,代码如下。
import numpy as np
from sklearn import metrics
# 定义分类精度函数
def metrics_result(actual, predict):
print("精度:{0:.3f}".format(metrics.precision_score(actual,predict)))
print("召回:{0:0.3f}".format(metrics.recall_score(actual,predict)))
print("f1-score:{0:.3f}".format(metrics.f1_score(actual,predict)))
metrics_result(test_set.label,predicted)