ES中文分詞器之精確短語匹配(自定義分詞器)

樓主在上篇文章中液斜,提出了將詞和字分開霜大,用不同的分詞器分別構(gòu)建索引癞谒,來解決match_phrase在中文中的短語或者句子匹配問題望蜡。詳細(xì)的內(nèi)容請看上一篇文章:
ES中文分詞器之精確短語匹配(解決了match_phrase匹配不全的問題)

為什么要自己寫分詞器绒窑?

樓主想要一種分詞器棕孙,分詞器完全按照詞典分詞,只要是詞典有的詞語些膨,分詞器就一定要分出來蟀俊。測試了兩個分詞器比如說IK,MMseg订雾,都不能按照樓主的要求分詞肢预。

MMSeg有考慮到詞頻,即使使用mmseg_max_word洼哎,也不能完全按照詞典分詞烫映。

IK理論上是按照詞典分詞的沼本,但是經(jīng)測試,還是發(fā)現(xiàn)了些問題锭沟。比如說“一群穆斯林聚在一起”抽兆,單獨用這句話測試,“穆斯林”可以分出族淮,而這句話放入一篇文章中辫红,卻無法分出“穆斯林”。

樓主是用ik和standard對比命中量發(fā)現(xiàn)不一致祝辣,導(dǎo)出不一致數(shù)據(jù)后贴妻,才發(fā)現(xiàn)的這個問題(ik和mmseg都修改了源碼,過濾掉中文之間的特殊符號蝙斜,因此不存在詞語中間有特殊符號standard可以分出名惩,ik分不出而導(dǎo)致的不一致情況)。

沒辦法了孕荠,自己寫一個吧娩鹉。

ES自定義分詞器

由于ES是采用juice依賴注入的方式,所以要實現(xiàn)一個工廠類和Provider類岛琼。

public class TestAnalyzerProvider extends AbstractIndexAnalyzerProvider<InfosecAnalyzer> {

public TestAnalyzerProvider(IndexSettings indexSettings, Environment env, String name, Settings settings) {
    super(indexSettings, name, settings);
}

public static AnalyzerProvider<? extends Analyzer> getMaxWord(IndexSettings indexSettings, Environment environment, String s, Settings settings) {
    return  new TestAnalyzerProvider(indexSettings,environment,s,settings);
}

@Override public InfosecAnalyzer get() {
    return new InfosecAnalyzer();
 }
}

public class TestTokenizerFactory extends AbstractTokenizerFactory {

 public TestTokenizerFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) {
     super(indexSettings, name, settings);
 }

public static TokenizerFactory getMaxWord(IndexSettings indexSettings, Environment environment, String name, Settings settings) {
    return new TestTokenizerFactory(indexSettings,environment,name,settings);
}

@Override
public Tokenizer create() {
    return new TestTokenizor();
 }
}

接下來寫自己的插件配置類:

public class AnalysisTestPlugin extends Plugin implements AnalysisPlugin {

public static String PLUGIN_NAME = "analysis-test;

@Override
public Map<String, AnalysisModule.AnalysisProvider<TokenizerFactory>> getTokenizers() {
    Map<String, AnalysisModule.AnalysisProvider<TokenizerFactory>> extra = new HashMap<>();

    extra.put("test_max_word", TestTokenizerFactory::getMaxWord);

    return extra;
}

@Override
public Map<String, AnalysisModule.AnalysisProvider<AnalyzerProvider<? extends Analyzer>>> getAnalyzers() {
    Map<String, AnalysisModule.AnalysisProvider<AnalyzerProvider<? extends Analyzer>>> extra = new HashMap<>();

    extra.put("test_max_word", TestAnalyzerProvider::getMaxWord);

    return extra;
}
}

因為我們只需要按照詞典分詞底循,所以這邊只有一種最大分詞模式,test_max_word槐瑞。接下來就是Analyzer 和Tokenizor。

public class TestAnalyzer extends Analyzer {

public TestAnalyzer(){
    super();
}
@Override
protected TokenStreamComponents createComponents(String fieldName) {
    Tokenizer _TestTokenizer = new TestTokenizor();
    return new TokenStreamComponents(_TestTokenizer);
}
}


