layout: posttitle: PyHanLP工具包使用指南
subtitle: 安装和如何使用
date: 2019-11-13
author: NSX
header-img: img/post-bg-ios9-web.jpg
catalog: true
tags:
- 技术
- 教程
- Python

PyHanLP 使用指南

Hanlp简介

HanLP是一系列模型与算法组成的NLP工具包,由大快搜索主导并完全开源,目标是普及自然语言处理在生产环境中的应用。

HanLP主要功能包括分词、词性标注、关键词提取、自动摘要、依存句法分析、命名实体识别、短语提取、拼音转换、简繁转换等等。

Github地址:https://github.com/hankcs/HanLP

官网地址:http://hanlp.linrunsoft.com/

PyHanLP安装

  1. pip install pyhanlp
  1. from pyhanlp import *
  2. # 首次编译运行时,HanLP会自动构建词典缓存,请稍候……
  3. Using local c:\users\yuquanle\anaconda3\envs\nlpcoure\lib\site-packages\pyhanlp\static\data-for-1.7.0.zip, ignore http://hanlp.linrunsoft.com/release/data-for-1.7.0.zip
  4. Extracting data.zip...

如果下载速度只有几十K,可以尝试这个链接手动下载:http://download.hanlp.com/

可能会报错: ValueError: 配置错误: 数据包 …/Python36/lib/site-packages/pyhanlp/static\data 不存在,请修改配置文件中的root

解决方案:

  1. 建议提前准备好datajar与配置文件,并使用环境变量进行配置
    解压之后,放入pyhanlp安装目录的static目录中:
    1. pyhanlp
    2. ├── main.py
    3. └── static
    4. ├── data
    5. ├── data-for-1.7.5.zip
    6. ├── hanlp-1.7.5.jar
    7. ├── hanlp.properties
    8. └── hanlp-1.7.5-release.zip
  1. 保证 hanlp.properties 中的 root 是指向正确的data路径。 比如:
    1. export HANLP_JAR_PATH=/hanlp/hanlp-1.6.0.jar
    2. export HANLP_STATIC_ROOT=/hanlp
    3. tree $HANLP_STATIC_ROOT -L 2
    4. ll $HANLP_JAR_PATH
    5. cat $HANLP_STATIC_ROOT/hanlp.properties | grep root


PS: windows可忽略

使用指南

1.标准分词和词性标注

  1. from pyhanlp import *
  2. print(HanLP.segment('欢迎在Python中调用HanLP的API'))
  3. for term in HanLP.segment('欢迎在Python中调用HanLP的API'):
  4. print('{}\t{}'.format(term.word, term.nature)) # 获取单词与词性
  5. > [欢迎/v, 在/p, Python/nx, 中/f, 调用/v, HanLP/nx, 的/ude1, API/nx]

说明

  • HanLP中有一系列“开箱即用”的静态分词器,以Tokenizer结尾,在接下来的例子中会继续介绍。
  • HanLP.segment其实是对StandardTokenizer.segmen的包装。
  • 分词结果包含词性,每个词性的意思请查阅《HanLP词性标注集》

算法详解

2.依存句法分析

它将句子分析成一颗依存句法树,描述出各个词语之间的依存关系。也即指出了词语之间在句法上的搭配关系,这种搭配关系是和语义相关联的。

  1. s_dep = HanLP.parseDependency('欢迎在Python中调用HanLP的APIs')
  2. print(s_dep)
  3. 1 欢迎 欢迎 v v _ 0 核心关系 _ _
  4. 2 p p _ 5 状中结构 _ _
  5. 3 Python Python ws nx _ 4 定中关系 _ _
  6. 4 nd f _ 2 介宾关系 _ _
  7. 5 调用 调用 v v _ 8 定中关系 _ _
  8. 6 HanLP HanLP ws nx _ 5 动宾关系 _ _
  9. 7 u u _ 5 右附加关系 _ _
  10. 8 API API ws nx _ 1 动宾关系 _ _
  11. # 可以直接拿到数组,任意顺序或逆序遍历
  12. word_array = sentence.getWordArray()
  13. for word in word_array:
  14. print("%s --(%s)--> %s" % (word.LEMMA, word.DEPREL, word.HEAD.LEMMA))
  15. print()

算法详解

