结巴中文分词原理分析4

2019 年 5 月 25 日 AINLP

作者:白宁超,工学硕士,现工作于四川省计算机研究院,著有《自然语言处理理论与实战》一书,点击阅读原文可直达原文链接,作者公众号:机器学习和自然语言处理(公众号ID:datathinks)




结巴分词基本用法1
1
 分词模式设置

安装结巴分词

  • 全自动安装:easy_install jieba 或者 pip install jieba / pip3 install jieba

  • 半自动安装:先下载 http://pypi.python.org/pypi/jieba/ ,解压后运行 python setup.py install

  • 手动安装:将 jieba 目录放置于当前目录或者 site-packages 目录

  • 通过 import jieba 来引用

本机是win10 64位,已经安装了pip工具,关于pip下载安装(here),然后win+R,输入pip install jieba,效果如下:


本机是win10 64位,已经安装了pip工具,关于pip下载安装(here),然后win+R,输入pip install jieba,效果如下:

结巴几种模式下的分词操作:(以下默认已导入:import jieba)

(1)全模式分词:


>>> import jieba

>>> str="我是白宁超来自博客园"

>>> seg_list=jieba.cut(str,cut_all=True)

>>> print("Full Mode: " "/ ".join(seg_list))  # 全模式

Full Mode: 我/ 是/ 白/ 宁/ 超/ 来自/ 博客/ 博客园


结果分析:显然我的名字:白宁超,没有正确分词,这是因为全模式把句子中所有可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义。

(2)精确模式分词


>>> seg_list=jieba.cut(str,cut_all=False)

>>> print("Default Mode: " "/ ".join(seg_list))  # 精确模式

Default Mode: 我/ 是/ 白宁超/ 来自/ 博客园

>>> seg_list=jieba.cut(str)

>>> print("Default Mode: " "/ ".join(seg_list))  # 默认模式

Default Mode: 我/ 是/ 白宁超/ 来自/ 博客园



结果分析:首先默认模式就是精确模式,即cut_all=False。这里很好的将“白宁超”划分为一个词。与全模式分词是有区别的。精确模式适合文本分析。

(3)默认精确模式分词


>>> seg_list = jieba.cut("他来到了网易杭研大厦")  # 默认是精确模式

>>> print("【新词发现】\t"+", ".join(seg_list))

【新词发现】  他, 来到, 了, 网易, 杭研, 大厦

结果分析:此处杭研并没有在词典中,但是也被Viterbi算法识别出来了。实际上是基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法可以发现新词。也可以在自定义字典去收集新词。

(4)搜索引擎模式分词


>>> seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式

>>> print("搜索引擎模式:\t"+", ".join(seg_list))

搜索引擎模式:小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, ,, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造


结果分析:在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。

(5)繁体分词


>>> str='''此開卷第一回也.作者自云:因曾歷過一番夢幻之后,故將真事隱去,

而借"通靈"之說,撰此《石頭記》一書也.故曰"甄士隱"云云.但書中所記

何事何人?自又云:“今風塵碌碌,一事無成,忽念及當日所有之女子,一

一細考較去,覺其行止見識,皆出于我之上.何我堂堂須眉,誠不若彼裙釵

哉?實愧則有余,悔又無益之大無可如何之日也!'''

>>> str=jieba.cut(str)

>>> print('/ '.join(str))

 

此開卷/ 第一回/ 也/ ./ 作者/ 自云/ :/ 因曾/ 歷過/ 一番/ 夢/ 幻之后/ ,/ 故將/ 真事/ 隱去/ ,/

/ 而/ 借/ "/ 通靈/ "/ 之/ 說/ ,/ 撰此/ 《/ 石頭記/ 》/ 一書/ 也/ ./ 故/ 曰/ "/ 甄士/ 隱/ "/ 云云/ ./ 但書中/ 所記/

