1. 程式人生 > >elasticsearch教程--中文分詞器作用和使用

elasticsearch教程--中文分詞器作用和使用

目錄

  • 概述

  • 環境準備

  • 認識中文分詞器

  • 常用的中文分詞器
  • IK Analyzer
  • hanlp中文分詞器
  • 彩蛋

概述    

    上一篇博文記錄了elasticsearch外掛安裝和管理, 在地大物博的祖國使用es,不得不考慮中文分詞器,es內建的分詞器對中文分詞的支援用慘不忍睹來形容不為過,看這篇博文之前,建議先看一下博文elasticsearch分詞器,對分詞器有個初步理解。本文將記錄一下專案中如何使用選用和使用中文分詞器的,希望能夠幫助到即將來踩坑的小夥伴們,歡迎批評指正

本文都是基於elasticsearch安裝教程 中的elasticsearch安裝目錄(/opt/environment/elasticsearch-6.4.0)為範例

環境準備

  1. 全新最小化安裝的centos 7.5
  2. elasticsearch 6.4.0

認識中文分詞器

    在博文elasticsearch分詞器中提到elasticsearch能夠快速的通過搜尋詞檢索出對應的文章歸功於倒排索引,下面通過中文舉例看看倒排索引。

中文分詞器作用以及效果

中文分詞器是做什麼的呢? what? 通過名字就知道了啊,為什麼還要問。。。下面通過三個文件示例,看看它是如何分詞的

文件1: 我愛偉大的祖國

文件2: 祝福祖國強大繁盛

文件3: 我愛藍天白雲

經過中文分詞器,以上文件均會根據分詞規則,將文件進行分詞後的結果如下:

注意:不同的分詞規則,分詞結果不一樣,選擇根據分詞器提供的分詞規則找到適合的分詞規則

文件1分詞結果: [我,愛,偉大,的,祖國]

文件2分詞結果: [祝福,祖國,強大,繁盛]

文件3分詞結果: [我,愛,藍天白雲,藍天,白雲]

通過上面的分詞結果,發現拆分的每個詞都是我們熟知的詞語, 但是如果不使用中文分詞,就會發現上面的文件把每個字拆分成了一個詞,對我們中文檢索很不友好。

再看倒排索引

    看到上面中文分詞器結果,就會有新的疑問,使用中文分詞器那樣分詞效果有什麼好處呢? 答案就是根據分詞建立詞彙與文件關係的倒排索引。這步都是es幫我們做的,下面通過"我","愛","祖國"三個詞看看倒排索引,如下圖:

通過上圖中的倒排索引,我們搜尋"祖國"時,es通過倒排索引可以快速的檢索出文檔1和文件3。如果沒有中文分詞器,搜尋"祖國"就會被拆分"祖""國"兩個詞的倒排索引, 就會把包含"祖"的文件都檢索出來,很明顯就會和我們想要的結果大相徑庭。

常用的中文分詞器

    Smart Chinese Analysis: 官方提供的中文分詞器,

    IKAnalyzer: 免費開源的java分詞器,目前比較流行的中文分詞器之一,簡單,穩定,想要特別好的效果,需要自行維護詞庫,支援自定義詞典

    結巴分詞: 開源的python分詞器,github有對應的java版本,有自行識別新詞的功能,支援自定義詞典

    Ansj中文分詞: 基於n-Gram+CRF+HMM的中文分詞的java實現,免費開源,支援應用自然語言處理

    hanlp: 免費開源,國人自然處理語言牛人無私風險的

個人對以上分詞器進行了一個粗略對比,如下圖:

截止到目前為止,他們的分詞準確性從高到低依次是:

hanlp> ansj >結巴>IK>Smart Chinese Analysis

結合準確性來看,選用中文分詞器基於以下考慮:

官方的Smart Chinese Analysis直接可以不考慮了

對搜尋要求不高的建議選用 IK 學習成本低,使用教程多,還支援遠端詞典

對新詞識別要求高的選用結巴分詞

