ElasticSearch pinyin分詞支持多音字

ElasticSearch pinyin分詞支持多音字

背景

我們有一個搜索業務底層採用ElasticSearch作爲搜索引擎,在索引的過程中,使用了ik中文分詞拼音分詞同義詞等多種分詞器。ES和各種插件的組合已經能滿足我們線上90%的需求,但是仍有部分需求無法覆蓋,我們在拼音分詞的時候過程中就遇到了無法解決的問題。

比如在 三一重工 中,一重 這個詞在拼音詞庫 polyphone.txt 中有對應的詞彙,讀作 yichong,因此這整個詞的讀音爲 sanyichonggong, 但是其真實讀音應該爲 sanyizhonggong。這是因爲在拼音分詞過程中會先去詞庫中檢索是否有對應的詞彙,沒有的話再用單字拼音代替最後拼接在一起。

再比如在 蔚來汽車 中,蔚來 算是一個新詞,在拼音詞庫 polyphone.txt 中沒有對應的詞彙,因此這個詞對應的拼音是每個字的拼音拼接而成,結果爲 yulai 。但是其真實讀音應該爲 weilai, 那麼我們的用戶就無法通過拼音 weilai 搜索到相關的內容。

經過查看拼音分詞源代碼發現,拼音分詞其實是調用nlp-lang這個項目裏的方法實現的分詞器。而這個nlp-lang項目中,拼音解析如果遇到多音字僅僅返回第一個拼音,這樣很多讀音都無法獲取到。

if(temp.length()==1){
    //單個字取第一個拼音
    lists.add(PinyinFormatter.formatPinyin(word.getParam()[0], format));
} else {
    for (String t : word.getParam()) {
        lists.add(PinyinFormatter.formatPinyin(t, format));
    }
}

面對龐大的多音字列表,通過手工維護、修改詞彙列表顯然無法完全達到目的。

爲此,我們決定調整這部分代碼滿足我們線上業務的需求。

調整

這部分僅僅介紹調整思路,不設計具體代碼實現。

1. nlp-lang

拼音分詞會調用 nlp-lang 中的一個方法,把中文字符串轉換爲拼音,獲得一個字符串列表

List<String> pinyinList = Pinyin.pinyin(source);

我們在這個基礎上新增了一個 multiplePinyin 方法,可以獲取多音字所有讀音,並且不再檢索 polyphone.txt 中的詞庫對照表。

System.out.println(Pinyin.pinyin("蔚來"))

>>> ['yu', 'lai']

System.out.println(Pinyin.multiplePinyin("蔚來"))

>>> ['yu wei', 'lai']

System.out.println(Pinyin.pinyin("三一重工"))

>>> ['san', 'yi', 'chong', 'gong']

System.out.println(Pinyin.multiplePinyin("三一重工"))

>>> ['san', 'yi', 'zhong chong', 'gong']

多音字的多個讀音用空格分割。

2. elasticsearch-analysis-pinyin

  1. 首先在原來的分詞器基礎上新增 multiple_pinyin 類型的分詞器和過濾器,確保不會影響到之前的拼音分詞的功能。
public Map<String, AnalysisModule.AnalysisProvider<org.elasticsearch.index.analysis.TokenFilterFactory>> getTokenFilters() {
    Map<String, AnalysisModule.AnalysisProvider<org.elasticsearch.index.analysis.TokenFilterFactory>> extra = new HashMap<>();
    extra.put("pinyin", PinyinTokenFilterFactory::new);
    
    // 新增加的分詞類型
    extra.put("multiple_pinyin", MultiplePinyinTokenFilterFactory::new);
    return extra;
}
  1. multiple_pinyin的分詞器中使用上面新增的 Pinyin.multiplePinyin 方法獲取到每個字的多音字。然後根據空格拆分後將所有可能的結果組合在一起。
// pinyin  "蔚來"

["yulai"]

// multiple_pinyin  "蔚來"

["yulai", "weilai"]


// pinyin "三一重工"

["sanyichonggong"]

// multiple_pinyin  "三一重工"

["sanyizhonggong", "sanyichonggong"]

// pinyin  "廈門重工" (兩個多音字:夏、重)

["xiamenzhonggong"]

// multiple_pinyin  "廈門重工"

["shamenzhonggong", "shamenchonggong", "xiamenzhonggong", "xiamenchonggong"]

問題

因爲支持多音字的拼音分詞是所有讀音可能結果的笛卡爾積,因此當輸入的字符串長度過大時,分詞的結果可能會特別大。假如輸入的字符串中有10個字是多音字,每個字都有2種讀音,那麼分詞結果就有2^10個。可想而之,耗時會非常長。

我們的使用場景中,僅僅針對物品名稱進行分詞,名稱不會很長,暫時沒有遇到性能瓶頸。

相關代碼

nlp-lang
elasticsearch-analysis-pinyin

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章