导读:近年来,随着NLP技术的日益成熟,开源实现的分词工具越来越多,如Ansj、盘古分词等。在本文中,我们选取了Jieba进行介绍和案例展示,主要基于以下考虑:
作者:涂铭 刘祥 刘树春
如需转载请联系大数据(ID:hzdashuju)
Jieba分词官网地址是: https://github.com/fxsjy/jieba
可以采用如下方式进行安装:
pip install jieba
Jieba分词结合了基于规则和基于统计这两类方法。
首先基于前缀词典进行词图扫描,前缀词典是指词典中的词按照前缀包含的顺序排列,例如词典中出现了“上”,之后以“上”开头的词都会出现在这一部分,例如“上海”,进而会出现“上海市”,从而形成一种层级包含结构。
如果将词看作节点,词和词之间的分词符看作边,那么一种分词方案则对应着从第一个字到最后一个字的一条分词路径。
因此,基于前缀词典可以快速构建包含全部可能分词结果的有向无环图,这个图中包含多条分词路径,有向是指全部的路径都始于第一个字、止于最后一个字,无环是指节点之间不构成闭环。
基于标注语料,使用动态规划的方法可以找出最大概率路径,并将其作为最终的分词结果。对于未登录词,Jieba使用了基于汉字成词的HMM模型,采用了Viterbi算法进行推导。(进一步了解中文分词算法,请点击:入门科普:一文看懂NLP和中文分词算法(附代码举例))
01 Jieba的三种分词模式
Jieba提供了三种分词模式:
下面是使用这三种模式的对比。
import jieba
sent = '中文分词是文本处理不可或缺的一步!'
seg_list = jieba.cut(sent, cut_all=True)
print('全模式:', '/ '.join(seg_list))
seg_list = jieba.cut(sent, cut_all=False)
print('精确模式:', '/ '.join(seg_list))
seg_list = jieba.cut(sent)
print('默认精确模式:', '/ '.join(seg_list))
seg_list = jieba.cut_for_search(sent)
print('搜索引擎模式', '/ '.join(seg_list))
运行结果如下:
中文/分词/是/文本/文本处理/本处/处理/不可/不可或缺/或缺/的/一步//
中文/分词/是/文本处理/不可或缺/的/一步/!
中文/分词/是/文本处理/不可或缺/的/一步/!
中文/分词/是/文本/本处/处理/文本处理/不可/或缺/不可或缺/的/一步/!
可以看到,全模式和搜索引擎模式下,Jieba将会把分词的所有可能都打印出来。一般直接使用精确模式即可,但是在某些模糊匹配场景下,使用全模式或搜索引擎模式更适合。
接下来将结合具体案例,讲解Jieba分词的具体用法。
02 实战之高频词提取
高频词一般是指文档中出现频率较高且非无用的词语,其一定程度上代表了文档的焦点所在。针对单篇文档,可以作为一种关键词来看。对于如新闻这样的多篇文档,可以将其作为热词,发现舆论焦点。
高频词提取其实就是自然语言处理中的TF(Term Frequency)策略。其主要有以下干扰项:
下面采用Jieba分词,针对搜狗实验室的新闻数据,进行高频词的提取。
数据见:
https://github.com/nlpinaction/learning-nlp chapter3/data/news下,包括9个目录,目录下均为txt文件,分别代表不同领域的新闻。
该数据本质上是一个分类语料,这里我们只挑选其中一个类别,统计该类的高频词。
首先,进行数据的读取:
def get_content(path):
with open(path, 'r', encoding='gbk', errors='ignore') as f:
content = ''
for l in f:
l = l.strip()
content += l
return content
该函数用于加载指定路径下的数据。
定义高频词统计的函数,其输入是一个词的数组:
def get_TF(words, topK=10):
tf_dic = {}
for w in words:
tf_dic[w] = tf_dic.get(w, 0) + 1
return sorted(tf_dic.items(), key = lambda x: x[1], reverse=True)[:topK]
最后,主函数如下,这里仅列举了求出高频词的前10个:
def main():
import glob
import random
import jieba
files = glob.glob('./data/news/C000013/*.txt')
corpus = [get_content(x) for x in files]
sample_inx = random.randint(0, len(corpus))
split_words = list(jieba.cut(corpus[sample_inx]))
print('样本之一:'+corpus[sample_inx])
print('样本分词效果:'+'/ '.join(split_words))
print('样本的topK(10)词:'+str(get_TF(split_words)))
运行主函数,结果如下:
中国卫生部官员24日说,截至2005年底,中国各地报告的尘肺病病人累计已超过60万例,职业病整体防治形势严峻。卫生部副部长陈啸宏在当日举行的“国家职业卫生示范企业授牌暨企业职业卫生交流大会”上说,中国各类急性职业中毒事故每年发生200多起,上千人中毒,直接经济损失达上百亿元。职业病病人总量大、发病率较高、经济损失大、影响恶劣。卫生部24日公布,2005年卫生部共收到全国30个省、自治区、直辖市(不包括西藏、港、澳、台)各类职业病报告12212例,其中尘肺病病例报告9173例,占75.11%。陈啸宏说,矽肺和煤工尘肺是中国最主要的尘肺病,且尘肺病发病工龄在缩短。去年报告的尘肺病病人中最短接尘时间不足三个月,平均发病年龄40.9岁,最小发病年龄20岁。陈啸宏表示,政府部门执法不严、监督不力,企业生产水平不高、技术设备落后等是职业卫生问题严重的原因。“但更重要的原因是有些企业法制观念淡薄,社会责任严重缺位,缺乏维护职工健康的强烈的意识,职工的合法权益不能得到有效的保障。”他说。为提高企业对职业卫生工作的重视,卫生部、国家安全生产监督管理总局和中华全国总工会24日在京评选出56家国家级职业卫生工作示范企业,希望这些企业为社会推广职业病防治经验,促使其他企业作好职业卫生工作,保护劳动者健康。
中国卫生部/官员/24/日/说/,/截至/2005/年底/,/中国/各地/报告/的/尘肺病/病人/累计/已/超过/60/万例/,/职业病/整体/防治/形势严峻/。/卫生部/副/部长/陈啸宏/在/当日/举行/的/“/国家/职业/卫生/示范/企业/授牌/暨/企业/职业/卫生/交流/大会/”/上/说/,/中国/各类/急性/职业/中毒/事故/每年/发生/200/多起/,/上千人/中毒/,/直接/经济损失/达上/百亿元/。/职业病/病人/总量/大/、/发病率/较/高/、/经济损失/大/、/影响/恶劣/。/卫生部/24/日/公布/,/2005/年/卫生部/共/收到/全国/30/个省/、/自治区/、/直辖市/(/不/包括/西藏/、/港/、/澳/、/台/)/各类/职业病/报告/12212/例/,/其中/尘肺病/病例/报告/9173/例/,/占/75/./11/%/。/陈啸宏/说/,/矽肺/和/煤工/尘肺/是/中国/最/主要/的/尘肺病/,/且/尘肺病/发病/工龄/在/缩短/。/去年/报告/的/尘肺病/病人/中/最/短/接尘/时间/不足/三个/月/,/平均/发病/年龄/40/./9/岁/,/最小/发病/年龄/20/岁/。/陈啸宏/表示/,/政府部门/执法不严/、/监督/不力/,/企业/生产/水平/不高/、/技术设备/落后/等/是/职业/卫生/问题/严重/的/原因/。/“/但/更/重要/的/原因/是/有些/企业/法制观念/淡薄/,/社会/责任/严重/缺位/,/缺乏/维护/职工/健康/的/强烈/的/意识/,/职工/的/合法权益/不能/得到/有效/的/保障/。/”/他/说/。/为/提高/企业/对/职业/卫生/工作/的/重视/,/卫生部/、/国家/安全/生产/监督管理/总局/和/中华全国总工会/24/日/在/京/评选/出/56/家/国家级/职业/卫生/工作/示范/企业/,/希望/这些/企业/为/社会/推广/职业病/防治/经验/,/促使/其他/企业/作好/职业/卫生/工作/,/保护/劳动者/健康/。
[(',',22),('、',11),('的',11),('。',10),('企业',8),('职业',7),('卫生',6),('尘肺病',5),('说',4),('报告',4)]
通过上面的结果,我们可以发现,诸如“的”“,”“。”“说”等词占据着很高的位置,而这类词对把控文章焦点并无太大意义。我们需要的是类似“尘肺病”这种能够简要概括重点的词汇。常用的办法,是自定义一个停用词典,当遇到这些词时,过滤掉即可。
因此,我们可以自定义词典,然后按照如下方式来进行优化。
首先,整理常用的停用词(包括标点符号),按照每行一个写入到一个文件中(data目录下的stop_words.utf8)。然后定义如下函数,用于过滤停用词:
def stop_words(path):
with open(path) as f:
return [l.strip() for l in f]
接下来修改main函数中第11行分词的部分,改为:
split_words = [x for x in jieba.cut(corpus[sample_inx]) if x not in stop_words('./data/stop_words.utf8')]
高频词前10位结果如下:
[('企业', 8), ('职业', 7), ('卫生', 6), ('尘肺病', 5), ('卫生部', 4), ('报告', 4), ('职业病', 4), ('中国', 3), ('陈啸宏', 3), ('工作', 3)]
对比之前的结果,会发现效果要想有所提升,必须去除了无用标点符号以及“的”等干扰词。注意,本节实战中所用的停用词典为笔者整理的通用词典,一般实践过程中,需要根据自己的任务,定期更新维护。
上面演示了通过Jieba按照常规切词来提取高频词汇的过程。事实上,常用的中文分词器在分词效果上差距并不是特别大,但是在特定场景下常常表现的并不是那么尽如人意。
通常这种情况下,我们需要定制自己的领域词典,用以提升分词的效果。Jieba分词就提供了这样的功能,用户可以加载自定义词典:
jieba.load_userdict('./data/user_dict.utf8')
Jieba要求的用户词典格式一般如下:
朝三暮四 3 i
大数据 5
汤姆 nz
公主坟
每一行为三个部分:词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。该词典文件需为utf8编码。
在提取高频词时,通过更合理的自定义词典加载,能够获得更佳的效果。当然这里仅仅演示了一篇文档的高频词计算,多篇文档的高频词提取也可按照该思路进行整体统计计算。
关于作者:涂铭,阿里巴巴数据架构师,对大数据、自然语言处理、Python、Java相关技术有深入的研究,积累了丰富的实践经验。
刘祥,百炼智能自然语言处理专家,主要研究知识图谱、NLG等前沿技术,参与机器自动写作产品的研发与设计。
刘树春,七牛云高级算法专家,七牛AI实验室NLP&OCR方向负责人,主要负责七牛NLP以及OCR相关项目的研究与落地。
本文摘编自《Python自然语言处理实战:核心技术与算法》,经出版方授权发布。
延伸阅读《Python自然语言处理实战》