/ 何事何/ 人/ ?/ 自又云/ :/ “/ 今風/ 塵碌碌/ ,/ 一事/ 無成/ ,/ 忽念及/ 當日/ 所/ 有/ 之/ 女子/ ,/ 一/

/ 一細/ 考較/ 去/ ,/ 覺其/ 行止/ 見識/ ,/ 皆/ 出于/ 我/ 之/ 上/ ./ 何/ 我堂/ 堂須/ 眉/ ,/ 誠不若/ 彼/ 裙釵/

/ 哉/ ?/ 實愧則/ 有/ 余/ ,/ 悔/ 又/ 無益/ 之/ 大/ 無/ 可/ 如何/ 之/ 日/ 也/ !

>>>


(6)jieba.lcut全模式、精准模式、搜索引擎模式


>>> seg_list=jieba.lcut(str,cut_all=True,HMM=True)<br>>>> type(seg_list)<br><class 'list'><br>>>> seg_list<br>['我''是''白''宁''超''来自''博客''博客园']<br>>>> print("Full Mode: " "/ ".join(seg_list))  # 全模式隐马<br>Full Mode: 我/ 是/ 白/ 宁/ 超/ 来自/ 博客/ 博客园<br>>>> type("/ ".join(seg_list))<br><class 'str'>


结果分析:显然调用jieba.lcut返回list类型,"/ ".join(seg_list)是将list转化为string类型。

(7)自定义分词器


#encoding=utf-8

from __future__ import print_function, unicode_literals

import sys

sys.path.append("../")

import jieba

jieba.load_userdict("userdict.txt")

import jieba.posseg as pseg

  

jieba.add_word('凱特琳')

jieba.del_word('自定义词')

  

test_sent = (

"李小福和李铁军是创新办主任也是云计算方面的专家; 什么是八一双鹿\n"

"例如我输入一个带“韩玉赏鉴”的标题,在自定义词库中也增加了此词为N类\n"

"「台中」正確應該不會被切開。mac上可分出「石墨烯」;此時又可以分出來凱特琳了。"

)

words = jieba.cut(test_sent)

print('/'.join(words))

 

print("="*40)

 

result = pseg.cut(test_sent)

 

for in result:

    print(w.word, "/", w.flag, ", ", end=' ')

 

print("\n" "="*40)

 

terms = jieba.cut('easy_install is great')

print('/'.join(terms))

terms = jieba.cut('python 的正则表达式是好用的')

print('/'.join(terms))

 

print("="*40)

# test frequency tune

testlist = [

('今天天气不错', ('今天''天气')),

('如果放到post中将出错。', ('中''将')),

('我们中出了一个叛徒', ('中''出')),

]

 

