支持中文分詞(N-最短路分詞、CRF分詞、索引分詞、用戶自定義詞典、詞性標註),命名實體識別(中國人名、音譯人名、日本人名、地名、實體機構名識別),關鍵詞提取,自動摘要,短語提取,拼音轉換,簡繁轉換,文本推薦,依存句法分析(MaxEnt依存句法分析、CRF依存句法分析)。提供Lucene插件,兼容Lucene4.x。
HanLP: Han Language Processing
漢語言處理包
HanLP是由一系列模型與算法組成的Java工具包,目標是促進自然語言處理在生產環境中的應用。HanLP具備功能完善、性能高效、架構清晰、語料時新、可自定義的特點。
HanLP提供下列功能:
中文分詞
最短路分詞
N-最短路分詞
CRF分詞
索引分詞
極速詞典分詞
用戶自定義詞典
詞性標註
命名實體識別
中國人名識別
音譯人名識別
日本人名識別
地名識別
實體機構名識別
關鍵詞提取
TextRank關鍵詞提取
自動摘要
TextRank自動摘要
短語提取
基於互信息和左右信息熵的短語提取
拼音轉換
多音字
聲母
韻母
聲調
簡繁轉換
繁體中文分詞
簡繁分歧詞
文本推薦
語義推薦
拼音推薦
字詞推薦
依存句法分析
基於神經網絡的高性能依存句法分析器
MaxEnt依存句法分析
CRF依存句法分析
語料庫工具
分詞語料預處理
詞頻詞性詞典制作
BiGram統計
詞共現統計
CoNLL語料預處理
CoNLL UA/LA/DA評測工具
在提供豐富功能的同時,HanLP內部模塊堅持低耦合、模型堅持惰性加載、服務堅持靜態提供、詞典堅持明文發佈,使用非常方便,同時自帶一些語料處理工具,幫助用戶訓練自己的語料。
項目地址
HanLP項目主頁:https://github.com/hankcs/HanLP
HanLP下載地址:https://github.com/hankcs/HanLP/releases
最新binary、文檔都以項目主頁爲準。博客是一個相對容易編輯的地方,國內訪問速度較快,所以用做文檔備份。
技術問題請在Github上發issue ,大家一起討論,也方便集中管理。博客留言、微博私信、郵件不受理任何HanLP相關的問題,謝謝合作!HanLP的GitHub主頁是官方指定唯一交流途徑,你可以找到最新的版本、最全的文檔、最多的社區支持、最豐富的插件。
反饋問題的時候請一定附上版本號、觸發代碼、輸入輸出,否則無法處理。
內存要求
內存120MB以上(-Xms120m -Xmx120m -Xmn64m),標準數據包(35萬核心詞庫+默認用戶詞典),分詞測試正常。
全部詞典和模型都是惰性加載的,如果你只用拼音轉換的話,則只加載拼音詞典,未加載的詞典相當於不存在,不佔內存。同理,模型也是如此。
更新日誌
最新版請移步GitHub!
舊版本一覽——
2015年7月12日 發佈了1.2.4版。在長時間的思考後,決定將用戶詞典用於分詞後的合併處理,使得用戶詞典中的長詞更容易切分出來。
2015年5月11日 發佈了1.2.2版。主要支持了並行化,優化了CRF分詞,新增了TnT分詞器,支持了數量詞識別。數據包data-for-1.2.2.zip也做了小幅調整。其實最新的更新日誌都可以在GitHub上看到,比博客詳細多了,更新也勤快多了。
2015年5月02日 發佈了1.1.5版。主要將ACDAT降級爲DAT,內存佔用減少了一半。所以需要刪緩存重新構建,或者下載data-for-1.1.5.zip。另外,還發布了內置數據包的Portable版,可以通過maven直接引入,零配置!
2015年4月28日 發佈了1.1.4版。這次訓練了一個新的CRF分詞模型,感覺效果要好很多(注:該模型與舊版本不兼容)。諸位可以評估一下,提出寶貴意見。
下載與配置
方式一、通過Maven的pom.xml
爲了方便用戶,特提供內置了數據包的Portable版,只需在pom.xml加入:
零配置,即可使用基本功能(除CRF分詞、依存句法分析外的全部功能)。連Maven都懶得用的話,可以直接下載portable版的jar。
如果用戶有自定義的需求,可以參考方式二,使用hanlp.properties進行配置。
目前Portable體積僅僅5.7MB,作爲代價,使用的是1998年的小詞典,對現代漢語的支持有限;所以還是建議外掛下面的數據包比較好。
方式二、下載jar、data、hanlp.properties
HanLP將數據與程序分離,給予用戶自定義的自由。
1、下載jar
2、下載data
數據包 | 功能 | 體積(MB) |
---|---|---|
data.zip | 全部詞典,全部模型 | 280(注:分詞詞典大約40MB,主要是句法分析模型佔體積,可以自行刪除。) |
在GitHub的release頁面Ctrl+F搜索data即可,下載後解壓到任意目錄,接下來通過配置文件告訴HanLP數據包的位置。
HanLP中的數據分爲詞典和模型,其中詞典是詞法分析必需的,模型是句法分析必需的。
用戶可以自行增刪替換,如果不需要句法分析功能的話,隨時可以刪除model文件夾。
3、配置文件
示例配置文件:hanlp.properties
配置文件的作用是告訴HanLP數據包的位置,只需修改第一行
爲data的父目錄即可,比如data目錄是/Users/hankcs/Documents/data
,那麼root=/Users/hankcs/Documents/
。
-
如果選用mini詞典的話,則需要修改配置文件:
CoreDictionaryPath=data/dictionary/CoreNatureDictionary.mini.txt
BiGramDictionaryPath=data/dictionary/CoreNatureDictionary.ngram.mini.txt
最後將HanLP.properties放入classpath即可,對於任何項目,都可以放到src目錄或resources目錄下,編譯時IDE會自動將其複製到classpath中。
如果放置不當,HanLP會智能提示當前環境下的合適路徑,並且嘗試從項目根目錄讀取數據集。
調用方法
HanLP幾乎所有的功能都可以通過工具類HanLP
快捷調用,當你想不起來調用方法時,只需鍵入HanLP.
,IDE應當會給出提示,並展示HanLP完善的文檔。
推薦用戶始終通過工具類HanLP
調用,這麼做的好處是,將來HanLP升級後,用戶無需修改調用代碼。
所有Demo都位於com.hankcs.demo下。
1. 第一個Demo
- System.out.println(HanLP.segment("你好,歡迎使用HanLP漢語處理包!"));
2. 標準分詞
- List<Term> termList = StandardTokenizer.segment("商品和服務");
- System.out.println(termList);
-
說明
-
HanLP中有一系列“開箱即用”的靜態分詞器,以
Tokenizer
結尾,在接下來的例子中會繼續介紹。 -
HanLP.segment
其實是對StandardTokenizer.segment
的包裝。 -
分詞結果包含詞性,每個詞性的意思請查閱《HanLP詞性標註集》。
-
-
算法詳解
3. NLP分詞
- List<Term> termList = NLPTokenizer.segment("中國科學院計算技術研究所的宗成慶教授正在教授自然語言處理課程");
- System.out.println(termList);
-
說明
-
NLP分詞
NLPTokenizer
會執行全部命名實體識別和詞性標註。
-
4. 索引分詞
- List<Term> termList = IndexTokenizer.segment("主副食品");
- for (Term term : termList)
- {
- System.out.println(term + " [" + term.offset + ":" + (term.offset + term.word.length()) + "]");
- }
-
說明
-
索引分詞
IndexTokenizer
是面向搜索引擎的分詞器,能夠對長詞全切分,另外通過term.offset
可以獲取單詞在文本中的偏移量。
-
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) + "\n最短路分詞:" + shortestSegment.seg(sentence));
- }
-
說明
-
N最短路分詞器
NShortSegment
比最短路分詞器慢,但是效果稍微好一些,對命名實體識別能力更強。 -
一般場景下最短路分詞的精度已經足夠,而且速度比N最短路分詞器快幾倍,請酌情選擇。
-
-
算法詳解
6. CRF分詞
- /**
- * CRF分詞(在最新訓練的未壓縮100MB模型下,能夠取得較好的效果,可以投入生產環境)
- *
- * @author hankcs
- */
- public class DemoCRFSegment
- {
- public static void main(String[] args)
- {
- HanLP.Config.ShowTermNature = false; // 關閉詞性顯示
- Segment segment = new CRFSegment();
- String[] sentenceArray = new String[]
- {
- "HanLP是由一系列模型與算法組成的Java工具包,目標是普及自然語言處理在生產環境中的應用。",
- "鐵桿部隊憤怒情緒集結 馬英九腹背受敵", // 繁體無壓力
- "馬英九回應連勝文“丐幫說”:稱黨內同志談話應謹慎",
- "高錳酸鉀,強氧化劑,紫紅色晶體,可溶於水,遇乙醇即被還原。常用作消毒劑、水淨化劑、氧化劑、漂白劑、毒氣吸收劑、二氧化碳精製劑等。", // 專業名詞有一定辨識能力
- "《夜晚的骰子》通過描述淺草的舞女在暗夜中扔骰子的情景,寄託了作者對庶民生活區的情感", // 非新聞語料
- "這個像是真的[委屈]前面那個打扮太江戶了,一點不上品...@hankcs", // 微博
- "鼎泰豐的小籠一點味道也沒有...每樣都淡淡的...淡淡的,哪有食堂2A的好次",
- "克里斯蒂娜·克羅爾說:不,我不是虎媽。我全家都熱愛音樂,我也鼓勵他們這麼做。",
- "今日APPS:Sago Mini Toolbox培養孩子動手能力",
- "財政部副部長王保安調任國家統計局黨組書記",
- "2.34米男子娶1.53米女粉絲 稱夫妻生活沒問題",
- "你看過穆赫蘭道嗎",
- "樂視超級手機能否承載賈布斯的生態夢"
- };
- for (String sentence : sentenceArray)
- {
- List<Term> termList = segment.seg(sentence);
- System.out.println(termList);
- }
- }
- }
-
說明
-
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上跑出了2000萬字每秒的速度。
-
-
算法詳解
8. 用戶自定義詞典
- 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 = "攻城獅逆襲單身狗,迎娶白富美,走上人生巔峯"; // 怎麼可能噗哈哈!
-
- // DoubleArrayTrie分詞
- 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\n", begin, end, new String(charArray, begin, end - begin), value);
- }
- });
- // 首字哈希之後二分的trie樹分詞
- BaseSearcher searcher = CustomDictionary.getSearcher(text);
- Map.Entry entry;
- while ((entry = searcher.next()) != null)
- {
- System.out.println(entry);
- }
-
- // 標準分詞
- System.out.println(HanLP.segment(text));
-
- // Note:動態增刪不會影響詞典文件
- // 目前CustomDictionary使用DAT儲存詞典文件中的詞語,用BinTrie儲存動態加入的詞語,前者性能高,後者性能低
- // 之所以保留動態增刪功能,一方面是歷史遺留特性,另一方面是調試用;未來可能會去掉動態增刪特性。
- }
-
說明
-
CustomDictionary
是一份全局的用戶自定義詞典,可以隨時增刪,影響全部分詞器。 -
另外可以在任何分詞器中關閉它。通過代碼動態增刪不會保存到詞典文件。
-
-
追加詞典
-
CustomDictionary
主詞典文本路徑是data/dictionary/custom/CustomDictionary.txt
,用戶可以在此增加自己的詞語(不推薦);也可以單獨新建一個文本文件,通過配置文件CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; 我的詞典.txt;
來追加詞典(推薦)。 -
始終建議將相同詞性的詞語放到同一個詞典文件裏,便於維護和分享。
-
-
詞典格式
-
每一行代表一個單詞,格式遵從
[單詞] [詞性A] [A的頻次] [詞性B] [B的頻次] ...
如果不填詞性則表示採用詞典的默認詞性。 -
詞典的默認詞性默認是名詞n,可以通過配置文件修改:
全國地名大全.txt ns;
如果詞典路徑後面空格緊接着詞性,則該詞典默認是該詞性。 -
關於用戶詞典的更多信息請參考詞典說明一章。
-
-
算法詳解
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
來排除關鍵年
作爲人名的可能性,也可以將關鍵年
作爲新詞登記到自定義詞典中。 -
如果你通過上述辦法解決了問題,歡迎向我提交pull request,詞典也是寶貴的財富。
-
-
算法詳解
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);
- }
-
說明
-
目前標準分詞器都默認關閉了地名識別,用戶需要手動開啓;這是因爲消耗性能,其實多數地名都收錄在核心詞典和用戶自定義詞典中。
-
在生產環境中,能靠詞典解決的問題就靠詞典解決,這是最高效穩定的方法。
-
-
算法詳解
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的目的不是演示動態識別,在生產環境中,能靠詞典解決的問題就靠詞典解決,這是最高效穩定的方法。
-
-
算法詳解
14. 關鍵詞提取
- String content = "程序員(英文Programmer)是從事程序開發、維護的專業人員。一般將程序員分爲程序設計人員和程序編碼人員,但兩者的界限並不非常清楚,特別是在中國。軟件從業人員分爲初級程序員、高級程序員、系統分析員和項目經理四大類。";
- List<String> keywordList = HanLP.extractKeyword(content, 5);
- System.out.println(keywordList);
-
說明
-
內部採用
TextRankKeyword
實現,用戶可以直接調用TextRankKeyword.getKeywordList(document, size)
-
-
算法詳解
15. 自動摘要
- String document = "算法可大致分爲基本算法、數據結構的算法、數論算法、計算幾何的算法、圖的算法、動態規劃以及數值分析、加密算法、排序算法、檢索算法、隨機化算法、並行算法、厄米變形模型、隨機森林算法。\n" +
- "算法可以寬泛的分爲三類,\n" +
- "一,有限的確定性算法,這類算法在有限的一段時間內終止。他們可能要花很長時間來執行指定的任務,但仍將在一定的時間內終止。這類算法得出的結果常取決於輸入值。\n" +
- "二,有限的非確定算法,這類算法在有限的時間內終止。然而,對於一個(或一些)給定的數值,算法的結果並不是唯一的或確定的。\n" +
- "三,無限的算法,是那些由於沒有定義終止定義條件,或定義的條件無法由輸入的數據滿足而不終止運行的算法。通常,無限算法的產生是由於未能確定的定義終止條件。";
- List<String> sentenceList = HanLP.extractSummary(document, 3);
- System.out.println(sentenceList);
-
說明
-
內部採用
TextRankSentence
實現,用戶可以直接調用TextRankSentence.getTopSentenceList(document, size)
。
-
-
算法詳解
16. 短語提取
- String text = "算法工程師\n" +
- "算法(Algorithm)是一系列解決問題的清晰指令,也就是說,能夠對一定規範的輸入,在有限時間內獲得所要求的輸出。" +
- "如果一個算法有缺陷,或不適合於某個問題,執行這個算法將不會解決這個問題。不同的算法可能用不同的時間、" +
- "空間或效率來完成同樣的任務。一個算法的優劣可以用空間複雜度與時間複雜度來衡量。算法工程師就是利用算法處理事物的人。\n" +
- "\n" +
- "1職位簡介\n" +
- "算法工程師是一個非常高端的職位;\n" +
- "專業要求:計算機、電子、通信、數學等相關專業;\n" +
- "學歷要求:本科及其以上的學歷,大多數是碩士學歷及其以上;\n" +
- "語言要求:英語要求是熟練,基本上能閱讀國外專業書刊;\n" +
- "必須掌握計算機相關知識,熟練使用仿真工具MATLAB等,必須會一門編程語言。\n" +
- "\n" +
- "2研究方向\n" +
- "視頻算法工程師、圖像處理算法工程師、音頻算法工程師 通信基帶算法工程師\n" +
- "\n" +
- "3目前國內外狀況\n" +
- "目前國內從事算法研究的工程師不少,但是高級算法工程師卻很少,是一個非常緊缺的專業工程師。" +
- "算法工程師根據研究領域來分主要有音頻/視頻算法處理、圖像技術方面的二維信息算法處理和通信物理層、" +
- "雷達信號處理、生物醫學信號處理等領域的一維信息算法處理。\n" +
- "在計算機音視頻和圖形圖像技術等二維信息算法處理方面目前比較先進的視頻處理算法:機器視覺成爲此類算法研究的核心;" +
- "另外還有2D轉3D算法(2D-to-3D conversion),去隔行算法(de-interlacing),運動估計運動補償算法" +
- "(Motion estimation/Motion Compensation),去噪算法(Noise Reduction),縮放算法(scaling)," +
- "銳化處理算法(Sharpness),超分辨率算法(Super Resolution),手勢識別(gesture recognition),人臉識別(face recognition)。\n" +
- "在通信物理層等一維信息領域目前常用的算法:無線領域的RRM、RTT,傳送領域的調製解調、信道均衡、信號檢測、網絡優化、信號分解等。\n" +
- "另外數據挖掘、互聯網搜索算法也成爲當今的熱門方向。\n" +
- "算法工程師逐漸往人工智能方向發展。";
- List<String> phraseList = HanLP.extractPhrase(text, 5);
- System.out.println(phraseList);
-
說明
-
內部採用
MutualInformationEntropyPhraseExtractor
實現,用戶可以直接調用MutualInformationEntropyPhraseExtractor..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();
- }
- }
-
說明
-
HanLP不僅支持基礎的漢字轉拼音,還支持聲母、韻母、音調、音標和輸入法首字母首聲母功能。
-
HanLP能夠識別多音字,也能給繁體中文注拼音。
-
最重要的是,HanLP採用的模式匹配升級到
AhoCorasickDoubleArrayTrie
,性能大幅提升,能夠提供毫秒級的響應速度!
-
-
算法詳解
18. 簡繁轉換
- /**
- * 簡繁轉換
- * @author hankcs
- */
- public class DemoTraditionalChinese2SimplifiedChinese
- {
- public static void main(String[] args)
- {
- System.out.println(HanLP.convertToTraditionalChinese("用筆記本電腦寫程序"));
- System.out.println(HanLP.convertToSimplifiedChinese("「以後等妳當上皇后,就能買士多啤梨慶祝了」"));
- }
- }
-
說明
-
HanLP能夠識別簡繁分歧詞,比如
打印機=印表機
。許多簡繁轉換工具不能區分“以後”“皇后”中的兩個“後”字,HanLP可以。
-
-
算法詳解
19. 文本推薦
- /**
- * 文本推薦(句子級別,從一系列句子中挑出與輸入句子最相似的那一個)
- * @author hankcs
- */
- public class DemoSuggester
- {
- public static void main(String[] args)
- {
- Suggester suggester = new Suggester();
- String[] titleArray =
- (
- "威廉王子發表演說 呼籲保護野生動物\n" +
- "《時代》年度人物最終入圍名單出爐 普京馬雲入選\n" +
- "“黑格比”橫掃菲:菲吸取“海燕”經驗及早疏散\n" +
- "日本保密法將正式生效 日媒指其損害國民知情權\n" +
- "英報告說空氣污染帶來“公共健康危機”"
- ).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 DemoWordDistance
- {
- public static void main(String[] args)
- {
- String[] wordArray = new String[]
- {
- "香蕉",
- "蘋果",
- "白菜",
- "水果",
- "蔬菜",
- "自行車",
- "公交車",
- "飛機",
- "買",
- "賣",
- "購入",
- "新年",
- "春節",
- "丟失",
- "補辦",
- "辦理",
- "送給",
- "尋找",
- "孩子",
- "教室",
- "教師",
- "會計",
- };
- for (String a : wordArray)
- {
- for (String b : wordArray)
- {
- System.out.println(a + "\t" + b + "\t之間的距離是\t" + CoreSynonymDictionary.distance(a, b));
- }
- }
- }
- }
-
說明
-
設想的應用場景是搜索引擎對詞義的理解,詞與詞並不只存在“同義詞”與“非同義詞”的關係,就算是同義詞,它們之間的意義也是有微妙的差別的。
-
-
算法
-
爲每個詞分配一個語義ID,詞與詞的距離通過語義ID的差得到。語義ID通過《同義詞詞林擴展版》計算而來。
-
21. 依存句法解析
- /**
- * 依存句法分析(神經網絡句法模型需要-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\n", 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\n", 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);
- }
- }
- }
-
說明
-
內部採用
NeuralNetworkDependencyParser
實現,用戶可以直接調用NeuralNetworkDependencyParser.compute(sentence)
-
也可以調用基於MaxEnt的依存句法分析器
MaxEntDependencyParser.compute(sentence)
-
-
算法詳解
詞典說明
本章詳細介紹HanLP中的詞典格式,滿足用戶自定義的需要。HanLP中有許多詞典,它們的格式都是相似的,形式都是文本文檔,隨時可以修改。
基本格式
詞典分爲詞頻詞性詞典和詞頻詞典。
-
詞頻詞性詞典
-
每一行代表一個單詞,格式遵從
[單詞] [詞性A] [A的頻次] [詞性B] [B的頻次] ...
。
-
-
詞頻詞典
-
每一行代表一個單詞,格式遵從
[單詞] [單詞的頻次]
。 -
每一行的分隔符爲空格符或製表符
-
少數詞典有自己的專用格式,比如同義詞詞典兼容《同義詞詞林擴展版》的文本格式,而轉移矩陣詞典則是一個csv表格。
下文主要介紹通用詞典,如不註明,詞典特指通用詞典。
數據結構
Trie樹(字典樹)是HanLP中使用最多的數據結構,爲此,我實現了通用的Trie樹,支持泛型、遍歷、儲存、載入。
用戶自定義詞典採用AhoCorasickDoubleArrayTrie和二分Trie樹儲存,其他詞典採用基於雙數組Trie樹(DoubleArrayTrie)實現的AC自動機AhoCorasickDoubleArrayTrie。
儲存形式
詞典有兩個形態:文本文件(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.txt
中發現了一個不是詞的詞,或者詞性標註得明顯不對,那麼你可以修改它,然後刪除緩存文件使其生效。
-
-
核心二元文法詞典
-
二元文法詞典
data/dictionary/CoreNatureDictionary.ngram.txt
儲存的是兩個詞的接續,如果你發現不可能存在這種接續時,刪掉即可。 -
你也可以添加你認爲合理的接續,但是這兩個詞必須同時在覈心詞典中才會生效。
-
-
命名實體識別詞典
-
基於角色標註的命名實體識別比較依賴詞典,所以詞典的質量大幅影響識別質量。
-
這些詞典的格式與原理都是類似的,請閱讀相應的文章或代碼修改它。
-
如果問題解決了,歡迎向我提交一個pull request,這是我在代碼庫中保留明文詞典的原因,衆人拾柴火焰高!
版權
Apache License Version 2.0
-
Apache License Version 2.0
-
HanLP產品初始知識產權歸上海林原信息科技有限公司所有,任何人和企業可以無償使用,可以對產品、源代碼進行任何形式的修改,可以打包在其他產品中進行銷售。
-
任何使用了HanLP的全部或部分功能、詞典、模型的項目、產品或文章等形式的成果必須顯式註明HanLP及此項目主頁。
鳴謝
感謝下列優秀開源項目:
感謝NLP界各位學者老師的著作:
-
《基於角色標註的中國人名自動識別研究》張華平 劉羣
-
《基於層疊隱馬爾可夫模型的中文命名實體識別》俞鴻魁 張華平 劉羣 呂學強 施水才
-
《基於角色標註的中文機構名識別》俞鴻魁 張華平 劉羣
-
《基於最大熵的依存句法分析》 辛霄 範士喜 王軒 王曉龍
-
An Efficient Implementation of Trie Structures, JUN-ICHI AOE AND KATSUSHI MORIMOTO
-
TextRank: Bringing Order into Texts, Rada Mihalcea and Paul Tarau
感謝上海林原信息科技有限公司的劉先生,允許我利用工作時間開發HanLP,提供服務器和域名,並且促成了開源。感謝諸位用戶的關注和使用,HanLP並不完善,未來還懇求各位NLP愛好者多多關照,提出寶貴意見