一、分詞工具
ansj、hanlp、jieba
二、優缺點
1.ansj
優點:
提供多種分詞方式
可直接根據內部詞庫分出人名、機構等信息
可構造多個詞庫,在分詞時可動態選擇所要使用的詞庫
缺點:
自定義詞典時,系統詞典還是被優先使用,導致詞性不是自定義詞典中的詞性
多單詞英文姓名無法分出
適用場景
若不使用自定義分詞,可直接使用ansj
2.hanlp
優點:
自定義分詞、詞性方便
可分出多單詞的英文名稱(詞典數據可帶空格)
可動態增刪詞庫,
動態添加詞典前五千萬速度很快,5m左右
缺點:
動態添加詞典前五千萬快的很,越往后越慢
詞典文件添加自定義詞典速度略慢,添加100w需要2m30s
適用場景:
詞典數量少於五千萬,詞典數據若包含空格,用hanlp比較合適
3.jieba
優點:
自定義分詞、詞性方便
詞典文件添加自定義詞典比hanlp快,詞典文件添加100w需要1m,八千萬 2h多
缺點:
自定義詞典時,帶空格的詞不支持
適用場景:
詞典數量大於五千萬
詞典數據不能包含空格,否則分不出
ansj的使用
1.maven引入ansj包
<ansj.version>5.0.4</ansj.version> <tree_split.version>1.5</tree_split.version> <nlp-lang.version>1.7.7</nlp-lang.version> <!-- ansj_seg --> <dependency> <groupId>org.ansj</groupId> <artifactId>ansj_seg</artifactId> <version>${ansj.version}</version> </dependency> <!-- tree_split --> <dependency> <groupId>org.ansj</groupId> <artifactId>tree_split</artifactId> <version>${tree_split.version}</version> </dependency> <!-- nlp-lang --> <dependency> <groupId>org.nlpcn</groupId> <artifactId>nlp-lang</artifactId> <version>${nlp-lang.version}</version> </dependency>
2.在項目根目錄下創建library文件夾,文件夾下包括以下幾個詞典文件(自行添加)
ambiguity.dic
default.dic
userLibrary.dic
3.使用
package com.zhen.segment; import java.io.InputStream; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.ansj.domain.Result; import org.ansj.domain.Term; import org.ansj.library.UserDefineLibrary; import org.ansj.splitWord.analysis.BaseAnalysis; import org.ansj.splitWord.analysis.NlpAnalysis; import org.ansj.splitWord.analysis.ToAnalysis; import org.nlpcn.commons.lang.tire.domain.Forest; import org.nlpcn.commons.lang.tire.domain.Value; import org.nlpcn.commons.lang.tire.library.Library; /** * @author FengZhen * @date Jan 30, 2019 * ansj分詞 */ public class SegmentTest { public static void main(String[] args) { // dynamicWord(); // localDic(); // moreUserDic(); } /** * 多用戶詞典(新增、刪除) */ public static void moreUserDic() { // 多用戶詞典 String str = "神探夏洛克這部電影作者.是一個dota迷"; System.out.println(ToAnalysis.parse(str)); // 兩個詞匯 神探夏洛克 douta迷 Forest dic1 = new Forest(); Library.insertWord(dic1, new Value("神探夏洛克", "define", "1000")); Forest dic2 = new Forest(); Library.insertWord(dic2, new Value("dota迷", "define", "1000")); System.out.println(ToAnalysis.parse(str, dic1, dic2)); System.out.println("-------刪除dic1中的詞"); Library.removeWord(dic1, "神探夏洛克"); System.out.println(ToAnalysis.parse(str, dic1, dic2)); } /** * 動態增刪詞庫 */ public static void dynamicWord(){ // 增加新詞,中間按照'\t'隔開 UserDefineLibrary.insertWord("ansj中文分詞", "userDefine", 1000); Result result = ToAnalysis.parse("我覺得Ansj中文分詞是一個不錯的系統!我是王婆!"); System.out.println("增加新詞例子:" + result); // 刪除詞語,只能刪除.用戶自定義的詞典. UserDefineLibrary.removeWord("ansj中文分詞"); result = ToAnalysis.parse("我覺得ansj中文分詞是一個不錯的系統!我是王婆!"); System.out.println("刪除用戶自定義詞典例子:" + result); //將用戶自定義詞典清空 UserDefineLibrary.clear(); } /** * 加載詞典文件 */ public static void localDic(){ try { //讀的是根目錄下的 Forest rootForest = Library.makeForest("library/userLibrary.dic"); System.out.println(rootForest.toMap()); //加載字典文件,取的是resource下的 InputStream inputStream = SegmentTest.class.getResourceAsStream("/library/userLibrary.dic"); Forest resoutceForest=Library.makeForest(inputStream); String str = "我覺得ansj中文分詞是一個不錯的系統!我是王婆!"; Result result=ToAnalysis.parse(str, resoutceForest);//傳入forest List<Term> termList=result.getTerms(); for(Term term:termList){ System.out.println(term.getName()+":"+term.getNatureStr()); } } catch (Exception e) { e.printStackTrace(); } } /** * 基本分詞 * 基本就是保證了最基本的分詞.詞語顆粒度最非常小的..所涉及到的詞大約是10萬左右. * 基本分詞速度非常快.在macAir上.能到每秒300w字每秒.同時准確率也很高.但是對於新詞他的功能十分有限 * @param content */ public static void baseAnay(String content) { Result result = BaseAnalysis.parse(delHTMLTag(content).replace("\n","").replace(" ", "").replace("\t","")); System.out.println("result:" + result); } /** * 精准分詞 * 它在易用性,穩定性.准確性.以及分詞效率上.都取得了一個不錯的平衡. * @param content */ public static void toAnay(String content){ Result result = ToAnalysis.parse(content); System.out.println("result:" + result); } /** * nlp分詞(單條新聞處理7秒) * 可以識別出未登錄詞.但是它也有它的缺點.速度比較慢.穩定性差.ps:我這里說的慢僅僅是和自己的其他方式比較.應該是40w字每秒的速度吧. * 個人覺得nlp的適用方式.1.語法實體名抽取.未登錄詞整理.只要是對文本進行發現分析等工作 * 會把企業分開 * @param content */ public static void nlpAnay(String content){ Result result = NlpAnalysis.parse(delHTMLTag(content).replace("\n","").replace(" ", "").replace("\t","")); System.out.println("result:" + result); List<Term> terms = result.getTerms(); for (Term term : terms) { String name = term.getName(); String nature = term.getNatureStr(); if (nature.equals("nt") || nature.equals("nr")) { System.out.println("------------------"); System.out.println("getName:" + term.getName()); System.out.println("getNatureStr:" + term.getNatureStr()); } } } /** * 篩除HTML標簽 * @param htmlStr * @return */ public static String delHTMLTag(String htmlStr){ String regEx_script="<script[^>]*?>[\\s\\S]*?<\\/script>"; //定義script的正則表達式 String regEx_style="<style[^>]*?>[\\s\\S]*?<\\/style>"; //定義style的正則表達式 String regEx_html="<[^>]+>"; //定義HTML標簽的正則表達式 Pattern p_script=Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE); Matcher m_script=p_script.matcher(htmlStr); htmlStr=m_script.replaceAll(""); //過濾script標簽 Pattern p_style=Pattern.compile(regEx_style,Pattern.CASE_INSENSITIVE); Matcher m_style=p_style.matcher(htmlStr); htmlStr=m_style.replaceAll(""); //過濾style標簽 Pattern p_html=Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE); Matcher m_html=p_html.matcher(htmlStr); htmlStr=m_html.replaceAll(""); //過濾html標簽 return htmlStr.trim(); //返回文本字符串 } }
hanlp的使用
1.maven添加依賴
<dependency> <groupId>com.hankcs</groupId> <artifactId>hanlp</artifactId> <version>portable-1.7.1</version> </dependency>
2.動態添加詞
/** * 新增詞 */ public static void addWord(String word, NatureEnum nature) { logger.info("==== addWord@word:{},nature:{},weight:{} ====", word, nature.getNature(), nature.getWeight()); if (!StringUtils.isBlank(word) && !word.equals("null")) { //大小括號問題 if (word.contains("(") || word.contains(")")) { CustomDictionary.insert(word.replace("(", "(").replace(")", ")"), nature.getNature()); }else if (word.contains("(") || word.contains(")")) { CustomDictionary.insert(word.replace("(", "(").replace(")", ")"), nature.getNature()); } CustomDictionary.insert(word, nature.getNature()); }else { logger.warn("==== addWord@ word({})為空 ====", word); } }
3.動態刪除詞
/** * 刪除詞 * @param forest * @param word */ public static void deleteWord(String word) { logger.info("==== deleteWord@word:{} ====", word); if (!StringUtils.isBlank(word)) { CustomDictionary.remove(word); }else { logger.warn("==== deleteWord@word為空({}) ====", word); } }
4.使用
/** * 分詞 * @param content * @param forests * ToAnalysis精准分詞 * BaseAnalysis基礎分詞 */ public static SegmentResult segment(String content) { logger.info("==== segment@content:{} ====", content); SegmentResult segmentResult = new SegmentResult(); List<Term> terms = HanLP.segment(content); Set<String> companySet = new HashSet<String>(); for (Term term : terms) { String name = term.word; String nature = term.nature.toString(); if (nature.equals(NatureEnum.Company.getNature())) { companySet.add(name); } } segmentResult.setCompanys(new ArrayList<String>(companySet)); logger.info("==== segment@分詞結果:{},提取結果:{} ====", terms.toString(), segmentResult.toString()); return segmentResult; }
5.自定義詞典文件
詞典文件格式如下,依次是 詞、詞性、權重
word nature weight
需要下載hanlp的data及hanlp.properties
https://github.com/hankcs/HanLP
data文件夾如下
hanlp.properties如下
#本配置文件中的路徑的根目錄,根目錄+其他路徑=完整路徑(支持相對路徑,請參考:https://github.com/hankcs/HanLP/pull/254) #Windows用戶請注意,路徑分隔符統一使用/ root=/Users/FengZhen/Desktop/accumulate/分詞/HanLP/ #好了,以上為唯一需要修改的部分,以下配置項按需反注釋編輯。 #核心詞典路徑 #CoreDictionaryPath=data/dictionary/CoreNatureDictionary.txt #2元語法詞典路徑 #BiGramDictionaryPath=data/dictionary/CoreNatureDictionary.ngram.txt #自定義詞典路徑,用;隔開多個自定義詞典,空格開頭表示在同一個目錄,使用“文件名 詞性”形式則表示這個詞典的詞性默認是該詞性。優先級遞減。 #所有詞典統一使用UTF-8編碼,每一行代表一個單詞,格式遵從[單詞] [詞性A] [A的頻次] [詞性B] [B的頻次] ... 如果不填詞性則表示采用詞典的默認詞性。 CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; Company.txt company; 現代漢語補充詞庫.txt; 全國地名大全.txt ns; 人名詞典.txt; 機構名詞典.txt; 上海地名.txt ns;data/dictionary/person/nrf.txt nrf; #停用詞詞典路徑 #CoreStopWordDictionaryPath=data/dictionary/stopwords.txt #同義詞詞典路徑 #CoreSynonymDictionaryDictionaryPath=data/dictionary/synonym/CoreSynonym.txt #人名詞典路徑 #PersonDictionaryPath=data/dictionary/person/nr.txt #人名詞典轉移矩陣路徑 #PersonDictionaryTrPath=data/dictionary/person/nr.tr.txt #繁簡詞典根目錄 #tcDictionaryRoot=data/dictionary/tc #HMM分詞模型 #HMMSegmentModelPath=data/model/segment/HMMSegmentModel.bin #分詞結果是否展示詞性 #ShowTermNature=true #IO適配器,實現com.hankcs.hanlp.corpus.io.IIOAdapter接口以在不同的平台(Hadoop、Redis等)上運行HanLP #默認的IO適配器如下,該適配器是基於普通文件系統的。 #IOAdapter=com.hankcs.hanlp.corpus.io.FileIOAdapter #感知機詞法分析器 #PerceptronCWSModelPath=data/model/perceptron/pku199801/cws.bin #PerceptronPOSModelPath=data/model/perceptron/pku199801/pos.bin #PerceptronNERModelPath=data/model/perceptron/pku199801/ner.bin #CRF詞法分析器 #CRFCWSModelPath=data/model/crf/pku199801/cws.txt #CRFPOSModelPath=data/model/crf/pku199801/pos.txt #CRFNERModelPath=data/model/crf/pku199801/ner.txt #更多配置項請參考 https://github.com/hankcs/HanLP/blob/master/src/main/java/com/hankcs/hanlp/HanLP.java#L59 自行添加
注意:
1.hanlp.properties中的root路徑為本地data文件夾的父級路徑
2.准備好的詞典文件可直接放入 data/dictionary/custom/下,然后再hanlp.properties中的 CustomDictionaryPath后添加該詞典文件名即可,如果不想在詞典文件中指定詞性,也可在CustomDictionaryPath指定名字的同時,在后邊空格指定詞性。如上Company.txt company
3.在調用分詞方法時,hanlp會去自動加載自定義添加的詞典。速度比較慢,100w需要2m30s
4.自定義詞典文件更新時,需要將data/dictionary/custom/CustomDictionary.txt.bin刪掉。
Jieba的使用
1.環境准備
需要Python環境,需要pip
2.安裝jieba
sudo pip install jieba
3.使用
# encoding=utf-8 import jieba seg_list = jieba.cut("我來到北京清華大學", cut_all=True) print("Full Mode: " + "/ ".join(seg_list)) # 全模式 seg_list = jieba.cut("我來到北京清華大學", cut_all=False) print("Default Mode: " + "/ ".join(seg_list)) # 精確模式 seg_list = jieba.cut("他來到了網易杭研大廈") # 默認是精確模式 print(", ".join(seg_list)) seg_list = jieba.cut_for_search("小明碩士畢業於中國科學院計算所,后在日本京都大學深造") # 搜索引擎模式 print(", ".join(seg_list))
4.自定義詞典
# encoding=utf-8 import jieba import jieba.posseg as pseg import time print("start-time" + time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))) file_name = "/home/jenkins/fz/segment/jieba/data/Company.txt" jieba.load_userdict(file_name) # file_name 為文件類對象或自定義詞典的路徑 print("end-time" + time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))) seg_list = jieba.cut("北京尚伯樂文化發展有限公司") print("custom dic: " + "/ ".join(seg_list)) seg_list = pseg.cut(content) #帶詞性 for seg in seg_list: print(seg)
詞典內容如下
鄭汴 1 company 燈塔市棧道村煤礦 1 company 深圳市萊斯達電信有限公司鐵嶺分公司 1 company T&T ENERTECHNO 1 company MMI Holdings Ltd 1 company :三十門市部 1 company 關嶺天利美容化妝品店 1 company 測試一下啦 1 company