for sent, seg in testlist:

    print('/'.join(jieba.cut(sent, HMM=False)))

    word = ''.join(seg)

    print('%s Before: %s, After: %s' % (word, jieba.get_FREQ(word), jieba.suggest_freq(seg, True)))

    print('/'.join(jieba.cut(sent, HMM=False)))

    print("-"*40


结果分析:

首先对一段话分词处理:


test_sent = (

"李小福和李铁军是创新办主任也是云计算方面的专家; 什么是八一双鹿\n"

"例如我输入一个带“韩玉赏鉴”的标题,在自定义词库中也增加了此词为N类\n"

"「台中」正確應該不會被切開。mac上可分出「石墨烯」;此時又可以分出來凱特琳了。"

)

words = jieba.cut(test_sent)

print('/'.join(words))

 

李小福/和/李铁/军是/创新办/主任/也/是/云计算/方面/的/专家/;/ /什么/是/八一双鹿/

/例如/我/输入/一个/带/“/韩玉赏鉴/”/的/标题/,/在/自定义词/库中/也/增加/了/此/词为/N/类/

/「/台中/」/正確/應該/不會/被/切開/。/mac/上/可/分出/「/石墨/烯/」/;/此時/又/可以/分出/來/凱特琳/了/


 此处“李小福“和“李铁军”都是人名,结果却分词“李小福”和“李铁”,而“军是”当做一个词处理,显然不对。我们可以将“李铁军”当着一个词加入自定义文本中:


import sys

sys.path.append("../")

import jieba

jieba.load_userdict("userdict.txt")

 

jieba.add_word('李铁军')

 

test_sent = (

"李小福和李铁军是创新办主任也是云计算方面的专家; 什么是八一双鹿\n"

"例如我输入一个带“韩玉赏鉴”的标题,在自定义词库中也增加了此词为N类\n"

"「台中」正確應該不會被切開。mac上可分出「石墨烯」;此時又可以分出來凱特琳了。"

)

words = jieba.cut(test_sent)

print('/'.join(words))

  

李小福/和/李铁军/是/创新办/主任/也/是/云计算/方面/的/专家/;/ /什么/是/八一双鹿/

/例如/我/输入/一个/带/“/韩玉赏鉴/”/的/标题/,/在/自定义词/库中/也/增加/了/此/词为/N/类/

/「/台中/」/正確/應該/不會/被/切開/。/mac/上/可/分出/「/石墨/烯/」/;/此時/又/可以/分出/來/凱特琳/了/。

结果显然经过自定义分词有所好转。而石墨/烯分词错误。


李小福/和/李铁军/是/创新办/主任/也/是/云计算/方面/的/专家/;/ /什么/是/八一双鹿/
/例如/我/输入/一个/带/“/韩玉赏鉴/”/的/标题/,/在/自定义词/库中/也/增加/了/此/词为/N/类/
/「/台中/」/正確/應該/不會/被/切開/。/mac/上/可/分出/「/石墨烯/」/;/此時/又/可以/分出/來/凱特琳/了/。


(8)词性标注


print("="*40)

result = pseg.cut(test_sent)

for in result:

    print(w.word, "/", w.flag, ", ", end=' ')

print("\n" "="*40)

terms = jieba.cut('easy_install is great')

print('/'.join(terms))

terms = jieba.cut('python 的正则表达式是好用的')

print('/'.join(terms))

print("="*40) 

# 结果

========================================

李小福 / nr ,  和 / c ,  李铁军 / x ,  是 / v ,  创新办 / i ,  主任 / b ,  也 / d ,  是 / v ,  云计算 / x ,  方面 / n ,  的 / uj ,  专家 / n ,  ; / x ,    / x ,  什么 / r ,  是 / v ,  八一双鹿 / nz , 

 / x ,  例如 / v ,  我 / r ,  输入 / v ,  一个 / m ,  带 / v ,  “ / x ,  韩玉赏鉴 / nz ,  ” / x ,  的 / uj ,  标题 / n ,  , / x ,  在 / p ,  自定义词 / n ,  库中 / nrt ,  也 / d ,  增加 / v ,  了 / ul ,  此 / r ,  词 / n ,  为 / p ,  N / eng ,  类 / q , 

 / x ,  「 / x ,  台中 / s ,  」 / x ,  正確 / ad ,  應該 / v ,  不 / d ,  會 / v ,  被 / p ,  切開 / ad ,  。/ x ,  mac / eng ,  上 / f ,  可 / v ,  分出 / v ,  「 / x ,  石墨烯 / x ,  」 / x ,  ;/ x ,  此時 / c ,  又 / d ,  可以 / c ,  分出 / v ,  來 / zg ,  凱特琳 / x ,  了 / ul ,  。/ x ,

========================================

easy_install/ /is/ /great

python/ /的/正则表达式/是/好用/的

========================================

结果分析:李小福 / nr ,   李铁军 / x 都是名字,属于名词,而李铁军 / x显然词性不对,这是由于刚刚jieba.add_word('李铁军')时候,没有进行词性参数输入,我们看看jieba.add_word('李铁军')源码:


def add_word(self, word, freq=None, tag=None)
jieba.add_word('李铁军',tag='nr')修改后结果

运行结果:


========================================

李小福 / nr ,  和 / c ,  李铁军 / nr ,  是 / v ,  创新办 / i ,  主任 / b ,  也 / d ,  是 / v ,  云计算 / x ,  方面 / n ,  的 / uj ,  专家 / n ,  ; / x ,    / x ,  什么 / r ,  是 / v ,  八一双鹿 / nz , 

 / x ,  例如 / v ,  我 / r ,  输入 / v ,  一个 / m ,  带 / v ,  “ / x ,  韩玉赏鉴 / nz ,  ” / x ,  的 / uj ,  标题 / n ,  , / x ,  在 / p ,  自定义词 / n ,  库中 / nrt ,  也 / d ,  增加 / v ,  了 / ul ,  此 / r ,  词 / n ,  为 / p ,  N / eng ,  类 / q , 

 / x ,  「 / x ,  台中 / s ,  」 / x ,  正確 / ad ,  應該 / v ,  不 / d ,  會 / v ,  被 / p ,  切開 / ad ,  。/ x ,  mac / eng ,  上 / f ,  可 / v ,  分出 / v ,  「 / x ,  石墨烯 / x ,  」 / x ,  ;/ x ,  此時 / c ,  又 / d ,  可以 / c ,  分出 / v ,  來 / zg ,  凱特琳 / x ,  了 / ul ,  。/ x ,

========================================


(9) 自定义调整词典


# test frequency tune

testlist = [

('今天天气不错', ('今天''天气')),

('如果放到post中将出错。', ('中''将')),

('我们中出了一个叛徒', ('一''个')),

]

for sent, seg in testlist:

    print('/'.join(jieba.cut(sent, HMM=False)))

    word = ''.join(seg)

    print('%s Before: %s, After: %s' % (word, jieba.get_FREQ(word), jieba.suggest_freq(seg, True)))

    print('/'.join(jieba.cut(sent, HMM=False)))

    print("-"*40)

结果:

========================================

今天天气/不错

今天天气 Before: 3, After: 0

今天天气/不错

----------------------------------------

如果/放到/post/中将/出错/。

中将 Before: 763, After: 494

如果/放到/post/中/将/出错/。

----------------------------------------

我们/中/出/了/一个/叛徒

一个 Before: 142747, After: 454

我们/中/出/了/一/个/叛徒

----------------------------------------

结果分析:列表中的每一条数据如('今天天气不错', ('今天', '天气')),其中('今天', '天气')调整分词颗粒精度的。如第三句正常分词:我们/中/出/了/一个/叛徒。我们假设某些情况下一和个分别分词,可以做如上处理。

  • 使用 add_word(word, freq=None, tag=None) 和 del_word(word) 可在程序中动态修改词典。

  • 使用 suggest_freq(segment, tune=True) 可调节单个词语的词频,使其能(或不能)被分出来。

注意:自动计算的词频在使用 HMM 新词发现功能时可能无效。


(10) 自定义调节词典解决歧义分词问题


>>> import jieba

>>> print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False)))

