深入解析 Elasticsearch IK 分詞器:ik_smart 和 ik_max_word 的區別與應用場景
1、Elasticsearch ik 分詞器常見問題
最近在 git 上看看 ik 的相關問題,發現大家問的比較多的是 ik 分詞器的 ik_smart 和 ik_max_word 兩個分詞模式,以及它倆之間的分詞差異。
圖片
圖片
這里來集中解釋一波,期望對大家有幫助。
2、ik_smart 與 ik_max_word 的異同
首先來看下官方的FAQs
What is the difference between ik_max_word and ik_smart?
ik_max_word: Performs the finest-grained segmentation of the text. For example, it will segment "中華人民共和國國歌" into "中華人民共和國,中華人民,中華,華人,人民共和國,人民,人,民,共和國,共和,和,國國,國歌", exhaustively generating various possible combinations, suitable for Term Query.
ik_smart: Performs the coarsest-grained segmentation of the text. For example, it will segment "中華人民共和國國歌" into "中華人民共和國,國歌", suitable for Phrase queries.
Note: ik_smart is not a subset of ik_max_word.
官方這里簡單的描述了一下使用用途,即:
- ik_smart 比較適合 match_phrase query,而 ik_max_word 更合適 term query。
- ik_smart 的分詞結果并不是 ik_max_word 的分詞結果的子集。
那這兩個分詞器在具體實現上會有什么不一樣呢?
哪些場景兩個分詞器的分詞結果肯定不同呢?
造成分詞結果不一樣的原因是什么?
3、ik 分詞器源碼分析
3.1. 量詞處理源碼剖析
這里先看一下這段代碼。
private void compound(Lexeme result){
if(!this.cfg.isUseSmart()){
return ;
}
//數量詞合并處理
if(!this.results.isEmpty()){
if(Lexeme.TYPE_ARABIC == result.getLexemeType()){
Lexeme nextLexeme = this.results.peekFirst();
boolean appendOk = false;
if(Lexeme.TYPE_CNUM == nextLexeme.getLexemeType()){
//合并英文數詞+中文數詞
appendOk = result.append(nextLexeme, Lexeme.TYPE_CNUM);
}else if(Lexeme.TYPE_COUNT == nextLexeme.getLexemeType()){
//合并英文數詞+中文量詞
appendOk = result.append(nextLexeme, Lexeme.TYPE_CQUAN);
}
if(appendOk){
//彈出
this.results.pollFirst();
}
}
//可能存在第二輪合并
if(Lexeme.TYPE_CNUM == result.getLexemeType() && !this.results.isEmpty()){
Lexeme nextLexeme = this.results.peekFirst();
boolean appendOk = false;
if(Lexeme.TYPE_COUNT == nextLexeme.getLexemeType()){
//合并中文數詞+中文量詞
appendOk = result.append(nextLexeme, Lexeme.TYPE_CQUAN);
}
if(appendOk){
//彈出
this.results.pollFirst();
}
}
}
}
這里由 smart 模式觸發的 合并英文數詞+中文量詞 的處理中,把 token 的屬性修改成了 TYPE_CQUAN (中文數量詞)。
這是 smart 模式下擁有而 max 模式下沒有的分詞方式和 token 類型。
舉個例子:“7天” 這個詞的分詞結果,結果中分別展示了位置:內容:類型
ik_max_word:
0-1 : 7 : ARABIC
1-2 : 天 : COUNT
ik_smart
0-2 : 7天 : TYPE_CQUAN
也就是說 ik_max_word 與 ik_smart 在‘英文數詞+中文量詞’的分詞場景下,分詞結果必定不一樣。
3.2. 切分模式和歧義消除剖析
ik分詞器的算法原則還是基于中文字典進行字典樹的匹配。
也就是說詞元匹配的前提是豐富的中文字典庫(ik 已經默認加載了幾十萬的字典庫了)。
我們先來看 ik_max_word 的切分模式:執行文本的最細粒度分割,將分段詳盡地生成各種可能的組合。
來看下“中華人民共和國國歌”的例子,這里為了更加直觀的體現字典樹的匹配模式,我們把字典庫的內容也列出來。
文本:中華人民共和國國歌
字典庫:中華人民共和國國歌,中華人民,中華,華人,人民共和國,人民,共和國,共和,國國,國歌
ik_max_word 分詞結果:
0-9 : 中華人民共和國國歌 : CN_WORD
0-4 : 中華人民 : CN_WORD
0-2 : 中華 : CN_WORD
1-3 : 華人 : CN_WORD
2-7 : 人民共和國 : CN_WORD
2-4 : 人民 : CN_WORD
4-7 : 共和國 : CN_WORD
4-6 : 共和 : CN_WORD
6-8 : 國國 : CN_WORD
7-9 : 國歌 : CN_WORD
可以看出 ik_max_word 分詞器把所有的字典結果都匹配出來了,同時也看到了好幾個詞元的位置是有重疊的,比如:“中華人民”“中華”“華人”這幾個詞元,位置在0-4這段有著不同的重疊。
這也就是造成了代碼中所需要處理的“歧義”,我們這里可以把“歧義”理解為多個詞元組合去代表一段內容。
而 ik_smart 分詞器主要作用就是通過對詞元組合進行歧義裁決來消除詞元間的歧義,消除歧義后的直觀體現就是不再會有位置重疊的詞元(這也是 ik_smart 更適合 match_phrase 查詢的原因)。
ik_smart 遵循歧義裁決的主要原則順序如下:
- 比較有效文本長度,越長越好;
- 比較詞元個數,越少越好;
- 路徑跨度越大越好;
- 根據統計學結論,逆向切分概率高于正向切分,因此位置越靠后的優先;
- 詞元位置權重比較,詞長越平均越好。
同樣的文本內容,同樣的字典庫,ik_smart 的分詞結果如下:
ik_smart 分詞結果:
0-9 : 中華人民共和國國歌 : CN_WORD
由于字典庫中“中華人民共和國國歌”可以覆蓋整個文本,并滿足上訴大多數條件,ik_smart 就只保留了第一個詞元。
為了更直觀的感受,我們把“中華人民共和國國歌”從詞庫中去除。
字典庫:中華人民,中華,華人,人民共和國,人民,共和國,共和,國國,國歌
ik_smart 分詞結果:
0-4 : 中華人民 : CN_WORD
4-7 : 共和國 : CN_WORD
7-9 : 國歌 : CN_WORD
對于 ik_smart 歧義裁決原理有興趣的同學可以看源碼中 LexemePath 類的 compareTo 方法。
4、使用建議
- 召回要求高,對分詞詞元匹配精準的,使用 ik_max_word,并結合 term 查詢。
- 召回要求低,分詞切分要求較低,節省存儲,比如日志場景,可以考慮 ik_smart 進行 match_phrase查詢。
- 索引分詞器和搜索分詞器原則上保持一致,如果索引使用 ik_max_word 而搜索使用 ik_smart,則有詞元匹配失敗的可能。
作者介紹
金多安,Elastic 認證專家,Elastic資深運維工程師,死磕Elasticsearch知識星球嘉賓,星球Top活躍技術專家,搜索客社區日報責任編輯