ElasticSearch 2 (26) - 语言处理系列之打字或拼写错误
ElasticSearch 2 (26) - 語言處理系列之打字或拼寫錯誤
摘要
我們喜歡在對結構化數(shù)據(jù)(如:日期和價格)做查詢時,結果只返回那些能精確匹配的文檔。但是,好的全文搜索不應該有這樣的限制。相反,我們可以擴大范圍,包括更多可能匹配的詞語,使用相關度評分將更匹配的文檔放置在結果集的頂部。
事實上,只做精確匹配的全文搜索很有可能會讓用戶失望,難道我們不希望搜索 “quick brown fox” 可以匹配到包含 “fast brown foxes” 的文檔,“Johnny Walker” 可以匹配 “Johnnie Walker”,“Arnold Shcwarzenneger” 可以匹配 “Arnold Schwarzenegger”?
如果文檔確實精確包含用戶的搜索條件,那么它們應該出現(xiàn)在結果集的頂部,而弱匹配可以置于結果列表的稍后地方。如果沒有精確的匹配,至少我們可以為用戶提供潛在匹配的可能,它們甚至可能是用戶搜索的初衷!
我們已經(jīng)在 規(guī)則化標記(Normalizing Tokens) 介紹了處理變音詞的匹配方式,在 縮減單詞至詞根形式(Reducing Words to Their Root Form) 中介紹了詞語的詞干提取方式,在 同義詞(Synonyms) 中介紹了同義詞的處理方式,但是所有這些方法都以單詞是正確拼寫這個條件為前提,或者說每個詞的拼寫方式只有一種。
模糊匹配允許在查詢時匹配拼寫錯誤的詞,語音標記過濾器可以在索引時用來語音匹配。
版本
elasticsearch版本: elasticsearch-2.x
內(nèi)容
模糊邏輯(Fuzziness)
模糊匹配將兩個模糊相似的詞作為同一詞語處理,首先,我們須要定義什么是模糊性。
在 1965 年,Vladimir Levenshtein 開發(fā)了 Levenshtein 距離算法,它可以計算從一個單詞變成另外一個單詞時,單個字符改變的總次數(shù)。它提出了三種類型的單字符修改方式:
- 將一個字符替換成為另外一個:f ox → b ox
- 插入新的字符:sic → sic k
- 刪除字符:b l ack → back
Frederick Damerau 后來對這些操作進行了擴展,新增了一種方式:
- 交換兩個相鄰字符的位置:st ar → ts ar
例如,將單詞 bieber 轉換成 beaver 需要以下步驟:
這三步用 Damerau-Levenshtein 法表示的距離是 3 。
顯然,bieber 與 beaver 有很長一段距離,它們相距太遠而不能被認為是簡單的拼寫錯誤。Damerau 觀察到 80% 的人為拼寫錯誤的距離是 1 。換句話說,有 80% 的拼寫錯誤可以通過單次字符修改得到原始的字符串。
Elasticsearch 支持一個最大的編輯距離是 2 ,可以通過參數(shù) fuzziness 來指定。
當然,單次修改的影響還取決于被修改字符串的長度。單詞 hat 經(jīng)兩次修改可以變成 mad ,所以允許長度為 3 的字符串修改兩次有點過度。參數(shù) fuzziness 可以被設置成 AUTO ,代表以下三中最大編輯距離:
- 0 對于長度為 1 或 2 的字符串
- 1 對于長度為 3、4 或 5 的字符串
- 2 對于長度大于 5 的字符串
當然,可能會發(fā)現(xiàn) 2 步修改仍然過度,返回的結果看上去并不相關。當最大 fuzziness 值為 1 時,我們可能會得到更好的搜索結果和更好的性能。
模糊查詢(Fuzzy Query)
fuzzy 查詢和使用 term 查詢的 fuzzy 特性是等價的。通常我們很少會自己直接使用它,但是理解它的工作方式有助于我們在高層 match 查詢中利用模糊的特性。
為了理解它是如何工作的,我們先新建索引一些文檔:
POST /my_index/my_type/_bulk{ "index": { "_id": 1 }}{ "text": "Surprise me!"}{ "index": { "_id": 2 }}{ "text": "That was surprising."}{ "index": { "_id": 3 }}{ "text": "I wasn't surprised."}現(xiàn)在我們就可以運行 fuzzy 查詢搜索 surprize:
GET /my_index/my_type/_search{"query": {"fuzzy": {"text": "surprize"}}}fuzzy 查詢是一個詞項級別的查詢,所以它沒有做任何分析的工作。它接收單個詞項,并且根據(jù)指定的模糊邏輯在字典中查找匹配的所有詞項。默認的 fuzziness 值是 AUTO。
在我們的例子中,surprize 距離 2 以內(nèi)的詞有 surprise 和 surprised,所以文檔 1 和 3 是匹配的。我們可以通過以下查詢將匹配結果縮小至 surprise :
GET /my_index/my_type/_search{"query": {"fuzzy": {"text": {"value": "surprize","fuzziness": 1}}}}提升性能(Improving Performance)
fuzzy 查詢接受原始詞項并為其構建一個 Levenshtein 自動機(automaton) ,這個結構有如一個圖,它能表示與原始字符串相距指定編輯距離值以內(nèi)的所有可能字符串。
模糊查詢隨后使用這個自動機逐步高效的在字典中匹配所有詞項,一旦收集到字典里所有的匹配詞項,它便能計算出所有與之匹配的文檔。
當然,由于索引里存儲的數(shù)據(jù)類型不同,一個編輯距離為 2 可以與大量詞匹配,性能也會非常差。以下兩個參數(shù)可以用來限制對性能的不良影響:
prefix_length
設置字符串不會被“模糊化”的起始長度。多數(shù)拼寫錯誤出現(xiàn)在詞語的末尾處,而不是開始處。比如,當 prefix_length 的值為 3 時,我們可以大大減少需要匹配詞項的數(shù)目。
max_expansions
如果模糊查詢擴展到三或四個選項的時候,這些新選項可能還是有意義的,但當它生成 1,000 個選項時,它們實際上毫無意義。用 max_expansions 來限制生成選項的總數(shù),模糊查詢會搜集匹配的詞項,直到找不出更多匹配或詞項數(shù)目達到 max_expansions 數(shù)值的限制。
模糊匹配查詢(Fuzzy match Query)
match 查詢自帶支持模糊匹配的功能:
GET /my_index/my_type/_search{"query": {"match": {"text": {"query": "SURPRIZE ME!","fuzziness": "AUTO","operator": "and"}}}}查詢字符串首先經(jīng)過分析,然后生成詞項 [surprize, me],最后每個詞項用指定的 fuzziness(模糊邏輯) 進行模糊化處理。
類似地,multi_match 查詢也支持模糊邏輯,但它只支持兩種類型 best_fields 或 most_fields:
GET /my_index/my_type/_search{"query": {"multi_match": {"fields": [ "text", "title" ],"query": "SURPRIZE ME!","fuzziness": "AUTO"}}}match 和 multi_match 查詢都能支持 prefix_length 和 max_expansions 參數(shù)。
小貼士
模糊邏輯只能使用于兩種基本查詢 match 和 multi_match,而無法使用于短語匹配,常用詞項或跨字段匹配。
模糊度的評分(Scoring Fuzziness)
用戶喜歡模糊查詢,他們以為這些查詢總會奇妙的找到正確的拼寫結果。不幸的是,事實卻無法激動人心。
假設我們有 1,000 個文檔包含詞語 “Schwarzenegger”,其中只有 1 個文檔里有錯誤拼寫 “Schwarzeneger”,根據(jù) TF/IDF 的理論,這個錯誤拼寫比正確拼寫更具相關性,因為它在文檔里出現(xiàn)的次數(shù)要少得多!
換句話說,如果我們同等對待模糊匹配和其他匹配,那么我們就會更偏向錯誤拼寫而不是正確拼寫,這會使用戶感到抓狂的。
小貼士
模糊匹配不應用于以評分為目的的查詢,而只能當有錯誤拼寫時,用于擴大匹配詞項的匹配范圍。
默認情況下,match 查詢給所有模糊匹配的分數(shù)總是 1 。這足以使?jié)撛诘钠ヅ涑霈F(xiàn)在結果的末尾,而不用影響到非模糊查詢的相關度評分計算。
小貼士
模糊查詢本身并沒有剛開始看起來那么有用,它們最好作為高級功能的一部分,例如 輸入即搜索 completion 完成建議器 或 你想查找 詞組建議器。
語音匹配(Phonetic Matching)
最后,令人絕望的是,試圖匹配那些聽上去相似的詞,這些詞的拼寫甚至是不同的。
現(xiàn)存一些算法可以將詞語轉換成語音形式表示。Soundex 算法是它們的始祖,其他所有類似算法都是 Soundex 的改進或定制形式,例如 Metaphone 和 Double Metaphone (它對語音匹配擴展到其他語言而不只是英語),Caverphone 算法可以匹配新西蘭姓名,Beider-Morse 算法以 Soundex 算法為基礎,但它能更好的匹配德語和依地語姓名,K?lner Phonetik 對德語詞支持更好。
我們從上面一串算法中要學到的是語音算法都相當粗糙,太依賴于它們針對設計的語言,這些語言可以是英語,也可以是德語,這限制了它們的可用性。不過,如果與其他技術相結合,為了處理某種問題,語音匹配也可以是個有用的工具。
首先,我們需要從下面網(wǎng)址安裝語音分析插件 https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-phonetic.html ,為集群每個節(jié)點都安裝,然后重啟每個節(jié)點。
安裝后,我們可以創(chuàng)建一個自定義分析器,嘗試使用其中一個語音標記過濾器:
PUT /my_index{"settings": {"analysis": {"filter": {"dbl_metaphone": { #1"type": "phonetic","encoder": "double_metaphone"}},"analyzer": {"dbl_metaphone": {"tokenizer": "standard","filter": "dbl_metaphone" #2}}}}}#1 首先,配置自定義 phonetic 語音標記過濾器以及 double_metaphone 編碼器。
#2 然后在自定義分析器中使用自定義的標記過濾器。
現(xiàn)在使用 analyze API 對其測試:
GET /my_index/_analyze?analyzer=dbl_metaphoneSmith SmytheSmith 和 Smythe 在同一位置各生成兩個標記:SM0 和 XMT。分析 John、Jon 和 Johnnie 會都生成兩個標記 JN 和 AN,但 Jonathon 會生成標記 JN0N 和 ANTN。
語音分析器和其他分析器的使用方式類似,首先為字段設置映射,然后在對數(shù)據(jù)建立索引:
PUT /my_index/_mapping/my_type{"properties": {"name": {"type": "string","fields": {"phonetic": { #1"type": "string","analyzer": "dbl_metaphone"}}}}}PUT /my_index/my_type/1{"name": "John Smith"}PUT /my_index/my_type/2{"name": "Jonnie Smythe"}#1 name.phonetic 字段使用自定義的 dbl_metaphone 分析器。
用 match 查詢來搜索:
GET /my_index/my_type/_search{"query": {"match": {"name.phonetic": {"query": "Jahnnie Smeeth","operator": "and"}}}}這個查詢同時返回兩個文檔,這樣表明語音查詢是有多粗糙。使用語音算法進行評分通常也沒有多大意義。使用語音算法的目的不在于提高精度,而在于提高召回,擴大撒網(wǎng)范圍從而獲得任何可能匹配的文檔。
通常將語音算法的結果作為其他算法或計算機的輸入信息,要比直接將結果給人使用要靠譜得多。
參考
elastic.co: Typoes and Mispelings
轉載于:https://www.cnblogs.com/richaaaard/p/5282630.html
總結
以上是生活随笔為你收集整理的ElasticSearch 2 (26) - 语言处理系列之打字或拼写错误的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Atitit. atiJavaExCon
- 下一篇: Scalaz(32)- Free :li