Building prefix dict from the default dictionary ...

Loading model from cache C:\Users\cuitbnc\AppData\Local\Temp\jieba.cache

Loading model cost 1.069 seconds.

Prefix dict has been built succesfully.

如果/放到/post/中将/出错/。

>>> jieba.suggest_freq(('中''将'), True)

494

>>> print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False)))

如果/放到/post/中/将/出错/。

>>> print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False)))

「/台/中/」/正确/应该/不会/被/切开

>>> jieba.suggest_freq('台中', True)

69

>>> print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False)))

「/台中/」/正确/应该/不会/被/切开

>>


总结:jieba.cut 方法接受三个输入参数: 需要分词的字符串;cut_all 参数用来控制是否采用全模式;HMM 参数用来控制是否使用 HMM 模型。jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode)。


2
词性标注


jieba.posseg.POSTokenizer(tokenizer=None) 新建自定义分词器,tokenizer 参数可指定内部使用的jieba.Tokenizer 分词器。jieba.posseg.dt 为默认词性标注分词器。标注句子分词后每个词的词性,采用和 ictclas 兼容的标记法。

用法示例


>>> import jieba.posseg as pseg

>>> words = pseg.cut("我爱北京天安门")

>>> for word, flag in words:

...    print('%s %s' % (word, flag))