Ansj和hanlp均基於自然處理語言,分詞準確度高,活躍度來講hanlp略勝一籌

博主選用的hanlp分詞器,目前線上執行結果來看準確性滿足需求

下面就寫一下博主對IKAnalyzer 和 hanlp分詞器的使用

IK Analyzer

    截止目前,IK分詞器外掛的優勢是支援自定義熱更新遠端詞典。

安裝ik分詞器外掛

es外掛安裝教程參考這裡

ik的es外掛地址: https://github.com/medcl/elasticsearch-analysis-ik/releases

博主使用的es版本是6.4.0,下載時要注意對應es版本

線上安裝ik es外掛 命令:

# /opt/apps/elasticsearch-6.4.0/bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.0/elasticsearch-analysis-ik-6.4.0.zip

檢視外掛安裝列表

# sudo /opt/apps/elasticsearch-6.4.0/bin/elasticsearch-plugin list

IK配置

    ik安裝完畢後配置檔案在 {ES_HOME}/config目錄下, 本例目錄是 /opt/apps/elasticsearch-6.4.0/config/analysis-ik/IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <!--配置檔名稱,表明整個配置檔案的目的即可,保持預設挺好的 -->
        <comment>IK Analyzer 擴充套件配置</comment>  
        <!--使用者可以在這裡配置自己的擴充套件字典,使用相對路徑,多個詞典使用逗號分隔,比如:custom/mydict1.dic,custom/mydict2.dic -->
        <entry key="ext_dict"></entry>
         <!--使用者可以在這裡配置自己的擴充套件停止詞字典,使用相對路徑,多個詞典使用逗號分隔,比如:custom/mydict1.dic,custom/mydict2.dic -->
        <entry key="ext_stopwords"></entry>
        <!--使用者可以在這裡配置遠端擴充套件字典,配置遠端擴充套件字典,多個詞典使用逗號分隔,比如: http://xxx.xx.com/xxx -->
        <entry key="remote_ext_dict">words_location</entry>
        <!--使用者可以在這裡配置遠端擴充套件停止詞字典,多個詞典使用逗號分隔,比如: http://xxx.xx.com/xxx -->
        <entry key="remote_ext_stopwords">words_location</entry>
</properties>

IK自定義詞典維護

文字詞典

    ik文字詞典均是以dic結尾,換行符作為分隔,示例如下:

# sudo vim /opt/apps/elasticsearch-6.4.0/config/analysis-ik/custom/myDic.dic

    修改ik配置檔案,將自定義的詞典新增到ik配置中

<entry key="ext_dict">custom/myDic.dic</entry>

重啟es,注意一定要重啟es

通過前面教程中,我們發現短語"我愛祖國",會被分詞為, "我","愛","祖國"三個詞, 如果按照上面詞典定義後, "我愛祖國"會被當成一個詞語不被分詞。

熱更新遠端詞典

    熱更新遠端詞典的優勢是,修改詞典後無需重啟es。每分鐘載入一次

    修改IK配置檔案如下:

      <!--使用者可以在這裡配置遠端擴充套件字典 -->
	<entry key="remote_ext_dict">location</entry>
 	<!--使用者可以在這裡配置遠端擴充套件停止詞字典-->
	<entry key="remote_ext_stopwords">location</entry>

其中 location 是指一個 url,比如 http://yoursite.com/getCustomDict,該請求只需滿足以下兩點即可完成分詞熱更新。

  1. 該 http 請求需要返回兩個頭部(header),一個是 Last-Modified,一個是 ETag,這兩者都是字串型別,只要有一個發生變化,該外掛就會去抓取新的分詞進而更新詞庫。

  2. 該 http 請求返回的內容格式是一行一個分詞,換行符用 \n 即可。

滿足上面兩點要求就可以實現熱更新分詞了,不需要重啟 ES 例項

