赞
踩
在自然语言处理中,分词是一项最基本的技术。中文分词与英文分词有很大的不同,对英文而言,一个单词就是一个词,而汉语以字为基本书写单位,词语之间没有明显的区分标记,需要人为切分。现在开源的中文分词工具有 SnowNLP、THULAC、Jieba 和 HanLP 等,这里梳理下 Jieba 组件的内容。
中文分词技术是中文信息处理的基础,有着极其广泛的实际应用,比如:汉语语言理解、机器翻译、语音合成、自动分类、自动摘要、数据挖掘和搜索引擎等,都需要对中文信息进行分词处理。因此,一个中文分词算法的好坏,会对其后续的应用产生极大的影响。
Jieba 是一个立志于做最好的 Python 中文分词组件,主要涉及的算法有:
Jieba 支持4种模式的分词:精准模式、全模式、搜索引擎模式及 paddle 模式,特点如下:
请注意,paddle 模式使用需安装 paddlepaddle-tiny。目前 paddle 模式支持 jieba v0.40 及以上版本,如果是 jieba v0.40 以下版本,需要升级 jieba,命令如下:
- # 安装paddlepaddle-tiny
- pip install paddlepaddle-tiny==1.6.1
-
- # 升级jieba
- pip install jieba --upgrade
此外,Jieba 还支持中文繁体分词、自定义词典、关键词提取、词性标注、并行分词、ChineseAnalyzer for Whoosh搜索引擎等功能。
jieba 给出了多种的安装方式及说明,参考如下:
我采用的是 pip 命令下载,安装的最新版本是 v-0.42.1:
pip install jieba
jieba 分词常用的函数,如下:
jieba.Tokenizer(dictionary=DEFAULT_DICT) 是新建自定义分词器,可用于同时使用不同词典。jieba.dt 为默认分词器,所有全局分词相关函数都是该分词器的映射,通过源码可以看到:
- # default Tokenizer instance
-
- dt = Tokenizer()
-
- # global functions
-
- get_FREQ = lambda k, d=None: dt.FREQ.get(k, d)
- add_word = dt.add_word
- calc = dt.calc
- cut = dt.cut
- lcut = dt.lcut
- cut_for_search = dt.cut_for_search
- lcut_for_search = dt.lcut_for_search
- del_word = dt.del_word
- get_DAG = dt.get_DAG
- get_dict_file = dt.get_dict_file
- initialize = dt.initialize
- load_userdict = dt.load_userdict
- set_dictionary = dt.set_dictionary
- suggest_freq = dt.suggest_freq
- tokenize = dt.tokenize
- user_word_tag_tab = dt.user_word_tag_tab
下面,用前3种的分词模式为例,演示如下:
- import jieba as jieba
-
-
- def choice_mode(num):
- list_mode = []
- text = '灰树叶飘转在池塘,看飞机轰的一声去远乡'
- if num == "1":
- # 精准模式,cut_all默认False
- list_mode = jieba.lcut(text)
- elif num == "2":
- # 全模式
- list_mode = jieba.lcut(text, cut_all=True)
- elif num == "3":
- # 搜索引擎模式
- list_mode = jieba.lcut_for_search(text)
- return list_mode
测试1:精准模式
- if __name__ == '__main__':
- # 灰/树叶/飘转/在/池塘/,/看/飞机/轰的一声/去/远乡/
- list_mode = choice_mode("1")
- for i in list_mode:
- print(i, end="/")
测试2:全模式
- if __name__ == '__main__':
- # 灰/树叶/飘/转在/池塘/,/看/飞机/轰的一声/一声/去/远/乡/
- list_mode = choice_mode("2")
- for i in list_mode:
- print(i, end="/")
测试3:搜索引擎模式
- if __name__ == '__main__':
- # 灰/树叶/飘转/在/池塘/,/看/飞机/一声/轰的一声/去/远乡/
- list_mode = choice_mode("3")
- for i in list_mode:
- print(i, end="/")
从上面的结果可以看出,对于文本类型,精准模式分词是最准确的。
有时候,我们更需要把文本里的"灰树叶"当成一个词语看待,jieba 提供了 add_word() 方法,可以实现分词的调整。新增如下代码:
jieba.add_word("灰树叶")
重新执行代码,精准模式的测试结果如下:
灰树叶/飘转/在/池塘/,/看/飞机/轰的一声/去/远乡/
在汉语中,词性就是词语的分类,比如名词,形容词,动词等。前面说过 jieba 默认的分词器是 jieba.dt,即 jieba.Tokenizer(),所有全局分词相关函数都是该分词器的映射。而 jieba.posseg.dt 为默认的词性标注分词器。
我们可以通过词性标注分词器,查看词语的词性,如下:
- import jieba as jieba
- import jieba.posseg as posseg
-
- text = '灰树叶飘转在池塘,看飞机轰的一声去远乡'
- # jieba.add_word("灰树叶")
- # jieba.add_word("看飞机")
- # jieba.add_word("去远乡")
- # 默认使用精准模式
- cxs = posseg.lcut(text)
- for i in cxs:
- print(i.word + i.flag, end='/') # flag为词性
结果(不带分词调整):
灰n/树叶n/飘转v/在p/池塘ns/,x/看v/飞机n/轰的一声i/去v/远a/乡n/
每个词语后面的字符就是词性了,如果加上分词调整的话,对应的词性也会发生变化,如下:
灰树叶x/飘转v/在p/池塘ns/,x/看飞机x/轰的一声i/去远乡x/
jieba 默认的分词词性还是很丰富的,默认词性标注对照表,梳理如下:
标注 | 解释 | 标注 | 解释 | 标注 | 解释 |
a | 形容词 | mq | 数量词 | tg | 时语素 |
ad | 副形词 | n | 名词 | u | 助词 |
ag | 形语素 | ng | 例:义 乳 亭 | ud | 例:得 |
an | 名形词 | nr | 人名 | ug | 例:过 |
b | 区别词 | nrfg | 人名 | uj | 例:的 |
c | 连词 | nrt | 人名 | ul | 例:了 |
d | 副词 | ns | 地名 | uv | 例:地 |
df | 例:不要 | nt | 机构团体 | uz | 例:着 |
dg | 副语素 | nz | 其他专名 | v | 动词 |
e | 叹词 | o | 拟声词 | vd | 副动词 |
f | 方位词 | p | 介词 | vg | 动语素 |
g | 语素 | q | 量词 | vi | 例:沉溺于 等同于 |
h | 前接成分 | r | 代词 | vn | 名动词 |
i | 成语 | rg | 例:兹 | vq | 例:去浄 去过 唸过 |
j | 简称略语 | rr | 人称代词 | x | 非语素字 |
k | 后接成分 | rz | 例:这位 | y | 语气词 |
l | 习用语 | s | 处所词 | z | 状态词 |
m | 数词 | t | 时间 | zg | 例:且 丗 丟 |
在利用 Jieba 分词时,调用的词库是它自带的一个 dict.txt 字典,使用的是默认分词器 jieba.dt,通常位于 Python 存放包文件的目录下 Lib\site-packages\jieba。
而默认的词库往往是不能满足我们的需求,分词效果也不太好,因此我们需要添加新词。开发者可以指定自己自定义的词典,以便包含 jieba 词库里没有的词,虽然 jieba 有新词识别能力,但是自行添加新词可以保证更高的正确率。
<1>. 自定义词典
第一步定义自己的词典文件,与 dict.txt 格式相同。每个单词占一行,且每行是由单词+词频+词性组成,用空格分割,顺序不能错乱,其中词频和词性是可省略的。
首先,参考 dict.txt 前几行内容,如下:
- AT&T 3 nz
- B超 3 n
- c# 3 nz
- C# 3 nz
- c++ 3 nz
- C++ 3 nz
- T恤 4 n
- A座 3 n
- ......
接着,找一段最近的新闻文本作为测试
- 9月30日,万里长征迈新步,雄关漫道不懈怠。面对错综复杂的国际环境、突飞猛进
- 的科技革命和产业变革,我们要以战略眼光、全局视野、系统思维,深刻认识到发展
- 大飞机事业的紧迫性和重要性。
最后,进行定义自己的词典 wxx_dt.txt,如下:
- 9月30日 300 t
- 战略眼光 10 nz
- 全局视野 10 nz
- 系统思维 10 nz
<2>. 使用自定义词典
通过 jieba.load_userdict(filename) 加载自定义词典,其中 filename 为词典的路径,测试如下:
- import jieba as jieba
-
- # 文本
- text = '9月30日,万里长征迈新步,雄关漫道不懈怠。面对错综复杂的国际环境、突飞猛进的科技革命和产业变革,我们要以战略眼光、全局视野、系统思维,深刻认识到发展大飞机事业的紧迫性和重要性。'
- # 自定义词典路径
- filename = 'D:\\XXX\\wxx.txt'
- # 加载自定义词典
- jieba.load_userdict(filename)
- # 使用自定义词典
- default_rs = jieba.lcut(text)
- for i in default_rs:
- print(i, end='/')
使用默认词典的效果:
- /9/月/30/日/,/万里长征/迈新步/,/雄关/漫道/不/懈怠/。/面对/错综复杂/的/国际/环境/、/突飞猛进/
- 的/科技/革命/和/产业/变革/,/我们/要/以/战略眼光/、/全局/视野/、/系统/思维/,/深刻/认识/到
- /发展/大/飞机/事业/的/紧迫性/和/重要性/。/
使用自定义词典的效果:
- 9月30日/,/万里长征/迈新步/,/雄关/漫道/不/懈怠/。/面对/错综复杂/的/国际/环境/、/突飞猛进/
- 的/科技/革命/和/产业/变革/,/我们/要/以/战略眼光/、/全局视野/、/系统思维/,/深刻/认识/到/
- 发展/大/飞机/事业/的/紧迫性/和/重要性/。/
<3>、调整词频词性
使用自定义词典,基本上可以将放在词典内词语进行组词,但可能会存在例外,比如曾经定义"国际环境"这个词,虽然调整了词频、词性仍然不能按照预想的结果输出/国际环境/,而输出的是/国际/环境/,此时可以通过 add_word() 调整分词,而/万里长征/ 我想拆开两个词语/万里/长征/,则可以通过 del_word() 调整分词。
词频也是影响词语的重要因素,如果不使用 add_word() 方式调整分词的话,那需要知道输出 /国际环境/ 的词频是怎样的?
我们可以使用 jieba.suggest_freq(segment, tune=True) 调节单个词语的词频,使其能(或不能)被分出来:
- # 显示词频
- freq = jieba.suggest_freq('国际环境', tune=True)
- print(f'当前词语词频为:{freq}') # 18
- # 使用自定义词典
- default_rs = jieba.lcut(text)
- for i in default_rs:
- print(i, end='/')
注意:自动计算的词频在启用 HMM 新词发现功能时可能会失效,为了避免这一偶发情况,可以关闭HMM功能(默认 HMM 是开启的):
default_rs = jieba.lcut(text, HMM=False)
词典会受到分词词语,词频,词性的影响,而通过自定义词典、调整分词词语、调整词频、调整词性等方式,用户可以增强分词词语的歧义纠错功能。
随着信息科学技术的快速发展及互联网的普及,网络文本资源呈几何级数不断增长。面对更新日益频繁和规模庞大的文本数据,如何高效准确地实现关键词提取成为影响信息检索系统性能的关键。提取关键词是文本分类、文本聚类、信息检索等技术的基础,在NLP领域应用广泛。
jieba 提供了两种算法支持关键词的提取:一是基于 TF-IDF 算法的关键词抽取,二是基于 TextRank 算法的关键词抽取。
TF-IDF : Term Frequency-Inverse DocumentFrequency的简称,意思是词频-逆文件频率,是一种信息检索的加权算法,用于评估一个词语对于一个文件集或一个语料库中的其中一份文件的重要程度。词语的重要程度与它在文件中出现的次数成正比,与它在语料库出现的频率成反比,简单的理解就是,一个词语在一篇文章中出现次数越多,同时在所有文档中出现次数越少,越能够代表该文章了。
TF词频,表达的是一个词语在文本中出现的频率。实际情况中,一些通用的词语对主题并没有太大作用,反倒是一些出现频率较少的词语才能够表达文章的主题。因此,单纯使用TF是不合适的,需要使用加权算法的思想,即一个词预测主题的能力越强,权重越大,否则,权重越小!
IDF逆文件频率,则表达的是基于权重的一个词语在语料库中出现的频率,即语料库中出现该词语越少,IDF越大。因此,TF与IDF组合才能真正反映提取的关键词重要性。
明白了原理之后,可以实操了,实操之前了解下源码, jieba 提供的 TF-IDF 算法位于 jieba.analyse 包,看下 __init__.py 源码:
- # TextRank算法与TF-IDF使用上相似
- ......
- # 通过构造器创建TFIDF对象、TextRank对象,其中TFIDF的idf_path=None
- default_tfidf = TFIDF()
- default_textrank = TextRank()
- # 通过TFIDF对象实例,访问extract_tags()
- extract_tags = tfidf = default_tfidf.extract_tags
- # 自定义设置idf文件语料库的路径
- set_idf_path = default_tfidf.set_idf_path
- # 通过TextRank对象实例,访问extract_tags()
- textrank = default_textrank.extract_tags
-
- # 自定义设置停用词语料库的路径
- def set_stop_words(stop_words_path):
- default_tfidf.set_stop_words(stop_words_path)
- default_textrank.set_stop_words(stop_words_path)
这里,有几个概念需要区分下:
1.1. extract_tags() 方法
- # sentence文本,withWeight权重,allowPOS词性
- extract_tags(sentence, topK=20, withWeight=False, allowPOS=(), withFlag=False)
参数说明,源码里注释已经很清晰了,如下:
Parameter:
- topK: return how many top keywords. `None` for all possible words.
- withWeight: if True, return a list of (word, weight);
if False, return a list of words.
- allowPOS: the allowed POS list eg. ['ns', 'n', 'vn', 'v','nr'].
if the POS of w is not in this list,it will be filtered.
- withFlag: only work with allowPOS is not empty.
if True, return a list of pair(word, weight) like posseg.cut if False, return a list of words
接下来,演示使用 TFIDF 算法提取关键字,测试代码如下:
- import jieba.analyse as analyse
-
- """ 使用TFIDF算法提取关键字 """
- # source_path为网上找的《2021年美国侵犯人权报告》.txt
- with open(source_path, mode='r', encoding='utf-8') as f:
- context = f.read()
- # 默认使用TFIDF算法,也可以使用TFIDF()
- tags = analyse.extract_tags(context, topK=10)
-
- # 显示前10
- tags1 = analyse.TFIDF().extract_tags(context, topK=10)
- print(f'tags1: {tags1}')
-
- # 显示前10、显示权重占比
- tags2 = analyse.TFIDF().extract_tags(context, topK=10, withWeight=True)
- print(f'tags2: {tags2}')
-
- # 显示前10、显示指定词性词语
- tags3 = analyse.TFIDF().extract_tags(context, topK=10, allowPOS=(['ns', 'n', 'vn', 'v', 'nr']))
- print(f'tags3: {tags3}')
1.2.自定义IDF文件语料库
jieba 官网提供了一份idf语料库可以下载使用,有两种方式可以设置:
- import jieba.analyse as analyse
-
- idf_path = 'D:\\XXX\\idf.txt.big'
-
- """ 使用TFIDF算法提取关键字,使用自定义idf语料库 """
- with open(source_path, mode='r', encoding='utf-8') as f:
- context = f.read()
-
- # 方式1:通过TFIDF构造器加入 idf_path
- tagsA = analyse.TFIDF(idf_path=idf_path).extract_tags(context)
-
- # 方式2:通过set_idf_path方法设置 idf_path
- idf = analyse.set_idf_path(idf_path)
- analyse.extract_tags(context)
1.3.自定义停用词语料库
这里,有几份不错的中文常用的停用词表,可以下载使用
- # 自定义停用词: 使用百度停用词表
- sw_path = 'D:\\XXX\\baidu_stopwords.txt'
- sw = analyse.set_stop_words(sw_path)
- analyse.extract_tags(context)
TextRank:是一种用于文本的基于图的排序算法。其基本思想来源于谷歌的PageRank算法,即通过把文本分割成若干组成单元(单词、句子)并建立图模型,利用投票机制对文本中的重要成分进行排序,仅利用单篇文档本身的信息即可实现关键词提取、文摘提取。和LDA、HMM等模型不同,TextRank不需要事先对多篇文档进行学习训练,因其简洁有效而得到广泛应用。
jieba.analyse.TextRank()的流程为:
共现关系,将文本进行分词,去除停用词或词性筛选等之后,设定窗口长度为K,即最多只能出现K个词,而后进行窗口滑动,在窗口中共同出现的词之间即可建立起无向边。
TextRank算法是利用局部词汇之间关系(共现窗口)对后续关键词进行排序,直接从文本本身抽取。关键词抽取的任务,就是从一段给定的文本中自动抽取出若干有意义的词语或词组。
1.1. textrank() 方法
textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'), withFlag=False)
参数说明,源码里注释也已经很清晰了,如下:
Parameter:
- topK: return how many top keywords. `None` for all possible words.
- withWeight: if True, return a list of (word, weight);
if False, return a list of words.
- allowPOS: the allowed POS list eg. ['ns', 'n', 'vn', 'v'].
if the POS of w is not in this list, it will be filtered.
- withFlag: if True, return a list of pair(word, weight) like posseg.cut if False, return a list of words
使用 TextRank 算法提取关键字,测试代码如下:
- import jieba.analyse as analyse
-
- """ 使用 TextRank 算法提取关键字 """
- with open(source_path, mode='r', encoding='utf-8') as f:
- context = f.read()
-
- rank1 = analyse.textrank(context, topK=10)
- print(f'rank1: {rank1}')
-
- rank2 = analyse.TextRank().extract_tags(context, topK=10)
- print(f'rank2: {rank2}')
-
- rank3 = analyse.default_textrank.extract_tags(context, topK=10, withWeight=True)
- print(f'rank3: {rank3}')
1.2. 自定义停用词语料库
- # 自定义停用词: 使用中文停用词表
- sw_path = 'D:\\XXX\\cn_stopwords.txt'
- sw = analyse.default_textrank.set_stop_words(sw_path)
- analyse.textrank(context)
TextRank 算法提取是依据词语之间的贡献关系来构建图,计算图中节点的 Rank;而 TF-IDF 算法跟 Jieba 分词一样,首先自己有一个默认词库,内含相应的词语与词频,更多的是依据 dict.txt 来计算词频与逆向词频权重。
上面实现了对文本文件的数据进行处理,并提取了关键词。如果把 topK 参数调节成50或者100个进行输出的话,此时借助词云则可以更直观的展示文本的内容。
首先,需要下载相关依赖:
pip install wordcloud
接着,介绍下使用词云的步骤:
请注意,根据需求调节对应的参数,词云对象 WordCloud 参数,如下:
- def __init__(self, font_path=None, width=400, height=200, margin=2,
- ranks_only=None, prefer_horizontal=.9, mask=None, scale=1,
- color_func=None, max_words=200, min_font_size=4,
- stopwords=None, random_state=None, background_color='black',
- max_font_size=None, font_step=1, mode="RGB",
- relative_scaling='auto', regexp=None, collocations=True,
- colormap=None, normalize_plurals=True, contour_width=0,
- contour_color='black', repeat=False,
- include_numbers=False, min_word_length=0, collocation_threshold=30):
- ......
接下来,以 TextRank 算法提取关键词为例,代码如下:
- import jieba.analyse as analyse
- import wordcloud as wc
- import matplotlib
- matplotlib.use('TkAgg')
- import matplotlib.pyplot as plt
-
- with open(source_path,
- mode='r',
- encoding='utf-8'
- ) as f:
- context = f.read()
- # 显示前100
- rank1 = analyse.textrank(context, topK=100)
- print(f'rank1: {rank1}')
- # 用空格分割
- wc_text = ' '.join(rank1)
- # 构造词云对象,并调节显示词云的参数
- wc_Obj = wc.WordCloud(
- font_path=r'.\simhei.ttf',
- background_color='white',
- width=1000,
- height=900,
- ).generate(wc_text)
- # 可视化显示
- plt.imshow(wc_Obj, interpolation='bilinear')
- plt.axis('off')
- plt.show()
当然,也可以使用带有图片背景的词云(百度找一张照片即可),我们通过调节词云对象的 color_func 、mask 和 scale 等参数即可,新增如下代码:
- import numpy as np
- import PIL.Image as image
-
- # 使用地图作为词云图片
- background_img = np.array(image.open('D:\\XXX\\demoMap.jpg'))
- color_func = wc.ImageColorGenerator(background_img)
- # 调节入参:图片随机颜色color_func,背景图片mask,清晰度scale
- wc_Obj = wc.WordCloud(
- ......
- color_func=color_func,
- mask=background_img,
- scale=18,
- ......
- ).generate(text_wc)
Python 中文分词组件 Jieba 的使用就演示到这里了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。