...

我 r

爱 v

北京 ns

天安门 ns


3
 并行分词


原理和用法

原理:将目标文本按行分隔后,把各行文本分配到多个 Python 进程并行分词,然后归并结果,从而获得分词速度的可观提升。基于 python 自带的 multiprocessing 模块,目前暂不支持 Windows。

用法:

jieba.enable_parallel(4) # 开启并行分词模式,参数为并行进程数

jieba.disable_parallel() # 关闭并行分词模式

例子:https://github.com/fxsjy/jieba/blob/master/test/parallel/test_file.py

import time

import sys

sys.path.append("../")

import jieba

jieba.initialize()

 

url = sys.argv[1]

content = open(url,"rb").read()

t1 = time.time()

words = "/ ".join(jieba.cut(content))

 

t2 = time.time()

tm_cost = t2-t1

 

log_f = open("1.log","wb")

log_f.write(words.encode('utf-8'))

log_f.close()

 

print('cost ' + str(tm_cost))

print('speed %s bytes/second' % (len(content)/tm_cost))

实验结果:在 4 核 3.4GHz Linux 机器上,对金庸全集进行精确分词,获得了 1MB/s 的速度,是单进程版的 3.3 倍。

注意:并行分词仅支持默认分词器 jieba.dt 和 jieba.posseg.dt。


4
参考文献
  1. 中文分词之HMM模型详解

  2. HMM相关文章

  3. 结巴分词GitHub源码


作者:白宁超,工学硕士,现工作于四川省计算机研究院,研究方向是自然语言处理和机器学习。曾参与国家自然基金项目和四川省科技支撑计划等多个省级项目。著有《自然语言处理理论与实战》一书。


作者博客官网:

https://bainingchao.github.io/


作者公众号,欢迎关注:


机器学习和自然语言处理
公众号ID:datathinks


登录查看更多
0

相关内容

“结巴”中文分词:做最好的 Python 中文分词组件
专知会员服务
42+阅读 · 2020年7月29日
【高能所】如何做好⼀份学术报告& 简单介绍LaTeX 的使用
【人大】图实现算法综述与评测分析
专知会员服务
37+阅读 · 2020年4月28日
【CVPR2020】图神经网络中的几何原理连接
专知会员服务
56+阅读 · 2020年4月8日
【新书】Python编程基础,669页pdf
专知会员服务
194+阅读 · 2019年10月10日
Jiagu:中文深度学习自然语言处理工具
AINLP
90+阅读 · 2019年2月20日
准确实用,7个优秀的开源中文分词库推荐
开源中国
5+阅读 · 2018年7月26日
NLP自然语言处理(二)——基础文本分析
乐享数据DataScientists
12+阅读 · 2017年2月7日
NLP自然语言处理(一)——jieba分词(R vs. python)
乐享数据DataScientists
6+阅读 · 2017年1月15日
Optimization for deep learning: theory and algorithms
Arxiv
104+阅读 · 2019年12月19日
Adversarial Metric Attack for Person Re-identification
Arxiv
14+阅读 · 2018年5月15日
Arxiv
3+阅读 · 2017年12月18日
VIP会员
相关VIP内容
专知会员服务
42+阅读 · 2020年7月29日
【高能所】如何做好⼀份学术报告& 简单介绍LaTeX 的使用
【人大】图实现算法综述与评测分析
专知会员服务
37+阅读 · 2020年4月28日
【CVPR2020】图神经网络中的几何原理连接
专知会员服务
56+阅读 · 2020年4月8日
【新书】Python编程基础,669页pdf
专知会员服务
194+阅读 · 2019年10月10日
相关资讯
Top
微信扫码咨询专知VIP会员