概述java
環境準備python
認識中文分詞器nginx
彩蛋git
上一篇博文記錄了elasticsearch插件安裝和管理, 在地大物博的祖國使用es,不得不考慮中文分詞器,es內置的分詞器對中文分詞的支持用慘不忍睹來形容不爲過,看這篇博文以前,建議先看一下博文elasticsearch分詞器,對分詞器有個初步理解。本文將記錄一下項目中如何使用選用和使用中文分詞器的,但願可以幫助到即未來踩坑的小夥伴們,歡迎批評指正github
本文都是基於elasticsearch安裝教程 中的elasticsearch安裝目錄(/opt/environment/elasticsearch-6.4.0)爲範例web
在博文elasticsearch分詞器中提到elasticsearch可以快速的經過搜索詞檢索出對應的文章歸功於倒排索引,下面經過中文舉例看看倒排索引。數據庫
中文分詞器是作什麼的呢? what? 經過名字就知道了啊,爲何還要問。。。下面經過三個文檔示例,看看它是如何分詞的vim
文檔1: 我愛偉大的祖國centos
文檔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分詞器插件的優點是支持自定義熱更新遠程詞典。
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安裝完畢後配置文件在 {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文本詞典均是以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
,該請求只需知足如下兩點便可完成分詞熱更新。
該 http 請求須要返回兩個頭部(header),一個是 Last-Modified
,一個是 ETag
,這二者都是字符串類型,只要有一個發生變化,該插件就會去抓取新的分詞進而更新詞庫。
該 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的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喲
# 詞典目錄,本文保持默認 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語料庫地址爲: https://github.com/hankcs/HanLP/releases, 本文截止目前最新版本爲1.6.8
本文寫完了中文分詞器的做用和使用,下一篇將記錄es客戶端在項目中如何使用