public class TestTokenizor extends Tokenizer {
//詞元文本屬性
private final CharTermAttribute termAtt;
//詞元位移屬性
private final OffsetAttribute offsetAtt;
//詞元分類屬性(該屬性分類參考o(jì)rg.wltea.analyzer.core.Lexeme中的分類常量)
private final TypeAttribute typeAtt;
//記錄最后一個詞元的結(jié)束位置
private int endPosition;

private TestSegmenter test =null;

public InfosecTokenizor(){
    super();
    offsetAtt = addAttribute(OffsetAttribute.class);
    termAtt = addAttribute(CharTermAttribute.class);
    typeAtt = addAttribute(TypeAttribute.class);

    test = new TestSegmenter(input);
}

@Override
public boolean incrementToken() throws IOException {
    clearAttributes();
    Word word = test.getNext();
    if(word != null) {
        termAtt.copyBuffer(word.getSen(), word.getWordOffset(), word.getLength());
        offsetAtt.setOffset(word.getStartOffset(), word.getEndOffset());
        typeAtt.setType(word.getType());
        return true;
    } else {
        end();
        return false;
    }
}

public void reset() throws IOException {
    super.reset();
    //setReader 自動被調(diào)用, input 自動被設(shè)置阁苞。
    test.reset(input);
}
}

自定義分詞器主要操作的是incrementToken方法困檩,每次從TestSegmenter中取出一個詞,如果改詞存在那槽,設(shè)置改詞的token屬性悼沿,返回true,即還有下一個token骚灸。如果改詞不存在糟趾,返回false,標(biāo)志著沒有數(shù)據(jù)了甚牲,結(jié)束分詞义郑。

自定義分詞的詳細(xì)內(nèi)容

由于代碼太多了,這里就不一一貼出丈钙,只介紹下算法思想非驮。

匹配類型

1)不匹配
2)前綴
3)匹配
4)匹配且是前綴

算法思想

先將數(shù)據(jù)分類組裝成句子,然后經(jīng)過句子處理器將句子分為多個word雏赦,存入queue中劫笙,再由increateToken()方法依次取出芙扎。

組裝句子

依次掃描,將同類的數(shù)據(jù)組裝成句子填大。比如說“你好哈233節(jié)日戒洼,快樂!233dad”允华,掃描第一個字符發(fā)現(xiàn)是中文圈浇,則繼續(xù)向下掃描,一直掃描到‘2’例获,發(fā)現(xiàn)‘2’不是中文汉额,則將“你好哈”組成句子交給句子處理器處理,將處理結(jié)果放入queue中榨汤。繼續(xù)掃描蠕搜,遍歷到‘節(jié)’,發(fā)現(xiàn)‘節(jié)’不是數(shù)組收壕,則將“233”組成一個word妓灌,放入queue。繼續(xù)掃描蜜宪,將“節(jié)”虫埂,“日”依次放入句子中,掃描到“圃验,”掉伏,因為要和standard 對比效果,所以我在代碼中過濾了中文間所有的符號澳窑,忽略“斧散,”繼續(xù)掃描,依次將“快”“樂”存入句子摊聋。后面類似處理即可鸡捐。

句子分詞

依次掃描句子,如果相鄰的數(shù)據(jù)可以組裝成一個詞麻裁,則將詞放入queue中箍镜,繼續(xù)遍歷下一個。例如“節(jié)日快樂”煎源,分詞時首先掃描“節(jié)”色迂,在詞典中查詢“節(jié)”,發(fā)現(xiàn)“節(jié)”是一個前綴薪夕,則繼續(xù)掃描“日”脚草,發(fā)現(xiàn)“節(jié)日”是一個詞匹配,且是一個前綴原献,則將“節(jié)日”存入queue中馏慨,繼續(xù)掃描“節(jié)日快”埂淮,發(fā)現(xiàn)“節(jié)日快”是一個前綴,繼續(xù)掃描“節(jié)日快樂”写隶,發(fā)現(xiàn)“節(jié)日快樂”僅是一個詞匹配倔撞,則將“節(jié)日快樂”存入queue中,結(jié)束從“節(jié)”開始的掃描慕趴。接下來按照上述方法從“日”字開始掃描痪蝇。依次處理完整個句子。

詞典

詞典采用樹的結(jié)構(gòu)冕房,比如說“節(jié)日愉快”躏啰,“節(jié)日快樂”和“萬事如意”這三個詞,在詞典中如下表示:

詞典結(jié)構(gòu)

查找時耙册,記錄上一次前綴匹配的DicSegment给僵,在前綴的DicSegment中,直接查找當(dāng)前掃描字符详拙,可以加快匹配速度帝际。