可以將需自動更新的熱詞放在一個 UTF-8 編碼的 .txt 檔案裡,放在 nginx 或其他簡易 http server 下,當 .txt 檔案修改時,http server 會在客戶端請求該檔案時自動返回相應的 Last-Modified 和 ETag。可以另外做一個工具來從業務系統提取相關詞彙,並更新這個 .txt 檔案。

本文將遠端詞典存入資料庫,示例如下:

    @RequestMapping(value = "/getCustomDict")
    public ResponseEntity<String> getCustomDict(WebRequest webRequest) {
        LOGGER.info("查詢ik遠端詞典開始");
        StringBuilder stringBuilder = new StringBuilder();
        RemoteExtDictQuery query = new RemoteExtDictQuery();
        query.setLastModifiedStart(new Date(1L));
        query.setStatus(1);
        final long[] lastModified = {1L};
        // 這是從資料庫查詢字典,如果詞語很多的話,可以分頁查詢
        List<RemoteExtDictEntity> remoteExtDictEntityList  = remoteExtDictService.pageListRemoteExtDict(query);
        if (remoteExtDictEntityList == null) {
            remoteExtDictEntityList = new ArrayList<>();
        }
        remoteExtDictEntityList.stream().forEach((remoteExtDictEntity) -> {
            if (remoteExtDictEntity != null) {
                if (StringUtils.isNotBlank(remoteExtDictEntity.getWord())) {
                    stringBuilder.append(remoteExtDictEntity.getWord()).append("\n");
                }
                if (remoteExtDictEntity.getLastModified() != null) {
                    // 獲取到最新一條的更新時間
                    long tempLastModified = remoteExtDictEntity.getLastModified().getTime();
                    if (Long.compare(lastModified[0], tempLastModified) < 0) {
                        lastModified[0] = tempLastModified;
                    }
                }
            }
        });
        // 使用總條數作為etag
        String eTag = String.valueOf(remoteExtDictService.pageListRemoteExtDictCount(query));
        if (webRequest.checkNotModified(eTag, lastModified[0])) {
            // 檢查etag和 lastModified,如果沒有變化則返回空,遠端詞典不更新,否則返回最新詞典
            return null;
        } else {
            return ResponseEntity.ok().contentType(MediaType.parseMediaType("text/plain; charset=UTF-8"))
                    .body(stringBuilder.toString());
        }
    }

hanlp 中文分詞器

    截止目前,hanlp詞庫是最大,分詞效果是最好。使用hanlp分詞外掛之前,建議先點選此處學習一下hanlp

安裝hanlp中文分詞器外掛

    hanlp的elasticsearch外掛眾多,這裡選用了這個,這個外掛支援的分詞模式要多一些,截止現在此外掛最新支援6.3.2,由於外掛中包含很大的詞典檔案,建議此外掛採用離線安裝

# cd /opt/packages
# sudo yum -y wget
## 因網路不通,可能需要很長時間
# sudo wget -c https://github.com/KennFalcon/elasticsearch-analysis-hanlp/releases/download/v6.3.2/elasticsearch-analysis-hanlp-6.3.2.zip
## 解壓hanlp外掛
# sudo unzip -n elasticsearch-analysis-hanlp-6.3.2.zip -d /opt/packages/elasticsearch-analysis-hanlp
# sudo mkdir -p /opt/apps/elasticsearch-6.4.0/analysis-hanlp/config
## 將外掛配置放到{ES_HOME}/config 目錄下
# sudo mv elasticsearch-analysis-hanlp/config/ /opt/apps/elasticsearch-6.4.0/analysis-hanlp/config
## 將外掛放到{ES_HOME}/plugins 目錄下
# sudo mv elasticsearch-analysis-hanlp /opt/apps/elasticsearch-6.4.0/plugins/analysis-hanlp

檢視外掛安裝列表

# sudo /opt/apps/elasticsearch-6.4.0/bin/elasticsearch-plugin list

注意: 這裡有一個hanlp的警告,es版本是6.4.0, 但是外掛允許的es版本是6.3.2

上面的警告需要修改一下外掛配置, 本方法僅限於博主對應的版本喲,其他版本沒去試驗