依存可视化工具

  1. HanLP在线句法分析并可视化:http://hanlp.hankcs.com/
  2. 南京大学开发的Dependency Viewer的Windows客户端工具
  1. web端推荐conllu.js,斯坦福大学也在用这个

英文依存标签含义解释

Tag 关系 Description Example
SBV 主谓关系 subject-verb 我送她一束花 (我 <– 送)
VOB 动宾关系 直接宾语,verb-object 我送她一束花 (送 –> 花)
IOB 间宾关系 间接宾语,indirect-object 我送她一束花 (送 –> 她)
FOB 前置宾语 前置宾语,fronting-object 他什么书都读 (书 <– 读)
DBL 兼语 double 他请我吃饭 (请 –> 我)
ATT 定中关系 attribute 红苹果 (红 <– 苹果)
ADV 状中结构 adverbial 非常美丽 (非常 <– 美丽)
CMP 动补结构 complement 做完了作业 (做 –> 完)
COO 并列关系 coordinate 大山和大海 (大山 –> 大海)
POB 介宾关系 preposition-object 在贸易区内 (在 –> 内)
LAD 左附加关系 left adjunct 大山和大海 (和 <– 大海)
RAD 右附加关系 right adjunct 孩子们 (孩子 –> 们)
IS 独立结构 independent structure 两个单句在结构上彼此独立
WP 标点符号 punctuation 标点符号
HED 核心关系 head 指整个句子的核心

3.关键词提取

  1. document = u'''
  2. 自然语言处理是计算机科学领域与人工智能领域中的一个重要方向。
  3. 它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。
  4. 自然语言处理是一门融语言学、计算机科学、数学于一体的科学。
  5. 因此,这一领域的研究将涉及自然语言,即人们日常使用的语言,
  6. 所以它与语言学的研究有着密切的联系,但又有重要的区别。
  7. 自然语言处理并不是一般地研究自然语言,
  8. 而在于研制能有效地实现自然语言通信的计算机系统,
  9. 特别是其中的软件系统。因而它是计算机科学的一部分。
  10. '''
  11. doc_keyword = HanLP.extractKeyword(document, 3)
  12. for word in doc_keyword:
  13. print(word)
  14. 研究
  15. 自然语言
  16. 自然语言处理

4.摘要抽取

  1. doc_keysentence = HanLP.extractSummary(document, 3)
  2. for key_sentence in doc_keysentence:
  3. print(key_sentence)
  4. 自然语言处理并不是一般地研究自然语言
  5. 自然语言处理是计算机科学领域与人工智能领域中的一个重要方向
  6. 它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法

5.1感知机词法分析器

  1. PerceptronLexicalAnalyzer = JClass('com.hankcs.hanlp.model.perceptron.PerceptronLexicalAnalyzer')
  2. analyzer = PerceptronLexicalAnalyzer()
  3. print(analyzer.analyze("上海华安工业(集团)公司董事长谭旭光和秘书胡花蕊来到美国纽约现代艺术博物馆参观"))
  4. > [上海/ns 华安/nz 工业/n (/w 集团/n )/w 公司/n]/nt 董事长/n 谭旭光/nr 和/c 秘书/n 胡花蕊/nr 来到/v [美国纽约/ns 现代/ntc 艺术/n 博物馆/n]/ns 参观/v

说明

  • NLP分词NLPTokenizer会执行词性标注和命名实体识别,由结构化感知机序列标注框架支撑。
  • 默认模型训练自9970万字的大型综合语料库,是已知范围内全世界最大的中文分词语料库。语料库规模决定实际效果,面向生产环境的语料库应当在千万字量级。用户可以在自己的语料上训练新模型以适应新领域、识别新的命名实体。

5.2 CRF 词法分析器

  1. CRFLexicalAnalyzer = JClass("com.hankcs.hanlp.model.crf.CRFLexicalAnalyzer")
  2. analyzer = CRFLexicalAnalyzer()
  3. print(analyzer.analyze("上海华安工业(集团)公司董事长谭旭光和秘书胡花蕊来到美国纽约现代艺术博物馆参观"))
  4. > [上海/ns 华安/nz 工业/n (/w 集团/n )/w 公司/n]/nt 董事长/n 谭旭光/nr 和/c 秘书/n 胡花蕊/nr 来到/v [美国纽约/ns 现代/ntc 艺术/n 博物馆/n]/ns 参观/v

