1. 程式人生 > >Lucene5學習之使用MMSeg4j分詞器

Lucene5學習之使用MMSeg4j分詞器

  MMSeg4j是一款中文分詞器,詳細介紹如下:

       1、mmseg4j 用 Chih-Hao Tsai 的 MMSeg 演算法(http://technology.chtsai.org/mmseg/ )實現的中文分詞器,並實現 lucene 的 analyzer 和 solr 的TokenizerFactory 以方便在Lucene和Solr中使用。

       2、MMSeg 演算法有兩種分詞方法:Simple和Complex,都是基於正向最大匹配。Complex 加了四個規則過慮。官方說:詞語的正確識別率達到了 98.41%。mmseg4j 已經實現了這兩種分詞演算法。

1.5版的分詞速度simple演算法是 1100kb/s左右、complex演算法是 700kb/s左右,(測試機:AMD athlon 64 2800+ 1G記憶體 xp)。

1.6版在complex基礎上實現了最多分詞(max-word)。“很好聽” -> "很好|好聽"; “中華人民共和國” -> "中華|華人|共和|國"; “中國人民銀行” -> "中國|人民|銀行"。

1.7-beta 版, 目前 complex 1200kb/s左右, simple 1900kb/s左右, 但記憶體開銷了50M左右. 上幾個版都是在10M左右

       可惜的是,MMSeg4j最新版1.9.1不支援Lucene5.0,於是我就修改了它的原始碼將它升級咯,使其支援Lucene5.x,至於我是怎樣修改,這裡就不一一說明的,我把我修改過的MMSeg4j最新原始碼上傳到了我的百度網盤,現分享給你們咯:

       下面是一個MMSeg4j分詞器簡單使用示例:

Java程式碼  收藏程式碼
  1. package com.chenlb.mmseg4j.analysis;  
  2. import java.io.IOException;  
  3. import org.apache.lucene.analysis.Analyzer;  
  4. import org.apache.lucene.analysis.TokenStream;  
  5. import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;  
  6. import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;  
  7. import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;  
  8. import org.apache.lucene.analysis.tokenattributes.TypeAttribute;  
  9. import org.junit.Assert;  
  10. import org.junit.Before;  
  11. import org.junit.Ignore;  
  12. import org.junit.Test;  
  13. /** 
  14.  * MMSegAnalyzer分詞器測試 
  15.  * @author Lanxiaowei 
  16.  * 
  17.  */  
  18. public class MMSegAnalyzerTest {  
  19.     String txt = "";  
  20.     @Before  
  21.     public void before() throws Exception {  
  22.         txt = "京華時報2009年1月23日報道 昨天,受一股來自中西伯利亞的強冷空氣影響,本市出現大風降溫天氣,白天最高氣溫只有零下7攝氏度,同時伴有6到7級的偏北風。";  
  23.         txt = "2009年ゥスぁま是中 ABcc國абвгαβγδ首次,我的ⅠⅡⅢ在chenёlbēū全國ㄦ範圍ㄚㄞㄢ內①ē②㈠㈩⒈⒑發行地方政府債券,";  
  24.         txt = "大S小3U盤浙BU盤T恤T臺A股牛B";  
  25.     }  
  26.     @Test  
  27.     //@Ignore  
  28.     public void testSimple() throws IOException {  
  29.         Analyzer analyzer = new SimpleAnalyzer();  
  30.         displayTokens(analyzer,txt);  
  31.     }  
  32.     @Test  
  33.     @Ignore  
  34.     public void testComplex() throws IOException {  
  35.         //txt = "1999年12345日報道了一條新聞,2000年中法國足球比賽";  
  36.         /*txt = "第一卷 雲天落日圓 第一節 偷歡不成倒大黴"; 
  37.         txt = "中國人民銀行"; 
  38.         txt = "我們"; 
  39.         txt = "工信處女幹事每月經過下屬科室都要親口交代24口交換機等技術性器件的安裝工作";*/  
  40.         //ComplexSeg.setShowChunk(true);  
  41.         Analyzer analyzer = new ComplexAnalyzer();  
  42.         displayTokens(analyzer,txt);  
  43.     }  
  44.     @Test  
  45.     @Ignore  
  46.     public void testMaxWord() throws IOException {  
  47.         //txt = "1999年12345日報道了一條新聞,2000年中法國足球比賽";  
  48.         //txt = "第一卷 雲天落日圓 第一節 偷歡不成倒大黴";  
  49.         //txt = "中國人民銀行";  
  50.         //txt = "下一個 為什麼";  
  51.         //txt = "我們家門前的大水溝很難過";  
  52.         //ComplexSeg.setShowChunk(true);  
  53.         Analyzer analyzer = new MaxWordAnalyzer();  
  54.         displayTokens(analyzer,txt);  
  55.     }  
  56.     /*@Test 
  57.     public void testCutLeeterDigitFilter() { 
  58.         String myTxt = "mb991ch cq40-519tx mmseg4j "; 
  59.         List<String> words = toWords(myTxt, new MMSegAnalyzer("") { 
  60.             @Override 
  61.             protected TokenStreamComponents createComponents(String text) { 
  62.                 Reader reader = new BufferedReader(new StringReader(text)); 
  63.                 Tokenizer t = new MMSegTokenizer(newSeg(), reader); 
  64.                 return new TokenStreamComponents(t, new CutLetterDigitFilter(t)); 
  65.             } 
  66.         }); 
  67.         //Assert.assertArrayEquals("CutLeeterDigitFilter fail", words.toArray(new String[words.size()]), "mb 991 ch cq 40 519 tx mmseg 4 j".split(" ")); 
  68.         for(String word : words) { 
  69.             System.out.println(word); 
  70.         } 
  71.     }*/  
  72.     public static void displayTokens(Analyzer analyzer,String text) throws IOException {  
  73.         TokenStream tokenStream = analyzer.tokenStream("text", text);  
  74.         displayTokens(tokenStream);  
  75.     }  
  76.     public static void displayTokens(TokenStream tokenStream) throws IOException {  
  77.         OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);  
  78.         PositionIncrementAttribute positionIncrementAttribute = tokenStream.addAttribute(PositionIncrementAttribute.class);  
  79.         CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);  
  80.         TypeAttribute typeAttribute = tokenStream.addAttribute(TypeAttribute.class);  
  81.         tokenStream.reset();  
  82.         int position = 0;  
  83.         while (tokenStream.incrementToken()) {  
  84.             int increment = positionIncrementAttribute.getPositionIncrement();  
  85.             if(increment > 0) {  
  86.                 position = position + increment;  
  87.                 System.out.print(position + ":");  
  88.             }  
  89.             int startOffset = offsetAttribute.startOffset();  
  90.             int endOffset = offsetAttribute.endOffset();  
  91.             String term = charTermAttribute.toString();  
  92.             System.out.println("[" + term + "]" + ":(" + startOffset + "-->" + endOffset + "):" + typeAttribute.type());  
  93.         }  
  94.     }  
  95.     /** 
  96.      * 斷言分詞結果 
  97.      * @param analyzer 
  98.      * @param text        源字串 
  99.      * @param expecteds   期望分詞後結果 
  100.      * @throws IOException  
  101.      */  
  102.     public static void assertAnalyzerTo(Analyzer analyzer,String text,String[] expecteds) throws IOException {  
  103.         TokenStream tokenStream = analyzer.tokenStream("text", text);  
  104.         CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);  
  105.         for(String expected : expecteds) {  
  106.             Assert.assertTrue(tokenStream.incrementToken());  
  107.             Assert.assertEquals(expected, charTermAttribute.toString());  
  108.         }  
  109.         Assert.assertFalse(tokenStream.incrementToken());  
  110.         tokenStream.close();  
  111.     }  
  112. }  

    mmseg4j分詞器有3個字典檔案,如圖:

       chars.dic是漢字字典檔案,裡面有12638個漢字

       units.dic裡是中文單位詞語,如小時,分鐘,米,釐米等等,具體自己開啟看看就明白了

       words.dic就是使用者自定義字典檔案,比如:麼麼噠,T恤,牛B等這些詞,放在這個字典檔案裡,分詞器就能把它當作一個詞

      我們在使用mmseg4j分詞器時,是這樣用的:

Java程式碼  收藏程式碼
  1. Analyzer analyzer = new SimpleAnalyzer();  

      檢視SimpleAnalyzer的建構函式,

Java程式碼  收藏程式碼
  1. public SimpleAnalyzer() {  
  2.     super();  
  3. }  

   呼叫的是父類MMSegAnalyzer的無參建構函式,接著檢視MMSegAnalyzer類的無參建構函式:

Java程式碼  收藏程式碼
  1. public MMSegAnalyzer() {  
  2.     dic = Dictionary.getInstance();  
  3. }  

    你會發現是通過Dictionary.getInstance()單例項模式去載入字典檔案的,接著檢視getInstance方法,

 這裡的程式碼註釋寫的很清楚,告訴了我們字典檔案的載入邏輯。

File path = getDefalutPath();用來獲取預設的字典檔案路徑,

然後根據字典檔案路徑呼叫getInstance(path)方法去載入字典檔案,接著檢視該方法,


 先從快取dics裡去字典檔案,如果快取裡沒有找到,則才會根據字典檔案路徑去載入,然後把載入到的字典檔案放入快取dics即dics.put(),

      接著看看Dictionary字典是如何初始化的,檢視Dictionary的建構函式原始碼:

      你會發現內部實際是通過呼叫init(path);方法進行字典初始化的,繼續查閱init方法,

      內部又是呼叫的reload方法載入的字典,繼續跟蹤至reload方法,

      內部通過loadDic去載入words和chars兩個字典檔案,通過loadUnit方法去載入units字典檔案,wordsLastTime是用來存放每個字典檔案的最後一次修改時間,引入這個map的目的是為了實現字典檔案重新載入,通過字典檔案的最後一次修改時間來判定檔案是否修改過,如果這個map裡不存在某字典檔案的最後一次修改時間,則表明該字典檔案是新加入的,需要重新載入至記憶體,這是loadDic方法的原始碼:

Java程式碼  收藏程式碼
  1. private Map<Character, CharNode> loadDic(File wordsPath) throws IOException {  
  2.         InputStream charsIn = null;  
  3.         File charsFile = new File(wordsPath, "chars.dic");  
  4.         if(charsFile.exists()) {  
  5.             charsIn = new FileInputStream(charsFile);  
  6.             addLastTime(charsFile); //chars.dic 也檢測是否變更  
  7.         } else {    //從 jar 里加載  
  8.             charsIn = this.getClass().getResourceAsStream("/data/chars.dic");  
  9.             charsFile = new File(this.getClass().getResource("/data/chars.dic").getFile()); //only for log  
  10.         }  
  11.         final Map<Character, CharNode> dic = new HashMap<Character, CharNode>();  
  12.         int lineNum = 0;  
  13.         long s = now();  
  14.         long ss = s;  
  15.         lineNum = load(charsIn, new FileLoading() { //單個字的  
  16.             public void row(String line, int n) {  
  17.                 if(line.length() < 1) {  
  18.                     return;  
  19.                 }  
  20.                 String[] w = line.split(" ");  
  21.                 CharNode cn = new CharNode();  
  22.                 switch(w.length) {  
  23.                 case 2:  
  24.                     try {  
  25.                         cn.setFreq((int)(Math.log(Integer.parseInt(w[1]))*100));//字頻計算出自由度  
  26.                     } catch(NumberFormatException e) {  
  27.                         //eat...  
  28.                     }  
  29.                 case 1:  
  30.                     dic.put(w[0].charAt(0), cn);  
  31.                 }  
  32.             }  
  33.         });  
  34.         log.info("chars loaded time="+(now()-s)+"ms, line="+lineNum+", on file="+charsFile);  
  35.         //try load words.dic in jar  
  36.         InputStream wordsDicIn = this.getClass().getResourceAsStream("/data/words.dic");  
  37.         if(wordsDicIn != null) {  
  38.             File wordsDic = new File(this.getClass().getResource("/data/words.dic").getFile());  
  39.             loadWord(wordsDicIn, dic, wordsDic);  
  40.         }  
  41.         File[] words = listWordsFiles();    //只要 wordsXXX.dic的檔案  
  42.         if(words != null) { //擴充套件詞庫目錄  
  43.             for(File wordsFile : words) {  
  44.                 loadWord(new FileInputStream(wordsFile), dic, wordsFile);  
  45.                 addLastTime(wordsFile); //用於檢測是否修改  
  46.             }  
  47.         }  
  48. 相關推薦

    Lucene5學習使用MMSeg4j

      MMSeg4j是一款中文分詞器,詳細介紹如下:        1、mmseg4j 用 Chih-Hao Tsai 的 MMSeg 演算法(http://technology.chtsai.org/mmseg/ )實現的中文分詞器,並實現 lucene 的 analy

    13.solr學習速成IK

    更新 api 一個 廣泛 針對 -i 處理器 多個 -1 IKAnalyzer簡介 IKAnalyzer是一個開源的,基於java語言開發的輕量級的中文分詞工具包。 IKAnalyzer特性 a. 算法采用“正向叠代最細粒度切分算法”,支持細粒度和最大詞

    ElasticsearchIK java api

    一、Elasticsearch分詞 在elasticsearch自帶的分詞器中,對中文分詞是支援的,只是所有的分詞都是按照單字進行分詞的,例如所帶的標準的分詞器standard分詞器,可以按照如下的方式查詢是如何進行分詞的 http://localhost:9200/iktest/_anal

    Elasticsearch筆記六中文及自定義

    中文分詞器 在lunix下執行下列命令,可以看到本來應該按照中文”北京大學”來查詢結果es將其分拆為”北”,”京”,”大”,”學”四個漢字,這顯然不符合我的預期。這是因為Es預設的是英文分詞器我需要為其配置中文分詞器。 curlHTTP://192.168.79.131:9

    Elasticsearch中文

    Elasticsearch的中文分詞器   1、單字分詞:     如:“我們是中國人”     效果:“我”“們”“是”“中”“國”“人”   2、二分法分詞:按兩個字進行切分。     如:“我們是中國人”,效果:“我們”、“們是”、“是中”、“中國”、“國人

    solr安裝mmseg4j

    1.下載資料    https://github.com/chenlb/mmseg4j-solr    注意版本: mmseg4j-solr-2.0.0.jar 要求 lucene/solr >= 4.3.0。在 lucene/solr [4.3.0, 4.7.1]

    MMSeg4j

    MMSeg4j用Chih-Hao Tsai 的MMSeg演算法實現的中文分詞器,並實現lucene的analyzer和solr的TokenizerFactory以方便在Lucene和Solr中使用。 MMSeg 演算法有兩種分詞方法:Simple和Comple

    自然語言處理中文詳解

    中文分詞是中文文字處理的一個基礎步驟,也是中文人機自然語言互動的基礎模組,不同於英文的是,中文句子中沒有詞的界限,因此在進行中文自然語言處理時,通常需要先進行分詞,分詞效果將直接影響詞性,句法樹等模組

    自然語言處理中文-jieba詳解及python實戰

    中文分詞是中文文字處理的一個基礎步驟,也是中文人機自然語言互動的基礎模組,在進行中文自然語言處理時,通常需要先進行分詞。本文詳細介紹現在非常流行的且開源的分詞器結巴jieba分詞器,並使用python實

    Elasticsearch中文外掛es-ik的自定義

    開發十年,就只剩下這套架構體系了! >>>   

    es學習(三):介紹以及中文ik的安裝與使用

    什麼是分詞 把文字轉換為一個個的單詞,分詞稱之為analysis。es預設只對英文語句做分詞,中文不支援,每個中文字都會被拆分為獨立的個體。 示例 POST http://192.168.247.8:9200/_analyze { "analyzer":"standar

    ElasticSearch學習筆記三十三 IK擴充套件字典及text全文型別資料聚合查詢

    ElasticSearch學習筆記之三十三 IK分詞器擴充套件字典及text全文型別資料分詞聚合查詢 專屬詞彙分詞失敗 擴充套件字典 檢視當前詞庫 自定義詞典 更新配置 再次檢視分詞 text全文型別資料分詞聚合

    如何開發自己的搜索帝國安裝ik

    style utf-8 編碼 ref 文本 需要 shell pack 用戶 you    Elasticsearch默認提供的分詞器,會把每個漢字分開,而不是我們想要的根據關鍵詞來分詞,我是中國人 不能簡單的分成一個個字,我們更希望 “中國人”,&

    Elasticsearch入門從零開始安裝ik

    gpo article terms n) rm -rf 從零開始 系列 pack 默認 起因 需要在ES中使用聚合進行統計分析,但是聚合字段值為中文,ES的默認分詞器對於中文支持非常不友好:會把完整的中文詞語拆分為一系列獨立的漢字進行聚合,顯然這並不是我的初衷。我們來看個

    Lucene.net(4.8.0) 學習問題記錄五: JIEba和Lucene的結合,以及對的思考

    += d+ ext eth reac chart rdl ret start 前言:目前自己在做使用Lucene.net和PanGu分詞實現全文檢索的工作,不過自己是把別人做好的項目進行遷移。因為項目整體要遷移到ASP.NET Core 2.0版本,而Lucene使用的版本

    dockeres+es-head+kibana+ik安裝

    data elastics work str search url 使用 數據 head 一、es 第一步:搜索docker search elasticsearch第二步:下載鏡像第三步:創建數據文件夾和配置文件宿主服務器創建文件夾mkdir -p /docker/es1

    Solr 7.5配置、資料庫連線、Java(學習篇(2)IK配置)

    2、配置IK分詞器 (本人已把需要用到的所有工具、檔案、jar包上傳至百度網盤,有需要者可下載使用, 連結:https://pan.baidu.com/s/1G_L-h0PN2GAaPcreKuuhlg 提取碼:qnwe ) 在ikanalyzer-solr6.5資料夾中找到核心jar包

    Es學習第五課, 介紹和中文配置

     上課我們介紹了倒排索引,在裡面提到了分詞的概念,分詞器就是用來分詞的。 分詞器是ES中專門處理分詞的元件,英文為Analyzer,定義為:從一串文字中切分出一個一個的詞條,並對每個詞條進行標準化。它由三部分組成, Character Filters:分詞之前進行預處

    jieba學習

    如有侵權,一定刪除。 結巴分詞分為三種模式:精確模式(預設)、全模式和搜尋引擎模式。 精確模式: import jieba s = '武漢大學是一所還不錯的大學' result = jieba.cut(s) print(','.join(result)) 輸出:

    elasticsearch

    1.概念 在elasticsearch中索引分析模組是可以通過註冊分詞器來進行配置的。分詞器的作用就是當一個文件被索引的時候,分詞器從文件中提取若干詞元(token)來支援索引的儲存和搜尋。elasticsearch內建了很多分詞器,分解器,和詞元過濾器. 索引分析模組包括: 分