Posted ABC技术研习社
tags: 11c语言处理中文字符串 127个常用的JS代码片段,每段代码花30秒就能看懂(二) 1602c语言显示中文 @pathvariable 中文处理 .java中文乱码处理
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了推荐中文自然语言处理利器-HanLP相关的知识,希望对你有一定的参考价值。
HanLP是由一系列模型与算法组成的Java工具包,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点。
HanLP提供下列功能:
中文分词
索引全切分模式
用户自定义词典
兼容繁体中文
训练用户自己的领域模型
极速词典分词
感知机分词、CRF分词
最短路分词、N-最短路分词
HMM-Bigram(速度与精度最佳平衡;一百兆内存)
由字构词(侧重精度,可识别新词;适合NLP任务)
词典分词(侧重速度,每秒数千万字符;省内存)
所有分词器都支持:
词性标注
HMM词性标注(速度快)
感知机词性标注、CRF词性标注(精度高)
命名实体识别
感知机命名实体识别、CRF命名实体识别
中国人名识别、音译人名识别、日本人名识别、地名识别、实体机构名识别
基于HMM角色标注的命名实体识别 (速度快)
基于线性模型的命名实体识别(精度高)
关键词提取
TextRank关键词提取
自动摘要
TextRank自动摘要
短语提取
基于互信息和左右信息熵的短语提取
拼音转换
多音字、声母、韵母、声调
简繁转换
简繁分歧词(简体、繁体、臺灣正體、香港繁體)
文本推荐
语义推荐、拼音推荐、字词推荐
依存句法分析
基于神经网络的高性能依存句法分析器
MaxEnt依存句法分析
文本分类
情感分析
word2vec
词向量训练、加载、词语相似度计算、语义运算、查询、KMeans聚类
文档语义相似度计算
语料库工具
在提供丰富功能的同时,HanLP内部模块坚持低耦合、模型坚持惰性加载、服务坚持静态提供、词典坚持明文发布,使用非常方便,同时自带一些语料处理工具,帮助用户训练自己的模型。
项目主页
在线演示、Python调用、Solr及Lucene插件、国内下载、更多信息。
下载与配置
方式一、Maven
为了方便用户,特提供内置了数据包的Portable版,只需在pom.xml加入:
<dependency>
<groupId>com.hankcs</groupId>
<artifactId>hanlp</artifactId>
<version>portable-1.6.3</version>
</dependency>
零配置,即可使用基本功能(除由字构词、依存句法分析外的全部功能)。如果用户有自定义的需求,可以参考方式二,使用hanlp.properties进行配置。
方式二、下载jar、data、hanlp.properties
HanLP将数据与程序分离,给予用户自定义的自由。
1、下载:data.zip
下载后解压到任意目录,接下来通过配置文件告诉HanLP数据包的位置。
HanLP中的数据分为词典和模型,其中词典是词法分析必需的,模型是句法分析必需的。
data
│
├─dictionary
└─model
用户可以自行增删替换,如果不需要句法分析等功能的话,随时可以删除model文件夹。
模型跟词典没有绝对的区别,隐马模型被做成人人都可以编辑的词典形式,不代表它不是模型。
GitHub代码库中已经包含了data.zip中的词典,直接编译运行自动缓存即可;模型则需要额外下载。
2、下载jar和配置文件:hanlp-release.zip
配置文件的作用是告诉HanLP数据包的位置,只需修改第一行
root=D:/JavaProjects/HanLP/
/Users/hankcs/Documents/dataroot=/Users/hankcs/Documents/
如果选用mini词典的话,则需要修改配置文件:
CoreDictionaryPath=data/dictionary/CoreNatureDictionary.mini.txt
BiGramDictionaryPath=data/dictionary/CoreNatureDictionary.ngram.mini.txt
hanlp.properties
如果放置不当,HanLP会提示当前环境下的合适路径,并且尝试从项目根目录读取数据集。
调用方法
HanLPHanLP.
所有Demo都位于com.hankcs.demo下,比文档覆盖了更多细节,更新更及时,强烈建议运行一遍。
1. 第一个Demo
System.out.println(HanLP.segment("你好,欢迎使用HanLP汉语处理包!"));
内存要求
内存120MB以上(-Xms120m -Xmx120m -Xmn64m),标准数据包(35万核心词库+默认用户词典),分词测试正常。全部词典和模型都是惰性加载的,不使用的模型相当于不存在,可以自由删除。
HanLP对词典的数据结构进行了长期的优化,可以应对绝大多数场景。哪怕HanLP的词典上百兆也无需担心,因为在内存中被精心压缩过。如果内存非常有限,请使用小词典。HanLP默认使用大词典,同时提供小词典,请参考配置文件章节。
写给正在编译HanLP的开发者
如果你正在编译运行从Github检出的HanLP代码,并且没有下载data缓存,那么首次加载词典/模型会发生一个自动缓存的过程。
自动缓存的目的是为了加速词典载入速度,在下次载入时,缓存的词典文件会带来毫秒级的加载速度。由于词典体积很大,自动缓存会耗费一些时间,请耐心等待。
自动缓存缓存的不是明文词典,而是双数组Trie树、DAWG、AhoCorasickDoubleArrayTrie等数据结构。
2. 标准分词
List<Term> termList = StandardTokenizer.segment("商品和服务");System.out.println(termList);
TokenizerHanLP.segmentStandardTokenizer.segment
3. NLP分词
System.out.println(NLPTokenizer.segment("我新造一个词叫幻想乡你能识别并标注正确词性吗?"));// 注意观察下面两个“希望”的词性、两个“晚霞”的词性System.out.println(NLPTokenizer.analyze("我的希望是希望张晚霞的背影被晚霞映红").translateLabels());System.out.println(NLPTokenizer.analyze("支援臺灣正體香港繁體:微软公司於1975年由比爾·蓋茲和保羅·艾倫創立。"));
NLPTokenizer
4. 索引分词
List<Term> termList = IndexTokenizer.segment("主副食品");for (Term term : termList) { System.out.println(term + " [" + term.offset + ":" + (term.offset + term.word.length()) + "]"); }
IndexTokenizerterm.offsetSegmentenableIndexMode
5. N-最短路径分词
Segment nShortSegment = new NShortSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);Segment shortestSegment = new DijkstraSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);String[] testCase = new String[]{ "今天,刘志军案的关键人物,山西女商人丁书苗在市二中院出庭受审。", "刘喜杰石国祥会见吴亚琴先进事迹报告团成员", };for (String sentence : testCase) { System.out.println("N-最短分词:" + nShortSegment.seg(sentence) + " 最短路分词:" + shortestSegment.seg(sentence)); }
NShortSegment
6. CRF分词
Segment segment = new CRFSegment(); segment.enablePartOfSpeechTagging(true);List<Term> termList = segment.seg("你看过穆赫兰道吗");System.out.println(termList);for (Term term : termList) { if (term.nature == null) { System.out.println("识别到新词:" + term.word); } }
说明
CRF对新词有很好的识别能力,但是开销较大。
算法详解
《CRF分词的纯Java实现》
《CRF++模型格式说明》
7. 极速词典分词
/** * 演示极速分词,基于AhoCorasickDoubleArrayTrie实现的词典分词,适用于“高吞吐量”“精度一般”的场合 * @author hankcs */public class DemoHighSpeedSegment{ public static void main(String[] args) { String text = "江西鄱阳湖干枯,中国最大淡水湖变成大草原"; System.out.println(SpeedTokenizer.segment(text)); long start = System.currentTimeMillis(); int pressure = 1000000; for (int i = 0; i < pressure; ++i) { SpeedTokenizer.segment(text); } double costTime = (System.currentTimeMillis() - start) / (double)1000; System.out.printf("分词速度:%.2f字每秒", text.length() * pressure / costTime); } }
说明
极速分词是词典最长分词,速度极其快,精度一般。
在i7-6700K上跑出了4500万字每秒的速度。
算法详解
《Aho Corasick自动机结合DoubleArrayTrie极速多模式匹配》
8. 用户自定义词典
/** * 演示用户词典的动态增删 * * @author hankcs */public class DemoCustomDictionary{ public static void main(String[] args) { // 动态增加 CustomDictionary.add("攻城狮"); // 强行插入 CustomDictionary.insert("白富美", "nz 1024"); // 删除词语(注释掉试试)// CustomDictionary.remove("攻城狮"); System.out.println(CustomDictionary.add("单身狗", "nz 1024 n 1")); System.out.println(CustomDictionary.get("单身狗")); String text = "攻城狮逆袭单身狗,迎娶白富美,走上人生巅峰"; // 怎么可能噗哈哈! // AhoCorasickDoubleArrayTrie自动机扫描文本中出现的自定义词语 final char[] charArray = text.toCharArray(); CustomDictionary.parseText(charArray, new AhoCorasickDoubleArrayTrie.IHit<CoreDictionary.Attribute>() { @Override public void hit(int begin, int end, CoreDictionary.Attribute value) { System.out.printf("[%d:%d]=%s %s ", begin, end, new String(charArray, begin, end - begin), value); } }); // 自定义词典在所有分词器中都有效 System.out.println(HanLP.segment(text)); } }
CustomDictionaryCustomDictionarydata/dictionary/custom/CustomDictionary.txtCustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; 我的词典.txt;[单词] [词性A] [A的频次] [词性B] [B的频次] ...全国地名大全.txt ns;Segment#enableCustomDictionaryForcing
9. 中国人名识别
String[] testCase = new String[]{ "签约仪式前,秦光荣、李纪恒、仇和等一同会见了参加签约的企业家。", "王国强、高峰、汪洋、张朝阳光着头、韩寒、小四", "张浩和胡健康复员回家了", "王总和小丽结婚了", "编剧邵钧林和稽道青说", "这里有关天培的有关事迹", "龚学平等领导,邓颖超生前", };Segment segment = HanLP.newSegment().enableNameRecognize(true);for (String sentence : testCase) { List<Term> termList = segment.seg(sentence); System.out.println(termList); }
HanLP.segment()关键年data/dictionary/person/nr.txt关键年 A 1关键年关键年
10. 音译人名识别
String[] testCase = new String[]{ "一桶冰水当头倒下,微软的比尔盖茨、Facebook的扎克伯格跟桑德博格、亚马逊的贝索斯、苹果的库克全都不惜湿身入镜,这些硅谷的科技人,飞蛾扑火似地牺牲演出,其实全为了慈善。", "世界上最长的姓名是简森·乔伊·亚历山大·比基·卡利斯勒·达夫·埃利奥特·福克斯·伊维鲁莫·马尔尼·梅尔斯·帕特森·汤普森·华莱士·普雷斯顿。", };Segment segment = HanLP.newSegment().enableTranslatedNameRecognize(true);for (String sentence : testCase) { List<Term> termList = segment.seg(sentence); System.out.println(termList); }
说明
目前分词器基本上都默认开启了音译人名识别,用户不必手动开启;上面的代码只是为了强调。
算法详解
《层叠隐马模型下的音译人名和日本人名识别》
11. 日本人名识别
String[] testCase = new String[]{ "北川景子参演了林诣彬导演的《速度与激情3》", "林志玲亮相网友:确定不是波多野结衣?", };Segment segment = HanLP.newSegment().enableJapaneseNameRecognize(true);for (String sentence : testCase) { List<Term> termList = segment.seg(sentence); System.out.println(termList); }
说明
目前标准分词器默认关闭了日本人名识别,用户需要手动开启;这是因为日本人名的出现频率较低,但是又消耗性能。
算法详解
《层叠隐马模型下的音译人名和日本人名识别》
12. 地名识别
String[] testCase = new String[]{ "武胜县新学乡政府大楼门前锣鼓喧天", "蓝翔给宁夏固原市彭阳县红河镇黑牛沟村捐赠了挖掘机", };Segment segment = HanLP.newSegment().enablePlaceRecognize(true);for (String sentence : testCase) { List<Term> termList = segment.seg(sentence); System.out.println(termList); }
说明
目前标准分词器都默认关闭了地名识别,用户需要手动开启;这是因为消耗性能,其实多数地名都收录在核心词典和用户自定义词典中。
在生产环境中,能靠词典解决的问题就靠词典解决,这是最高效稳定的方法。
建议对命名实体识别要求较高的用户使用感知机词法分析器。
算法详解
《实战HMM-Viterbi角色标注地名识别》
13. 机构名识别
String[] testCase = new String[]{ "我在上海林原科技有限公司兼职工作,", "我经常在台川喜宴餐厅吃饭,", "偶尔去地中海影城看电影。", };Segment segment = HanLP.newSegment().enableOrganizationRecognize(true);for (String sentence : testCase) { List<Term> termList = segment.seg(sentence); System.out.println(termList); }
说明
目前分词器默认关闭了机构名识别,用户需要手动开启;这是因为消耗性能,其实常用机构名都收录在核心词典和用户自定义词典中。
HanLP的目的不是演示动态识别,在生产环境中,能靠词典解决的问题就靠词典解决,这是最高效稳定的方法。
建议对命名实体识别要求较高的用户使用感知机词法分析器。
算法详解
《层叠HMM-Viterbi角色标注模型下的机构名识别》
14. 关键词提取
String content = "程序员(英文Programmer)是从事程序开发、维护的专业人员。一般将程序员分为程序设计人员和程序编码人员,但两者的界限并不非常清楚,特别是在中国。软件从业人员分为初级程序员、高级程序员、系统分析员和项目经理四大类。";List<String> keywordList = HanLP.extractKeyword(content, 5);System.out.println(keywordList);
TextRankKeywordTextRankKeyword.getKeywordList(document, size)
15. 自动摘要
String document = "算法可大致分为基本算法、数据结构的算法、数论算法、计算几何的算法、图的算法、动态规划以及数值分析、加密算法、排序算法、检索算法、随机化算法、并行算法、厄米变形模型、随机森林算法。 " + "算法可以宽泛的分为三类, " + "一,有限的确定性算法,这类算法在有限的一段时间内终止。他们可能要花很长时间来执行指定的任务,但仍将在一定的时间内终止。这类算法得出的结果常取决于输入值。 " + "二,有限的非确定算法,这类算法在有限的时间内终止。然而,对于一个(或一些)给定的数值,算法的结果并不是唯一的或确定的。 " + "三,无限的算法,是那些由于没有定义终止定义条件,或定义的条件无法由输入的数据满足而不终止运行的算法。通常,无限算法的产生是由于未能确定的定义终止条件。";List<String> sentenceList = HanLP.extractSummary(document, 3);System.out.println(sentenceList);
TextRankSentenceTextRankSentence.getTopSentenceList(document, size)
16. 短语提取
String text = "算法工程师 " + "算法(Algorithm)是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。算法工程师就是利用算法处理事物的人。 " + " " + "1职位简介 " + "算法工程师是一个非常高端的职位; " + "专业要求:计算机、电子、通信、数学等相关专业; " + "学历要求:本科及其以上的学历,大多数是硕士学历及其以上; " + "语言要求:英语要求是熟练,基本上能阅读国外专业书刊; " + "必须掌握计算机相关知识,熟练使用仿真工具MATLAB等,必须会一门编程语言。 " + " " + "2研究方向 " + "视频算法工程师、图像处理算法工程师、音频算法工程师 通信基带算法工程师 " + " " + "3目前国内外状况 " + "目前国内从事算法研究的工程师不少,但是高级算法工程师却很少,是一个非常紧缺的专业工程师。算法工程师根据研究领域来分主要有音频/视频算法处理、图像技术方面的二维信息算法处理和通信物理层、雷达信号处理、生物医学信号处理等领域的一维信息算法处理。 " + "在计算机音视频和图形图像技术等二维信息算法处理方面目前比较先进的视频处理算法:机器视觉成为此类算法研究的核心;另外还有2D转3D算法(2D-to-3D conversion),去隔行算法(de-interlacing),运动估计运动补偿算法(Motion estimation/Motion Compensation),去噪算法(Noise Reduction),缩放算法(scaling),锐化处理算法(Sharpness),超分辨率算法(Super Resolution),手势识别(gesture recognition),人脸识别(face recognition)。 " + "在通信物理层等一维信息领域目前常用的算法:无线领域的RRM、RTT,传送领域的调制解调、信道均衡、信号检测、网络优化、信号分解等。 " + "另外数据挖掘、互联网搜索算法也成为当今的热门方向。 " + "算法工程师逐渐往人工智能方向发展。";List<String> phraseList = HanLP.extractPhrase(text, 10);System.out.println(phraseList);
MutualInformationEntropyPhraseExtractorMutualInformationEntropyPhraseExtractor.extractPhrase(text, size)
17. 拼音转换
/** * 汉字转拼音 * @author hankcs */public class DemoPinyin{ public static void main(String[] args) { String text = "重载不是重任"; List<Pinyin> pinyinList = HanLP.convertToPinyinList(text); System.out.print("原文,"); for (char c : text.toCharArray()) { System.out.printf("%c,", c); } System.out.println(); System.out.print("拼音(数字音调),"); for (Pinyin pinyin : pinyinList) { System.out.printf("%s,", pinyin); } System.out.println(); System.out.print("拼音(符号音调),"); for (Pinyin pinyin : pinyinList) { System.out.printf("%s,", pinyin.getPinyinWithToneMark()); } System.out.println(); System.out.print("拼音(无音调),"); for (Pinyin pinyin : pinyinList) { System.out.printf("%s,", pinyin.getPinyinWithoutTone()); } System.out.println(); System.out.print("声调,"); for (Pinyin pinyin : pinyinList) { System.out.printf("%s,", pinyin.getTone()); } System.out.println(); System.out.print("声母,"); for (Pinyin pinyin : pinyinList) { System.out.printf("%s,", pinyin.getShengmu()); } System.out.println(); System.out.print("韵母,"); for (Pinyin pinyin : pinyinList) { System.out.printf("%s,", pinyin.getYunmu()); } System.out.println(); System.out.print("输入法头,"); for (Pinyin pinyin : pinyinList) { System.out.printf("%s,", pinyin.getHead()); } System.out.println(); } }
AhoCorasickDoubleArrayTrie
18. 简繁转换
/** * 简繁转换 * @author hankcs */public class DemoTraditionalChinese2SimplifiedChinese{ public static void main(String[] args) { System.out.println(HanLP.convertToTraditionalChinese("用笔记本电脑写程序")); System.out.println(HanLP.convertToSimplifiedChinese("「以後等妳當上皇后,就能買士多啤梨慶祝了」")); } }
打印机=印表機
19. 文本推荐
/** * 文本推荐(句子级别,从一系列句子中挑出与输入句子最相似的那一个) * @author hankcs */public class DemoSuggester{ public static void main(String[] args) { Suggester suggester = new Suggester(); String[] titleArray = ( "威廉王子发表演说 呼吁保护野生动物 " + "《时代》年度人物最终入围名单出炉 普京马云入选 " + "“黑格比”横扫菲:菲吸取“海燕”经验及早疏散 " + "日本保密法将正式生效 日媒指其损害国民知情权 " + "英报告说空气污染带来“公共健康危机”" ).split("\n"); for (String title : titleArray) { suggester.addSentence(title); } System.out.println(suggester.suggest("发言", 1)); // 语义 System.out.println(suggester.suggest("危机公共", 1)); // 字符 System.out.println(suggester.suggest("mayun", 1)); // 拼音 } }
说明
在搜索引擎的输入框中,用户输入一个词,搜索引擎会联想出最合适的搜索词,HanLP实现了类似的功能。
可以动态调节每种识别器的权重
20. 语义距离
/** * 演示词向量的训练与应用 * * @author hankcs */public class DemoWord2Vec{ public static void main(String[] args) throws IOException { WordVectorModel wordVectorModel = trainOrLoadModel(); printNearest("中国", wordVectorModel); printNearest("美丽", wordVectorModel); printNearest("购买", wordVectorModel); // 文档向量 DocVectorModel docVectorModel = new DocVectorModel(wordVectorModel); String[] documents = new String[]{ "山东苹果丰收", "农民在江苏种水稻", "奥运会女排夺冠", "世界锦标赛胜出", "中国足球失败", }; System.out.println(docVectorModel.similarity(documents[0], documents[1])); System.out.println(docVectorModel.similarity(documents[0], documents[4])); for (int i = 0; i < documents.length; i++) { docVectorModel.addDocument(i, documents[i]); } printNearestDocument("体育", documents, docVectorModel); printNearestDocument("农业", documents, docVectorModel); printNearestDocument("我要看比赛", documents, docVectorModel); printNearestDocument("要不做饭吧", documents, docVectorModel); } }
说明
word2vec文档
《word2vec原理推导与代码分析》
21. 依存句法分析
/** * 依存句法分析(MaxEnt和神经网络句法模型需要-Xms1g -Xmx1g -Xmn512m) * @author hankcs */public class DemoDependencyParser{ public static void main(String[] args) { CoNLLSentence sentence = HanLP.parseDependency("徐先生还具体帮助他确定了把画雄鹰、松鼠和麻雀作为主攻目标。"); System.out.println(sentence); // 可以方便地遍历它 for (CoNLLWord word : sentence) { System.out.printf("%s --(%s)--> %s ", word.LEMMA, word.DEPREL, word.HEAD.LEMMA); } // 也可以直接拿到数组,任意顺序或逆序遍历 CoNLLWord[] wordArray = sentence.getWordArray(); for (int i = wordArray.length - 1; i >= 0; i--) { CoNLLWord word = wordArray[i]; System.out.printf("%s --(%s)--> %s ", word.LEMMA, word.DEPREL, word.HEAD.LEMMA); } // 还可以直接遍历子树,从某棵子树的某个节点一路遍历到虚根 CoNLLWord head = wordArray[12]; while ((head = head.HEAD) != null) { if (head == CoNLLWord.ROOT) System.out.println(head.LEMMA); else System.out.printf("%s --(%s)--> ", head.LEMMA, head.DEPREL); } } }
NeuralNetworkDependencyParserNeuralNetworkDependencyParser.compute(sentence)MaxEntDependencyParser.compute(sentence)
词典说明
本章详细介绍HanLP中的词典格式,满足用户自定义的需要。HanLP中有许多词典,它们的格式都是相似的,形式都是文本文档,随时可以修改。
基本格式
词典分为词频词性词典和词频词典。
CoreNatureDictionary.txt[单词] [词性A] [A的频次] [词性B] [B的频次] ....txt,.csvCoreNatureDictionary.ngram.txt[单词] [单词的频次]
少数词典有自己的专用格式,比如同义词词典兼容《同义词词林扩展版》的文本格式,而转移矩阵词典则是一个csv表格。
下文主要介绍通用词典,如不注明,词典特指通用词典。
数据结构
Trie树(字典树)是HanLP中使用最多的数据结构,为此,我实现了通用的Trie树,支持泛型、遍历、储存、载入。
用户自定义词典采用AhoCorasickDoubleArrayTrie和二分Trie树储存,其他词典采用基于双数组Trie树(DoubleArrayTrie)实现的AC自动机AhoCorasickDoubleArrayTrie。关于一些常用数据结构的性能评估,请参考wiki。
储存形式
词典有两个形态:文本文件(filename.txt)和缓存文件(filename.txt.bin或filename.txt.trie.dat和filename.txt.trie.value)。
文本文件
采用明文储存,UTF-8编码,CRLF换行符。
缓存文件
就是一些二进制文件,通常在文本文件的文件名后面加上.bin表示。有时候是.trie.dat和.trie.value。后者是历史遗留产物,分别代表trie树的数组和值。
如果你修改了任何词典,只有删除缓存才能生效。
修改方法
HanLP的核心词典训练自人民日报2014语料,语料不是完美的,总会存在一些错误。这些错误可能会导致分词出现奇怪的结果,这时请打开调试模式排查问题:
HanLP.Config.enableDebug();
data/dictionary/CoreNatureDictionary.txtCoreNatureDictionary.ngram.txtCoreNatureDictionary.txtdata/dictionary/CoreNatureDictionary.ngram.txt
如果问题解决了,欢迎向我提交一个pull request,这是我在代码库中保留明文词典的原因,众人拾柴火焰高!
以上是关于推荐中文自然语言处理利器-HanLP的主要内容,如果未能解决你的问题,请参考以下文章