说明

  • 调用更底层的API需要参考Java语法用JClass引入更深的类路径。以感知机词法分析器为例,CRF分词的类位于包名com.hankcs.hanlp.model.crf.CRFLexicalAnalyzer下,先用JClass得到类,然后就可以调用了
  • CRF对新词有很好的识别能力,但是开销较大。

算法详解

5.3 极速词典分词

  1. SpeedTokenizer = JClass("com.hankcs.hanlp.tokenizer.SpeedTokenizer")
  2. text = "江西鄱阳湖干枯,中国最大淡水湖变成大草原"
  3. JClass("com.hankcs.hanlp.HanLP$Config").ShowTermNature = False
  4. print(SpeedTokenizer.segment(text))

说明

  • 调用更底层的API需要参考Java语法用JClass引入更深的类路径。以感知机词法分析器为例,极速字典分词的类位于包名com.hankcs.hanlp.tokenizer.SpeedTokenizer下,先用JClass得到类,然后就可以调用了
  • 极速分词是词典最长分词,速度极其快,精度一般。
  • 在i7-6700K上跑出了4500万字每秒的速度。

算法详解

6.中国人名识别

  1. NER = HanLP.newSegment().enableNameRecognize(True)
  2. p_name = NER.seg('王国强、高峰、汪洋、张朝阳光着头、韩寒、小四')
  3. print(p_name)
  4. > [王国强/nr, 、/w, 高峰/n, 、/w, 汪洋/n, 、/w, 张朝阳/nr, 光着头/l, 、/w, 韩寒/nr, 、/w, 小/a, 四/

说明

  • 目前分词器基本上都默认开启了中国人名识别,比如HanLP.segment()接口中使用的分词器等等,用户不必手动开启;上面的代码只是为了强调。
  • 有一定的误命中率,比如误命中关键年,则可以通过在data/dictionary/person/nr.txt加入一条关键年 A 1来排除关键年作为人名的可能性,也可以将关键年作为新词登记到自定义词典中。
  • 建议NLP用户使用感知机或CRF词法分析器,精度更高。

算法详解

7.音译人名识别

  1. sentence = '微软的比尔盖茨、Facebook的扎克伯格跟桑德博格、亚马逊的贝索斯、苹果的库克,这些硅谷的科技人'
  2. person_ner = HanLP.newSegment().enableTranslatedNameRecognize(True)
  3. p_name = person_ner.seg(sentence)
  4. print(p_name)
  5. > [微软/ntc, 的/ude1, 比尔盖茨/nrf, 、/w, Facebook/nx, 的/ude1, 扎克伯格/nrf, 跟/p, 桑德博格/nrf, 、/w,

说明

  • 目前分词器基本上都默认开启了音译人名识别,用户不必手动开启

算法详解

8.短语提取

  1. phraseList = HanLP.extractPhrase(document, 3)
  2. print(phraseList)
  3. [计算机科学, 中的重要, 之间自然语言]

9.1 汉字转拼音

  1. s = '重载不是重任'
  2. pinyinList = HanLP.convertToPinyinList(s)
  3. for pinyin in pinyinList:
  4. print(pinyin.getPinyinWithoutTone(),pinyin.getTone(), pinyin, pinyin.getPinyinWithToneMark())
  5. chong 2 chong2 chóng
  6. zai 3 zai3 zǎi
  7. bu 2 bu2 bú
  8. shi 4 shi4 shì
  9. zhong 4 zhong4 zhòng
  10. ren 4 ren4 rèn

9.2 拼音转汉字

  1. def demo_pinyin_to_chinese():
  2. """ HanLP中的数据结构和接口是灵活的,组合这些接口,可以自己创造新功能
  3. [renmenrenweiyalujiangbujian/null, lvse/[滤色, 绿色]]
  4. """
  5. AhoCorasickDoubleArrayTrie = JClass(
  6. "com.hankcs.hanlp.collection.AhoCorasick.AhoCorasickDoubleArrayTrie")
  7. StringDictionary = JClass(
  8. "com.hankcs.hanlp.corpus.dictionary.StringDictionary")
  9. CommonAhoCorasickDoubleArrayTrieSegment = JClass(
  10. "com.hankcs.hanlp.seg.Other.CommonAhoCorasickDoubleArrayTrieSegment")
  11. CommonAhoCorasickSegmentUtil = JClass(
  12. "com.hankcs.hanlp.seg.Other.CommonAhoCorasickSegmentUtil")
  13. Config = JClass("com.hankcs.hanlp.HanLP$Config")
  14. TreeMap = JClass("java.util.TreeMap")
  15. TreeSet = JClass("java.util.TreeSet")
  16. dictionary = StringDictionary()
  17. dictionary.load(Config.PinyinDictionaryPath)
  18. entry = {}
  19. m_map = TreeMap()
  20. for entry in dictionary.entrySet():
  21. pinyins = entry.getValue().replace("[\\d,]", "")
  22. words = m_map.get(pinyins)
  23. if words is None:
  24. words = TreeSet()
  25. m_map.put(pinyins, words)
  26. words.add(entry.getKey())
  27. words = TreeSet()
  28. words.add("绿色")
  29. words.add("滤色")
  30. m_map.put("lvse", words)
  31. segment = CommonAhoCorasickDoubleArrayTrieSegment(m_map)
  32. print(segment.segment("renmenrenweiyalujiangbujianlvse"))
  33. if __name__ == "__main__":
  34. demo_pinyin_to_chinese()

10.繁简转换

  1. Jianti = HanLP.convertToSimplifiedChinese("我愛自然語言處理技術!")
  2. Fanti = HanLP.convertToTraditionalChinese("我爱自然语言处理技术!")
  3. print(Jianti)
  4. print(Fanti)
  5. 我爱自然语言处理技术!
  6. 我愛自然語言處理技術!

说明

  • HanLP能够识别简繁分歧词,比如打印机=印表機。
  • 支持香港繁体和台湾繁体

算法详解

11. 用户自定义词典

  1. print(HanLP.segment(text))
  2. CustomDictionary = JClass("com.hankcs.hanlp.dictionary.CustomDictionary")
  3. CustomDictionary.add("攻城狮") # 动态增加
  4. CustomDictionary.insert("白富美", "nz 1024") # 强行插入
  5. #CustomDictionary.remove("攻城狮"); # 删除词语(注释掉试试)
  6. CustomDictionary.add("单身狗", "nz 1024 n 1")
  7. #print(CustomDictionary.get("单身狗"))
  8. print(HanLP.segment(text))
  9. > [攻城/vi, 狮/ng, 逆袭/nz, 单身/n, 狗/n, ,/w, 迎娶/v, 白富美/nr, ,/w, 走上/v, 人生/n, 巅峰/n]
  10. > [攻城狮/nz, 逆袭/nz, 单身狗/nz, ,/w, 迎娶/v, 白富美/nz, ,/w, 走上/v, 人生/n, 巅峰/n]

说明

  • CustomDictionary是一份全局的用户自定义词典,可以随时增删,影响全部分词器。另外可以在任何分词器中关闭它。通过代码动态增删不会保存到词典文件。
  • 中文分词≠词典,词典无法解决中文分词,Segment提供高低优先级应对不同场景,请参考FAQ

追加词典

  • CustomDictionary主词典文本路径是data/dictionary/custom/CustomDictionary.txt,用户可以在此增加自己的词语(不推荐);也可以单独新建一个文本文件,通过配置文件CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; 我的词典.txt;来追加词典(推荐)。
  • 始终建议将相同词性的词语放到同一个词典文件里,便于维护和分享。

词典格式

  • 每一行代表一个单词,格式遵从[单词] [词性A] [A的频次] [词性B] [B的频次] … 如果不填词性则表示采用词典的默认词性。
  • 词典的默认词性默认是名词n,可以通过配置文件修改:全国地名大全.txt ns;如果词典路径后面空格紧接着词性,则该词典默认是该词性。

算法详解

12. 数词和数量词识别

  1. StandardTokenizer = JClass("com.hankcs.hanlp.tokenizer.StandardTokenizer")
  2. StandardTokenizer.SEGMENT.enableNumberQuantifierRecognize(True)
  3. print(StandardTokenizer.segment(sentence))
  4. [十九元/mq, 套餐/n, 包括/v, 什么/ry]
  5. [壹佰块/mq, 都/d, 不/d, 给/p, 我/rr]
  6. [9012345678只/mq, 蚂蚁/n]

13. 同义改写

  1. CoreSynonymDictionary = JClass("com.hankcs.hanlp.dictionary.CoreSynonymDictionary")
  2. text = "这个方法可以利用同义词词典将一段文本改写成意思相似的另一段文本,而且差不多符合语法"
  3. print(CoreSynonymDictionary.rewrite(text))
  4. > 其一措施可以使同义词词典将一律段文本改写成意思相似之任何一样段文本,而且大多符合语法

14. 情感分析

  1. NaiveBayesClassifier = JClass('com.hankcs.hanlp.classification.classifiers.NaiveBayesClassifier')
  2. classifier = NaiveBayesClassifier()
  3. # 创建分类器,更高级的功能请参考IClassifier的接口定义
  4. classifier.train(chn_senti_corp)
  5. # 训练后的模型支持持久化,下次就不必训练了
  6. predict(classifier, "前台客房服务态度非常好!早餐很丰富,房价很干净。再接再厉!")

15. 聚类

  1. ClusterAnalyzer = JClass('com.hankcs.hanlp.mining.cluster.ClusterAnalyzer')
  2. analyzer = ClusterAnalyzer()
  3. analyzer.addDocument("赵一", "流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 流行, 蓝调, 蓝调, 蓝调, 蓝调, 蓝调, 蓝调, 摇滚, 摇滚, 摇滚, 摇滚")
  4. analyzer.addDocument("钱二", "爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲")
  5. analyzer.addDocument("张三", "古典, 古典, 古典, 古典, 民谣, 民谣, 民谣, 民谣")
  6. analyzer.addDocument("李四", "爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 爵士, 金属, 金属, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲, 舞曲")
  7. analyzer.addDocument("王五", "流行, 流行, 流行, 流行, 摇滚, 摇滚, 摇滚, 嘻哈, 嘻哈, 嘻哈")
  8. analyzer.addDocument("马六", "古典, 古典, 古典, 古典, 古典, 古典, 古典, 古典, 摇滚")
  9. print(analyzer.kmeans(3))
  10. print(analyzer.repeatedBisection(3))
  11. print(analyzer.repeatedBisection(1.0)) # 自动判断聚类数量k

16. 语义距离

  1. CoreSynonymDictionary = JClass("com.hankcs.hanlp.dictionary.CoreSynonymDictionary")
  2. word_array = [
  3. "香蕉",
  4. "苹果",
  5. "白菜",
  6. "水果",
  7. "蔬菜",
  8. "自行车",
  9. "公交车",
  10. "飞机",
  11. "买",
  12. "卖",
  13. "购入",
  14. "新年",
  15. ]
  16. print("%-5s\t%-5s\t%-10s\t%-5s\n" % ("词A", "词B", "语义距离", "语义相似度"))
  17. for a in word_array:
  18. for b in word_array:
  19. print("%-5s\t%-5s\t%-15d\t%-5.10f" % (a, b, CoreSynonymDictionary.distance(a, b), CoreSynonymDictionary.similarity(a, b)))
  20. > 返回两两之间的语义距离和语义相似度

说明

17. 文本推荐(类似搜索引擎功能)

  1. Suggester = JClass("com.hankcs.hanlp.suggest.Suggester")
  2. suggester = Suggester()
  3. title_array = [
  4. "威廉王子发表演说 呼吁保护野生动物",
  5. "魅惑天后许佳慧不爱“预谋” 独唱《许某某》",
  6. "《时代》年度人物最终入围名单出炉 普京马云入选",
  7. "“黑格比”横扫菲:菲吸取“海燕”经验及早疏散",
  8. "日本保密法将正式生效 日媒指其损害国民知情权",
  9. "英报告说空气污染带来“公共健康危机”"
  10. ]
  11. for title in title_array:
  12. suggester.addSentence(title)
  13. print(suggester.suggest("陈述", 2)) # 语义
  14. print(suggester.suggest("危机公关", 1)) # 字符
  15. print(suggester.suggest("mayun", 1)) # 拼音
  16. print(suggester.suggest("徐家汇", 1)) # 拼音

参考