Neil Zhu,簡書ID Not_GOD,University AI 創(chuàng)始人 & Chief Scientist,致力于推進世界人工智能化進程。制定并實施 UAI 中長期增長戰(zhàn)略和目標,帶領團隊快速成長為人工智能領域最專業(yè)的力量。
作為行業(yè)領導者,他和UAI一起在2014年創(chuàng)建了TASA(中國最早的人工智能社團), DL Center(深度學習知識中心全球價值網絡),AI growth(行業(yè)智庫培訓)等,為中國的人工智能人才建設輸送了大量的血液和養(yǎng)分。此外,他還參與或者舉辦過各類國際性的人工智能峰會和活動,產生了巨大的影響力,書寫了60萬字的人工智能精品技術內容,生產翻譯了全球第一本深度學習入門書《神經網絡與深度學習》,生產的內容被大量的專業(yè)垂直公眾號和媒體轉載與連載。曾經受邀為國內頂尖大學制定人工智能學習規(guī)劃和教授人工智能前沿課程,均受學生和老師好評。
理解“query then fetch”和“dfs query then fetch”
在上篇文章中,我們遇到一個返回查詢結果相當奇怪的情形。
上篇文章并沒有翻譯,請看原文。 :P
在此我們再給出那個查詢的代碼:
$ curl -XGET localhost:9200/startswith/test/_search?pretty -d '{
"query": {
"match_phrase_prefix": {
"title": {
"query": "d",
"max_expansions": 5
}
}
}
}' | grep title
"_score" : 1.0, "_source" : {"title":"drunk"}
"_score" : 0.30685282, "_source" : {"title":"dzone"}
"_score" : 0.30685282, "_source" : {"title":"data"}
"_score" : 0.30685282, "_source" : {"title":"drive"}
為何文檔“drunk”分數為1.0
,而其余的分數是0.3
?難道這些文檔不應該是相同的分數么,因為他們都同等地匹配了“d”。答案是肯定的,但是這個分數本身也有比較合理的地方。
相關性打分
ES使用的打分算法包含了稱之為“TF-IDF”的統計信息來幫助計算處于那個索引中的文檔的相關性。
TFIDF基本思想就是“一個項在文檔中出現的次數越多,那么這個文檔更加相關;但相關性會被這個項在整個文檔庫中的次數削弱”。
稀有項出現在相對少的文檔中,那么任何查詢匹配了一個稀有項的相關性就變得很高。相反,平常項到處都有,他們的相關性就低了。
當用戶執(zhí)行一個搜索時,ES面對一個有趣的困境。你的查詢需要找到所有相關的文檔,但是這些文檔分布在你的cluster中的任何數目的shard中。
每個shard是一個Lucene的索引,保存了自身的TF和DF統計信息。一個shard只知道在其自身中出現的次數,而非整個cluster。
但是相關算法使用了TF-IDF,它需要知道對于整個索引的而不是對每個shard的TF和DF么?
默認搜索類型:query then fetch
答案:是也不是。默認情形下,ES會使用一個稱之為Query then fetch
的搜索類型。它運作的方式如下:
- 發(fā)送查詢到每個shard
- 找到所有匹配的文檔,并使用本地的Term/Document Frequency信息進行打分
- 對結果構建一個優(yōu)先隊列(排序,標頁等)
- 返回關于結果的元數據到請求節(jié)點。注意,實際文檔還沒有發(fā)送,只是分數
- 來自所有shard的分數合并起來,并在請求節(jié)點上進行排序,文檔被按照查詢要求進行選擇
- 最終,實際文檔從他們各自所在的獨立的shard上檢索出來
- 結果被返回給用戶
這個系統一般是能夠良好地運作的。大多數情形下,你的索引有足夠的文檔來平滑Term/Document frequency統計信息。因此,盡管每個shard不一定擁有完整的關于整個cluster的frequency信息,結果仍然足夠好,因為fequency在每個地方基本上是類似的。
但是在我們開頭提起的那個查詢,默認搜索類型有時候會失敗。
dfs query then fetch
在上篇文章中,我們默認建立了一個索引,ES通常使用5個shard。接著插入了5個文檔進入索引,向ES發(fā)送請求返回相關結果和準確的分數。其結果并不是很公平,對吧?
這是由于默認的搜索類型導致的,每個shard僅僅包含一個或者兩個文檔(ES使用hash確保隨機分布)。當我們要求ES計算分數時候,每個shard僅僅擁有關于五個文檔的一個很窄的視角。所以分數是不準確的。
幸運的是,ES并沒有讓你無所適從。如果你遇到了這樣的打分偏離的情形,ES提供了一個稱為“DFS Query Then Fetch”。這個過程基本和Query Then Fetch類型,除了它執(zhí)行了一個預查詢來計算整體文檔的frequency。
- 預查詢每個shard,詢問Term和Document frequency
- 發(fā)送查詢到每隔shard
- 找到所有匹配的文檔,并使用全局的Term/Document Frequency信息進行打分
- 對結果構建一個優(yōu)先隊列(排序,標頁等)
- 返回關于結果的元數據到請求節(jié)點。注意,實際文檔還沒有發(fā)送,只是分數
- 來自所有shard的分數合并起來,并在請求節(jié)點上進行排序,文檔被按照查詢要求進行選擇
- 最終,實際文檔從他們各自所在的獨立的shard上檢索出來
- 結果被返回給用戶
如果我們使用這個新的搜索類型,那么獲得的結果更加合理了(這些都一樣的):
$ curl -XGET 'localhost:9200/startswith/test/_search?pretty=true&search_type=dfs_query_then_fetch' -d '{
"query": {
"match_phrase_prefix": {
"title": {
"query": "d",
"max_expansions": 5
}
}
}
}' | grep title
"_score" : 1.9162908, "_source" : {"title":"dzone"}
"_score" : 1.9162908, "_source" : {"title":"data"}
"_score" : 1.9162908, "_source" : {"title":"drunk"}
"_score" : 1.9162908, "_source" : {"title":"drive"}
結論
當然,更好準確性不是免費的。預查詢本身會有一個額外的在shard中的輪詢,這個當然會有性能上的問題(跟索引的大小,shard的數量,查詢的頻率等)。在大多數情形下,是沒有必要的,擁有足夠的數據可以解決這樣的問題。
但是有時候,你可能會遇到奇特的打分場景,在這些情況中,知道如何使用DFS query then fetch
去進行搜索執(zhí)行過程的微調還是有用的。