diff --git "a/docs/nlp/2.\345\210\206\350\257\215.md" "b/docs/nlp/2.\345\210\206\350\257\215.md" new file mode 100644 index 000000000..f0af2ce45 --- /dev/null +++ "b/docs/nlp/2.\345\210\206\350\257\215.md" @@ -0,0 +1,571 @@ +# 自然语言处理 - 2.分词 + +**分词(Word Segmentation)**: 将连续的自然语言文本,切分成具有语义合理性和完整性的词汇序列 + +例句: 致毕业和尚未毕业的同学。 + +1. `致` `毕业` `和` `尚未` `毕业` `的` `同学` +2. `致` `毕业` `和尚` `未` `毕业` `的` `同学` + +> 今天我们聊聊 jieba 结巴分词器(牛逼) + +* 第一个不靠吹嘘学校or公司,完全靠实力开源的一个项目 +* 知乎上网友评论是腾讯,而最近GitHub上看到的是百度邮箱 +* “结巴”中文分词:做最好的 Python 中文分词组件(这个的确没吹牛!) +* GitHub上面 README.md 为中文版本(别看这个小事,很多中国公司开源项目都是英文) + +## 部署使用 + +> 安装 + +`pip install jieba` + +> 引用 + +`import jieba` + +> 特点 + +* 四种分词模式 + * 精确模式: 试图将句子最精确地切开,适合文本分析; + * 全模式: 把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义 + * 搜索引擎模式: 在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词 + * paddle模式: 利用PaddlePaddle深度学习框架,训练序列标注(双向GRU)网络模型实现分词,同时支持词性标注 + * paddle模式使用需安装paddlepaddle-tiny: `pip install paddlepaddle-tiny==1.6.1` + * 目前paddle模式支持jieba v0.40及以上版本。 + * jieba v0.40以下版本,请升级 jieba: `pip install jieba --upgrade` +* 支持繁体分词 +* 支持自定义词典 +* MIT 授权协议 + +## 四种分词模式 + +> 1.精确模式 + +精确模式: 试图将句子最精确地切开,适合文本分析 + +```python +# encoding=utf-8 +import jieba + +seg_list = jieba.cut("他来到了网易杭研大厦") # 默认是精确模式 +print(", ".join(seg_list)) + +seg_list = jieba.cut("我来到北京清华大学", cut_all=False) +print("Default Mode: " + "/ ".join(seg_list)) # 精确模式 + +# 输出结果 +#【精确模式】: 我/ 来到/ 北京/ 清华大学 +``` + +> 2.全模式 + +全模式: 把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义 + +```python +# encoding=utf-8 +import jieba + +seg_list = jieba.cut("我来到北京清华大学", cut_all=True) +print("Full Mode: " + "/ ".join(seg_list)) # 全模式 + + +# 输出结果 +#【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学 +``` + +> 3.搜索引擎模式 + +搜索引擎模式: 在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词 + +```python +# encoding=utf-8 +import jieba + +seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") # 搜索引擎模式 +print(", ".join(seg_list)) + +# 输出结果 +#【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造 +``` + +> 4.paddle 模式 + +paddle 模式: 利用PaddlePaddle深度学习框架,训练序列标注(双向GRU)网络模型实现分词,同时支持词性标注 + +* paddle模式使用需安装paddlepaddle-tiny: `pip install paddlepaddle-tiny==1.6.1` +* 目前paddle模式支持jieba v0.40及以上版本。 +* jieba v0.40以下版本,请升级 jieba: `pip install jieba --upgrade` + +```python +# encoding=utf-8 +import jieba + +jieba.enable_paddle() # 启动paddle模式。 0.40版之后开始支持,早期版本不支持 +strs=["我来到北京清华大学", "乒乓球拍卖完了", "中国科学技术大学"] +for str in strs: + seg_list = jieba.cut(str,use_paddle=True) # 使用paddle模式 + print("Paddle Mode: " + '/'.join(list(seg_list))) + +# 输出结果 +#【Paddle 模式】 我/来到/北京/清华大学 +#【Paddle 模式】 乒乓球/拍卖/完/了 +#【Paddle 模式】 中国/科学技术/大学 +``` + +## 添加自定义词典 + +> 延迟加载机制 + +* jieba 采用延迟加载,`import jieba` 和 `jieba.Tokenizer()` 不会立即触发词典的加载,一旦有必要才开始加载词典构建前缀字典。 +* 如果你想手工初始 jieba,也可以手动初始化。 + +```python +import jieba +jieba.initialize() # 手动初始化(可选) +``` + +在 0.28 之前的版本是不能指定主词典的路径的,有了延迟加载机制后,你可以改变主词典的路径: + +```python +jieba.set_dictionary('data/dict.txt.big') +``` + +案例 + +```python +# encoding=utf-8 +import sys +sys.path.append("../") +import jieba + +def cuttest(test_sent): + result = jieba.cut(test_sent) + print(" ".join(result)) + +def testcase(): + cuttest("这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱Python和C++。") + cuttest("我不喜欢日本和服。") + cuttest("雷猴回归人间。") + cuttest("工信处女干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作") + cuttest("我需要廉租房") + cuttest("永和服装饰品有限公司") + cuttest("我爱北京天安门") + cuttest("abc") + cuttest("隐马尔可夫") + cuttest("雷猴是个好网站") + +if __name__ == "__main__": + testcase() + """foobar.txt 格式 + 的 3188252 uj + 了 883634 ul + 是 796991 v + """ + jieba.set_dictionary("foobar.txt") + print("================================") + testcase() +``` + +> 切换其他词典 + +* 占用内存较小的词典文件: +* 支持繁体分词更好的词典文件: +* 下载你所需要的词典,然后覆盖 `jieba/dict.txt` 即可;或者用 `jieba.set_dictionary('data/dict.txt.big')` + +> 载入自定义词典 + +* 开发者可以指定自己自定义的词典,以便包含 jieba 词库里没有的词 + * 虽然 jieba 有新词识别能力,但是自行添加新词可以保证更高的正确率 +* 用法: `jieba.load_userdict(file_name)` # file_name 为文件类对象或自定义词典的路径 + * 词典格式和 dict.txt 一样,一个词占一行 + * 每一行分三部分:词语、词频(可省略)、词性(可省略),`用空格隔开`,`顺序不可颠倒` + * file_name 若为路径或二进制方式打开的文件,则文件必须为 UTF-8 编码 + * 词频省略时使用自动计算的能保证分出该词的词频 + +```python +""" filename 内容为: +创新办 3 i +云计算 5 +凱特琳 nz +台中 +""" +# encoding=utf-8 +import jieba + +seg_list = jieba.cut("我来到北京清华大学", cut_all=False) +print("Default Mode: " + "/ ".join(seg_list)) # 精确模式 + +jieba.load_userdict(file_name) + +seg_list = jieba.cut("我来到北京清华大学", cut_all=False) +print("Default Mode: " + "/ ".join(seg_list)) # 精确模式 + +# 加载自定义词库前: 李小福 / 是 / 创新 / 办 / 主任 / 也 / 是 / 云 / 计算 / 方面 / 的 / 专家 / +# 加载自定义词库后: 李小福 / 是 / 创新办 / 主任 / 也 / 是 / 云计算 / 方面 / 的 / 专家 / +``` + +## 调整词典的词 + +* 使用 `add_word(word, freq=None, tag=None)` 和 `del_word(word)` 可在程序中动态修改词典。 +* 使用 `suggest_freq(segment, tune=True)` 可调节单个词语的词频,使其能(或不能)被分出来。 + +**注意:自动计算的词频在使用 HMM 新词发现功能时可能无效** + +代码示例: + +```python +print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False))) +# 如果/放到/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))) +#「/台中/」/正确/应该/不会/被/切开 +``` + +## 关键词提取 + +> 基于 TF-IDF 算法的关键词抽取 + +```python +import jieba.analyse + +tags = jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=()) +""" +* sentence 为待提取的文本 +* topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20 +* withWeight 为是否一并返回关键词权重值,默认值为 False +* allowPOS 仅包括指定词性的词,默认值为空,即不筛选 +* jieba.analyse.TFIDF(idf_path=None) 新建 TFIDF 实例,idf_path 为 IDF 频率文件 +""" + +# 关键词提取所使用逆向文件频率(IDF)文本语料库可以切换成自定义语料库的路径 +jieba.analyse.set_idf_path(file_name) # file_name为自定义语料库的路径 + +# 关键词提取所使用停止词(Stop Words)文本语料库可以切换成自定义语料库的路径 +jieba.analyse.set_stop_words(file_name) # file_name为自定义语料库的路径 + + +# 测试案例 +# 下载地址: https://github.com/fxsjy/jieba/blob/master/extra_dict/stop_words.txt +""" +the +of +is +""" +jieba.analyse.set_stop_words("../extra_dict/stop_words.txt") +# 下载地址: https://raw.githubusercontent.com/fxsjy/jieba/master/extra_dict/idf.txt.big +"""格式如下: +劳动防护 13.900677652 +勞動防護 13.900677652 +奥萨贝尔 13.900677652 +""" +jieba.analyse.set_idf_path("../extra_dict/idf.txt.big") +s = "此外,公司拟对全资子公司吉林欧亚置业有限公司增资4.3亿元,增资后,吉林欧亚置业注册资本由7000万元增加到5亿元。吉林欧亚置业主要经营范围为房地产开发及百货零售等业务。目前在建吉林欧亚城市商业综合体项目。2013年,实现营业收入0万元,实现净利润-139.13万元。" +for word, weight in jieba.analyse.extract_tags(s, withWeight=True): + print('%s %s' % (word, weight)) +``` + +> 基于 TextRank 算法的关键词抽取 + +基本思想: + +1. 将待抽取关键词的文本进行分词 +2. 以固定窗口大小(默认为5,通过span属性调整),词之间的共现关系,构建图 +3. 计算图中节点的PageRank,注意是无向带权图 + + +```python +# encoding=utf-8 +import jieba + +# 直接使用,接口相同,注意默认过滤词性。 +jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v')) +# 新建自定义 TextRank 实例 +jieba.analyse.TextRank() + + +# 测试案例 +s = "此外,公司拟对全资子公司吉林欧亚置业有限公司增资4.3亿元,增资后,吉林欧亚置业注册资本由7000万元增加到5亿元。吉林欧亚置业主要经营范围为房地产开发及百货零售等业务。目前在建吉林欧亚城市商业综合体项目。2013年,实现营业收入0万元,实现净利润-139.13万元。" +for x, w in jieba.analyse.textrank(s, withWeight=True): + print('%s %s' % (x, w)) +``` + +## 词性标注 + +* `jieba.posseg.POSTokenizer(tokenizer=None)` 新建自定义分词器, tokenizer 参数可指定内部使用的 `jieba.Tokenizer` 分词器, `jieba.posseg.dt` 为默认词性标注分词器 +* 标注句子分词后每个词的词性,采用和 ictclas 兼容的标记法 +* 除了jieba默认分词模式,提供paddle模式下的词性标注功能。paddle模式采用延迟加载方式,通过 `enable_paddle()` 安装 `paddlepaddle-tiny`,并且import相关代码 + +paddle模式词性标注对应表如下: + +paddle模式词性和专名类别标签集合如下表,其中词性标签 24 个(小写字母),专名类别标签 4 个(大写字母) + +| 标签 | 含义 | 标签 | 含义 | 标签 | 含义 | 标签 | 含义 | +| ---- | -------- | ---- | -------- | ---- | -------- | ---- | -------- | +| n | 普通名词 | f | 方位名词 | s | 处所名词 | t | 时间 | +| nr | 人名 | ns | 地名 | nt | 机构名 | nw | 作品名 | +| nz | 其他专名 | v | 普通动词 | vd | 动副词 | vn | 名动词 | +| a | 形容词 | ad | 副形词 | an | 名形词 | d | 副词 | +| m | 数量词 | q | 量词 | r | 代词 | p | 介词 | +| c | 连词 | u | 助词 | xc | 其他虚词 | w | 标点符号 | +| PER | 人名 | LOC | 地名 | ORG | 机构名 | TIME | 时间 | + + +```python +# encoding=utf-8 +import jieba +import jieba.posseg as pseg + + +words = jieba.posseg.cut("我爱北京天安门") +for word, flag in words: + print('%s %s' % (word, flag)) + +""" +我 r +爱 v +北京 ns +天安门 ns +""" + +words = pseg.cut("我爱北京天安门") #jieba默认模式 +jieba.enable_paddle() #启动paddle模式。 0.40版之后开始支持,早期版本不支持 +words = pseg.cut("我爱北京天安门",use_paddle=True) #paddle模式 +for word, flag in words: + print('%s %s' % (word, flag)) + +""" +我 r +爱 v +北京 ns +天安门 ns +""" +``` + +## 并行分词 + +原理:将目标文本按行分隔后,把各行文本分配到多个 Python 进程并行分词,然后归并结果,从而获得分词速度的可观提升 + +基于 python 自带的 multiprocessing 模块,目前暂不支持 Windows + +用法: + +* `jieba.enable_parallel(4)` # 开启并行分词模式,参数为并行进程数 +* `jieba.disable_parallel()` # 关闭并行分词模式 + +```python +import sys +import time +import jieba + +jieba.enable_parallel() + +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')) + +print('speed %s bytes/second' % (len(content)/tm_cost)) +``` + +实验结果:在 4 核 3.4GHz Linux 机器上,对金庸全集进行精确分词,获得了 1MB/s 的速度,是单进程版的 3.3 倍。 + +注意:并行分词仅支持默认分词器 `jieba.dt` 和 `jieba.posseg.dt` + +## Tokenize:返回词语在原文的起止位置 + +注意,输入参数只接受 unicode + +> 默认模式 + +```python +# encoding=utf-8 +import jieba + + +result = jieba.tokenize(u'永和服装饰品有限公司') +for tk in result: + print("word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2])) + +""" +word 永和 start: 0 end:2 +word 服装 start: 2 end:4 +word 饰品 start: 4 end:6 +word 有限公司 start: 6 end:10 +""" +``` + + +```python +# encoding=utf-8 +import jieba + + +result = jieba.tokenize(u'永和服装饰品有限公司', mode='search') +for tk in result: + print("word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2])) + +""" +word 永和 start: 0 end:2 +word 服装 start: 2 end:4 +word 饰品 start: 4 end:6 +word 有限 start: 6 end:8 +word 公司 start: 8 end:10 +word 有限公司 start: 6 end:10 +""" +``` + +## ChineseAnalyzer for Whoosh 搜索引擎 + +引用: `from jieba.analyse import ChineseAnalyzer` + +* `pip install whoosh` +* Whoosh是一个用来索引文本并能根据索引搜索的的包含类和方法的类库。它允许你开发一个针对自己内容的搜索引擎。 +* 例如,如果你想创建一个博客软件,你可以使用Whoosh添加一个允许用户搜索博客类目的搜索功能。 + +```python +# -*- coding: UTF-8 -*- +from __future__ import unicode_literals +import sys,os +sys.path.append("../") +from whoosh.index import create_in,open_dir +from whoosh.fields import * +from whoosh.qparser import QueryParser + +from jieba.analyse.analyzer import ChineseAnalyzer + +analyzer = ChineseAnalyzer() + +schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT(stored=True, analyzer=analyzer)) +if not os.path.exists("tmp"): + os.mkdir("tmp") + +ix = create_in("tmp", schema) # for create new index +#ix = open_dir("tmp") # for read only +writer = ix.writer() + +writer.add_document( + title="document1", + path="/a", + content="This is the first document we’ve added!" +) + +writer.add_document( + title="document2", + path="/b", + content="The second one 你 中文测试中文 is even more interesting! 吃水果" +) + +writer.add_document( + title="document3", + path="/c", + content="买水果然后来世博园。" +) + +writer.add_document( + title="document4", + path="/c", + content="工信处女干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作" +) + +writer.add_document( + title="document4", + path="/c", + content="咱俩交换一下吧。" +) + +writer.commit() +searcher = ix.searcher() +parser = QueryParser("content", schema=ix.schema) + +for keyword in ("水果世博园","你","first","中文","交换机","交换"): + print("result of ", keyword) + q = parser.parse(keyword) + results = searcher.search(q) + for hit in results: + print(hit.highlights("content")) + print("="*10) + +for t in analyzer("我的好朋友是李明;我爱北京天安门;IBM和Microsoft; I have a dream. this is intetesting and interested me a lot"): + print(t.text) + + +""" +result of 水果世博园 +买水果然后来世博园 +========== +result of 你 +second one 中文测试中文 is even more interesting +========== +result of first +first document we’ve added +========== +result of 中文 +second one 你 中文测试中文 is even more interesting +========== +result of 交换机 +干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作 +========== +result of 交换 +咱俩交换一下吧 +干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作 +========== +我 +好 +朋友 +是 +李明 +我 +爱 +北京 +天安 +天安门 +ibm +microsoft +dream +intetest +interest +me +lot +""" +``` + +--- + +测试过: + +* jieba: [https://github.com/fxsjy/jieba](https://github.com/fxsjy/jieba) +* Hanlp: [https://github.com/hankcs/HanLP](https://github.com/hankcs/HanLP) +* nltk: [https://yiyibooks.cn/yiyi/nltk_python/index.html](https://yiyibooks.cn/yiyi/nltk_python/index.html) + * nltk 已经兼容 stanford corenlp 分词模版 + * CoreNLP: [https://github.com/Lynten/stanford-corenlp](https://github.com/Lynten/stanford-corenlp) +* pyltp: [http://www.ltp-cloud.com/document](http://www.ltp-cloud.com/document) +* pkuseg: [https://github.com/lancopku/pkuseg-pytho](https://github.com/lancopku/pkuseg-python) + +呆测试: + +* NLPIR: [http://ictclas.nlpir.org](http://ictclas.nlpir.org) +* 新浪云: [http://www.sinacloud.com/doc/sae/python/segment.html](http://www.sinacloud.com/doc/sae/python/segment.html) +* 盘古分词: [http://pangusegment.codeplex.com/](http://pangusegment.codeplex.com/) +* 搜狗分词: [http://www.sogou.com/labs/webservice/](http://www.sogou.com/labs/webservice/) +* 庖丁解牛: [https://code.google.com/p/paoding/](https://code.google.com/p/paoding/) +* BosonNLP: [http://bosonnlp.com/dev/center](http://bosonnlp.com/dev/center) +* IKAnalyzer: [http://www.oschina.net/p/ikanalyzer](http://www.oschina.net/p/ikanalyzer) +* SCWS中文分词: [http://www.xunsearch.com/scws/docs.php](http://www.xunsearch.com/scws/docs.php)