# sudo vim /opt/apps/elasticsearch-6.4.0/plugins/analysis-hanlp/plugin-descriptor.properties

將 elasticsearch.version=6.3.2 修改為 elasticsearch.version=6.4.0,再次檢視外掛列表

ok,安裝成功,安裝完畢後必須重啟es喲必須重啟es喲必須重啟es喲

hanlp配置

# 詞典目錄,本文保持預設
root=plugins/analysis-hanlp/
# 核心詞典目錄,本文保持預設,多個詞典使用分號分隔
CoreDictionaryPath=data/dictionary/CoreNatureDictionary.txt
# Bigram詞典目錄,本文保持預設,多個詞典使用分號分隔
BiGramDictionaryPath=data/dictionary/CoreNatureDictionary.ngram.txt
# 核心停用詞詞典目錄,本文保持預設,多個詞典使用分號分隔
CoreStopWordDictionaryPath=data/dictionary/stopwords.txt
# 核心同義詞詞典目錄,本文保持預設,多個詞典使用分號分隔
CoreSynonymDictionaryDictionaryPath=data/dictionary/synonym/CoreSynonym.txt
# 人名詞典目錄,本文保持預設,多個詞典使用分號分隔
PersonDictionaryPath=data/dictionary/person/nr.txt
# 人名詞典tr目錄,本文保持預設,多個詞典使用分號分隔
PersonDictionaryTrPath=data/dictionary/person/nr.tr.txt
# tc詞典目錄,本文保持預設,多個詞典使用分號分隔
tcDictionaryRoot=data/dictionary/tc
# 自定義詞典目錄,根據最新詞典設定,多個詞典使用分號分隔
CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; SearchCustomDictionary.txt; 現代漢語補充詞庫.txt; 全國地名大全.txt ns; 人名詞典.txt; 機構名詞典.txt; 上海地名.txt ns;data/dictionary/person/nrf.txt nrf;
# CRF segment model path
#CRFSegmentModelPath=data/model/segment/CRFSegmentModel.txt
# HMM segment model path
#HMMSegmentModelPath=data/model/segment/HMMSegmentModel.bin
# True of false show term nature
#ShowTermNature=true
# IO Adapter
##IOAdapter=com.hankcs.hanlp.corpus.io.FileIOAdapter

hanlp自定義詞典

hanlp語料庫詞典

    hanlp語料庫地址為: https://github.com/hankcs/HanLP/releases, 本文截止目前最新版本為1.6.8

  1. 下載資料包 hanlp.linrunsoft.com/release/data-for-1.6.8.zip
  2. 解壓到配置檔案中key為root的值對應目錄下
  3. 根據詞典名調整hanlp配置中的詞典配置,尤其注意CustomDictionaryPath的配置,以前採用的應用名,現在採用的中文名稱
  4. 刪除二進位制快取檔案 rm -r /opt/apps/elasticsearch-6.4.0/plugins/analysis-hanlp/data/dictionary/custom/CustomDictionary.txt.bin, 如果自定義詞典變更了,一點要執行這一步,否則詞典不生效
  5. 一定要重啟es!!!一定要重啟es!!!一定要重啟es!!!

hanlp自定義熱更新詞典

  1. 在配置檔案中key為root的值對應目錄下找到目錄custom,進入此目錄
  2. 建立一個txt檔案,示例: myDic.txt
  3. 在myDic.txt檔案中新增詞,分隔符為換行符, 詞典格式為: [單詞] [詞性A] [A的頻次]  ,如圖: 
  4. 刪除二進位制快取檔案 rm -r /opt/apps/elasticsearch-6.4.0/plugins/analysis-hanlp/data/dictionary/custom/CustomDictionary.txt.bin, 如果自定義詞典變更了,一點要執行這一步,否則詞典不生效
  5. 一定要重啟es!!!一定要重啟es!!!一定要重啟es!!!

彩蛋

    本文寫完了中文分詞器的作用和使用,下一篇將記錄es客戶端在專案中如何使用