比如說已經(jīng)匹配到了”節(jié)日快“這個前綴,在匹配”節(jié)日快樂“時饶辙,直接在”快“對應(yīng)的DicSegment中查找蹲诀,這樣就不用再次匹配”節(jié)日“兩個字符。

問題

測試的過程中同樣的發(fā)現(xiàn)了一些問題弃揽,比如說:

原文:長白山脈
test分詞:長白 1 長白山 2 長白山脈 3 白山4 山脈5
查找詞語:長白山
test分詞:長白 1 長白山 2 白山 3

通過分詞可以看出在“長白山脈”中查詢不到“長白山”的脯爪。問題在于match_phrase的限制,長白山的分詞順序在原文構(gòu)建索引時的位置不一樣矿微,中間多出了一個“長白山脈”披粟。

解決方案:

不能匹配的原因是,查找詞語在原文中和后面的字組成了詞語冷冗。用最小粒度分詞即可解決。也就是說只用長度為2和3的詞語惑艇。不存在長度為4的詞語蒿辙,所以一個詞長度為3時,在原文中不會和后面的數(shù)據(jù)組成詞滨巴。當(dāng)詞的長度為2時思灌,和后面的一個字匹配,可以組成一個長度為3的詞恭取,按照我們分詞的規(guī)則泰偿,是先分出兩個字的詞,再分出三個字的詞蜈垮,所以耗跛,兩個字的詞是可以匹配的到的裕照。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市调塌,隨后出現(xiàn)的幾起案子晋南,更是在濱河造成了極大的恐慌,老刑警劉巖羔砾,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件负间,死亡現(xiàn)場離奇詭異,居然都是意外死亡姜凄,警方通過查閱死者的電腦和手機政溃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來态秧,“玉大人董虱,你說我怎么就攤上這事∮炝” “怎么了空扎?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長润讥。 經(jīng)常有香客問我转锈,道長,這世上最難降的妖魔是什么楚殿? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任撮慨,我火速辦了婚禮,結(jié)果婚禮上脆粥,老公的妹妹穿的比我還像新娘砌溺。我一直安慰自己,他們只是感情好变隔,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布规伐。 她就那樣靜靜地躺著,像睡著了一般匣缘。 火紅的嫁衣襯著肌膚如雪猖闪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天肌厨,我揣著相機與錄音培慌,去河邊找鬼。 笑死柑爸,一個胖子當(dāng)著我的面吹牛吵护,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼馅而,長吁一口氣:“原來是場噩夢啊……” “哼祥诽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起用爪,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤原押,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后偎血,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诸衔,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年颇玷,在試婚紗的時候發(fā)現(xiàn)自己被綠了笨农。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡帖渠,死狀恐怖谒亦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情空郊,我是刑警寧澤份招,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站狞甚,受9級特大地震影響锁摔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哼审,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一谐腰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涩盾,春花似錦十气、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至址儒,卻和暖如春籍胯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背离福。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炼蛤,地道東北人妖爷。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親絮识。 傳聞我的和親對象是個殘疾皇子绿聘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 分詞器選擇 調(diào)研了幾種分詞器,例如IK分詞器次舌,ansj分詞器熄攘,mmseg分詞器,發(fā)現(xiàn)IK的分詞效果最好彼念。舉個例子:...
    YG_9013閱讀 45,999評論 7 22
  • 常用概念: 自然語言處理(NLP) 數(shù)據(jù)挖掘 推薦算法 用戶畫像 知識圖譜 信息檢索 文本分類 常用技術(shù): 詞級別...
    御風(fēng)之星閱讀 9,202評論 1 25
  • 背景 英文以空格作為分隔符挪圾,而中文詞語之間沒有分隔; 在中文里逐沙,“詞”和“詞組”邊界模糊現(xiàn)代漢語的基本表達(dá)單元雖然...
    翼徳閱讀 2,749評論 0 5
  • 轉(zhuǎn)載請注明:終小南 ? 中文分詞算法總結(jié) 什么是中文分詞眾所周知哲思,英文是以 詞為單位的,詞和詞之間是靠空格隔開吩案,而...
    kirai閱讀 9,846評論 3 24
  • 1)ICTCLAS 最早的中文開源分詞項目之一棚赔,由中科院計算所的張華平、劉群所開發(fā)徘郭,采用C/C++編寫靠益,算法基于《...
    MobotStone閱讀 5,720評論 1 15