在所有人类语言中,一句话、一段文本、一篇文章都是有一个个的词组成的。词是包含独立意义的最小文本单元,将长文本拆分成单个独立的词汇的过程叫做分词。分词之后,文本原本的语义将被拆分到在更加精细化的各个独立词汇中,词汇的结构比长文本简单,对于计算机而言,更容易理解和分析,所以,分词往往是自然语言处理的第一步。
对于英文文本,句子中的词汇可以通过空格很容易得进行划分,但是在我们中文中则不然,没有明显的划分标志,所以需要通过专门的方法(算法)进行分词。在Python中,有多种库实现了各种方法支持中文分词,例如:jieba、hanlp、pkuseg等。在本篇中,先来说说jieba分词。
1 四种模式分词¶
(1)精确模式:试图将句子最精确地切开,适合文本分析。精确分词模式对应的方法是jieba.cut,该方法接受四个输入参数: 需要分词的字符串;cut_all 参数用来控制是否采用全模式,值为False时表示采用精确分词模式;HMM 参数用来控制是否使用 HMM 模型。
(2)全模式:把句子中所有的可以成词的词语都扫描出来,速度非常快,但是不能解决歧义。全模式同样是调用jieba.cut方法实现,不过cut_all参数值设置为True。
(3)搜索引擎模式:在精确模式的基础上,对长词再词切分,提高召回率,适合用于搜索引擎分词。搜索引擎模式对应的方法是jieba.cut_for_search。该方法接受两个参数:需要分词的字符串;是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细。
注意,待分词的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建议直接输入 GBK 字符串,可能无法预料地错误解码成 UTF-8。 另外,jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),或者用jieba.lcut 以及 jieba.lcut_for_search 直接返回 list。
在分词文本过大时,可以使用jieba.enable_parallel()来开启并行分词模式,使用多进行进行分词。
import jieba
strt = "据报道,因雷暴雨天气,该地区川亿线变压器跌落式熔断器引流线烧断,造成电压不稳" # 精确模式,默认hi精确模式,所以可以不指定cut_all=False sl = jieba.cut(strt, cut_all=False, HMM=False) print("精确模式分词结果:", ",".join(sl)) print('\n') # 全模式 sl = jieba.cut(strt, cut_all=True) print("全模式分词结果:", ",".join(sl) ) print('\n') # 搜索引擎模式 sl = jieba.cut_for_search(strt) print("搜索引擎模式分词结果:", ",".join(sl))
精确模式分词结果: 据,报道,,,因,雷暴雨,天气,,,该,地区,川,亿,线,变压器,跌落,式,熔断器,引流,线,烧,断,,,造成,电压,不,稳 全模式分词结果: 据,报道,,,因,雷暴,雷暴雨,暴雨,雨天,天气,,,该地,地区,川,亿,线,变压,变压器,跌落,式,熔断,熔断器,引流,流线,烧,断,,,造成,成电,电压,不稳 搜索引擎模式分词结果: 据,报道,,,因,雷暴,暴雨,雷暴雨,天气,,,该,地区,川,亿线,变压,变压器,跌落,式,熔断,熔断器,引流,线,烧断,,,造成,电压,不,稳
在上面分词结果中,可以看出,部分专有名词并没有被正确划分,这时候可以试试将HMM参数设置为True,表示使用隐马尔可夫模型发现新词:
sl = jieba.cut(strt, cut_all=False, HMM=True) print(",".join(sl))
据,报道,,,因,雷暴雨,天气,,,该,地区,川,亿线,变压器,跌落,式,熔断器,引流线,烧断,,,造成,电压,不稳
遗憾的是,就算使用了发现新词功能,只是进一步划分出了“烧断”一词,仍然没有对“川亿线”、“跌落式”、“引流线”等词汇进行正确提取,这是隐马尔可夫模型分词原理决定的,只能发现在原始训练词库汇总频率稍高的新词,而对没有出现过的新词无能为力。这时候,我们可以自定义词典来添加新词。
2 自定义词典分词¶
2.1 添加词典¶
自定义词典分词是指在分词前,用户手动将部分词汇添加到结巴分词的词库中。通过这种方式可以进一步提取出默认词库中没有的词汇,提高分词准确率。
在添加词典通过jieba.load_userdict(file_name)方法实现,参数file_name是词典文件名,词典中一个词占一行,每一行分三部分,即词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。
可见,“川亿线”一次已被正确划分。再来试试jieba.load_userdict(file_name)添加词库,这种方式的好处是可以一次性添加多个词。我们先将词汇写入文件,请读者自行新建一个名为“dict.txt”的文件,写入一下内容:
跌落式 90 a 熔断器 80 a 跌落式熔断器 10 n 引流线 80 n
strt = "据报道,因雷暴雨天气,该地区川亿线变压器跌落式熔断器引流线烧断,造成电压不稳"
jieba.load_userdict('dict.txt') sl = jieba.cut(strt, cut_all=False) print(",".join(sl))
据,报道,,,因,雷暴雨,天气,,,该,地区,川,亿线,变压器,跌落式熔断器,引流线,烧断,,,造成,电压,不,稳
从上面结果可以看出,添加自定义的词库后,原本没有被正确划分出来的词,如“跌落式熔断器”,“引流线”等都被正确划分出来了。
2.2 调整词典¶
添加自定义的词典后,有时候我们还是需要对词典进行微调。
- jieba.suggest_freq()
在上述我们自定义的词典中包含“跌落式”、“熔断器”、“跌落式熔断器”三个词,但是分词结果中按最长的“跌落式熔断器”进行分词,如果我们分别进行划分,可以使用jieba.suggest_freq()方法调节单个词语的词频,使其能(或不能)被正确划分出来。
jieba.suggest_freq(('跌落式', '熔断器'), tune=True)
0
sl = jieba.cut(strt, cut_all=False, HMM=False) print(",".join(sl))
据,报道,,,因,雷暴雨,天气,,,该,地区,川,亿,线,变压器,跌落,式,熔断器,引流线,烧,断,,,造成,电压,不稳
这时候,“跌落式”、“熔断器”两个词就没有在被完整划分。
同时,对于居中的“不稳”一词,在上述分词结果中被分成了两个部分,这里也可以通过jieba.suggest_freq()调整,使其分为一个完整词。
jieba.suggest_freq('不稳', tune=True)
12
sl = jieba.cut(strt, cut_all=False) print(",".join(sl))
据,报道,,,因,雷暴雨,天气,,,该,地区,川,亿线,变压器,跌落式熔断器,引流线,烧断,,,造成,电压,不稳
sl = jieba.cut(strt, cut_all=False) print(",".join(sl))
据,报道,,,因,雷暴雨,天气,,,该,地区,川,亿线,变压器,跌落,式,熔断器,引流线,烧断,,,造成,电压,不稳
- jieba.add_word(word, freq=None, tag=None)
jieba.add_word()用于向词库中添加一个词,该方法有三个参数:word指需要添加的词,freq是词频,tag是词性,其中,词频和词性可省略。例如,在上述分词中没有被正确划分为一个词“川亿线”添加到词库中:
jieba.add_word('川亿线') sl = jieba.cut(strt, cut_all=False, HMM=False) print(",".join(sl))
据,报道,,,因,雷暴雨,天气,,,该,地区,川亿线,变压器,跌落,式,熔断器,引流线,烧,断,,,造成,电压,不稳
- del_word(word)
del_word(word)可以删除词库中的一个词。
jieba.del_word('川亿线') sl = jieba.cut(strt, cut_all=False, HMM=False) print(",".join(sl))
据,报道,,,因,雷暴雨,天气,,,该,地区,川,亿,线,变压器,跌落,式,熔断器,引流线,烧,断,,,造成,电压,不稳
3 词性标注¶
jieba分词中,通过jieba.posseg提供词性标注支持。jieba.posseg.cut()方法返回一个生成器,jieba.posseg.lcut()放回一个列表。
import jieba.posseg
sl = jieba.posseg.cut(strt) for x in sl: print('分词: ', x.word, ' 词性: ', x.flag)
分词: 据 词性: p 分词: 报道 词性: v 分词: , 词性: x 分词: 因 词性: p 分词: 雷暴雨 词性: nr 分词: 天气 词性: n 分词: , 词性: x 分词: 该 词性: r 分词: 地区 词性: n 分词: 川 词性: j 分词: 亿线 词性: m 分词: 变压器 词性: n 分词: 跌落 词性: v 分词: 式 词性: k 分词: 熔断器 词性: n 分词: 引流线 词性: n 分词: 烧断 词性: v 分词: , 词性: x 分词: 造成 词性: v 分词: 电压 词性: n 分词: 不稳 词性: a
结巴分词中,各种词性标注含义如下所示:
4 关键词提取¶
4.1 基于 TF-IDF 算法的关键词抽取¶
TF-IDF算法可以分为TF和IDF两个部分来理解,TF指的是Term Frequency,意思词频。
IDF指的是inverse document frequency,即逆文档频率。
从计算公式上可以看出,TF衡量的是某个词在当前文档的频率,IDF衡量的是包含某个词的文档占比,当占比越少时,IDF越大。TF-IDF算法的基本思想:某一个词在本篇文章中词频越高(TF越大),而在其他文章中出现次数少(IDF小),则越有可能是本篇文章的关键词。将TF与IDF相结合,进行关键词筛选,于是有了一个新的量TF-IDF:
jieba分词中实现了TF-IDF算法,进行关键词选取,可以通过调用jieba.analyse.extract_tags()使用这一功能,在extract_tags()有4个参数:
- sentence 为待提取的文本
- topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20
- withWeight 为是否一并返回关键词权重值,默认值为 False
- allowPOS 仅包括指定词性的词,默认值为空,即不筛选
import jieba.analyse as analyse
txt = "自然语言处理是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。自然语言处理是一门融语言学、计算机科学、数学于一体的科学。因此,这一领域的研究将涉及自然语言,即人们日常使用的语言,所以它与语言学的研究有着密切的联系,但又有重要的区别。自然语言处理并不是一般地研究自然语言,而在于研制能有效地实现自然语言通信的计算机系统,特别是其中的软件系统。因而它是计算机科学的一部分。"
analyse.extract_tags(txt, topK=5, withWeight=True, allowPOS=())
[('自然语言', 1.1237629576061539), ('计算机科学', 0.4503481350267692), ('语言学', 0.27566262244215384), ('研究', 0.2660770221507693), ('领域', 0.24979825580353845)]
4.2 基于 TextRank 算法的关键词抽取¶
TextRank算法在原理上比DF-ITF算法复杂许多,本文不在展开介绍。在jieba分词库中使用TextRank算法通过调用jieba.analyse.textrank方法实现。该方法参数与上述使用TF-IDF算法时调用的analyse.extract_tags的参数一致。
txt = "自然语言处理是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。自然语言处理是一门融语言学、计算机科学、数学于一体的科学。因此,这一领域的研究将涉及自然语言,即人们日常使用的语言,所以它与语言学的研究有着密切的联系,但又有重要的区别。自然语言处理并不是一般地研究自然语言,而在于研制能有效地实现自然语言通信的计算机系统,特别是其中的软件系统。因而它是计算机科学的一部分。"
analyse.textrank(txt, topK=5, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'))
['研究', '领域', '计算机科学', '实现', '处理']