相關性搜索簡介——常規技術與應用


原文地址:相關性搜索簡介——常規技術與應用

參考資料:《信息檢索導論》 // 《Lucene in action, 2ed》 & code // Lucene 8.11 Doc // 《Practical Apache Lucene 8》 // 《相關性搜索》 & code // 《精通特征工程》 //

《Lucene in action, 2ed》基於老版 Lucene 3.0(2021 年 Lucene 已經發展到 8.xx,歷史版本可參考 Lucene Change log),但依舊可以使用這本書了解 Lucene 和信息檢索的基本概念

本文是我對搜索技術與應用學習的總結,包括搜索的底層數據結構與基礎算法。機器學習在搜索中的應用將在其他博文中介紹。閱讀本文需要有一定的算法基礎和搜索經驗,最好使用過 ElasticSearch、Solr 或者 Lucene 等搜索相關工具

相關性搜索

相關性是一種改進用戶搜索結果的實踐,它在用戶體驗的具體上下文中滿足用戶的信息需求,同時平衡排名業務需求的影響

隨着時代的發展,我們被海量的信息所包圍,快速有效的獲取信息是非常重要的研究課題。現階段,相關性搜索是我們獲取信息的重要技術手段。信息檢索(Information Retrieval, IR)是相關性搜索的技術基礎

相關性搜索有其自身的復雜性,因為不同場景下的相關性有不同的意義,而且搜索引擎除了滿足用戶相關性需求外也要滿足業務需求,所以搜索業務需要技術人員和業務人員一起努力實現

下面是不同場景下對搜索的要求

  1. Web 搜索中為了剔除虛假網站,Google 開發出了 PageRank 等算法,用網頁之間的相關性剔除惡意網頁
  2. Amazon 等電商網站沒有虛假網站的問題,但為了盈利,搜索引擎需要考慮其他方面的因素,例如清理庫存等。從用戶的角度來說,除了相關性,物美價廉是電商搜索的默認因素
    1. 租房搜索網站還需要考慮距離、學校、房間數等因素
  3. 對於百科、空間、博客相關網站的搜索工具,過濾過激、暴力和違法等信息也是非常重要的功能
  4. 專家搜索。在法律、醫療和研究等領域,專業詞語之間的轉換需要專業的知識
  5. 其他領域。圖片、歌曲、視頻的相關性搜索也有各自的技術特點。結合深度學習實現多媒體的搜索也是相關性檢索的重要應用場景

構造相關性搜索工具

graph LR A[搜集信息和需求] B[設計搜索應用] C[部署監控與改進] A-->B B-->C
  1. 搜集信息和需求
    1. 理解用戶及信息需求;搜集必要的搜索信息,例如文檔、位置、價格等等
      1. 領域專家與專業用戶的建議非常重要。反饋與內容管理是搜索引擎進行改進的核心驅動
    2. 明確業務需求,免費還是付費等
  2. 設計搜索應用
    1. 實用搜索引擎有多種改善搜索體驗的工具,例如返回搜索文檔時返回部分高亮文檔,用於告知用戶為什么文檔會命中;搜索界面 UI 簡單易用,提供一些額外的功能,比如拼寫糾正、搜索提示等
  3. 部署監控與改進
    1. 保留用戶搜索行為日志,便於后續分析改進搜索策略。行為日志包括但不限於用戶搜索詞、搜索結果、頁面停留時間、轉化率、用戶黏度和翻頁信息等
      1. 監控數據可以用戶機器學習的素材,使用統計方式提升搜索體驗
    2. 其他

檢索的技術基礎

graph TB A[search tech] B[正/倒排索引] C[搜索模型] D[bool/向量/概率] E[特征提取] F[分詞] G[語義提取] H[TFIDF] I[非文本] J[音頻<br/>Parsons code] K[視頻語義] L[特征擴展] M[同義詞] O[文本特征] P[地理位置] A-->B A-->C C-->D A-->E E-->O O-->F O-->G O-->H E-->I I-->J I-->K E-->L L-->M I-->P

倒(正)排索引

相關知識請參考其他書籍或 ES 官方簡介正排索引是與倒排索引相關的概念

倒排索引中的 token 還有很多其他屬性,比如:文檔頻率、詞頻、詞位置、詞偏移量、負載數據、存儲字段、文檔值等,這些屬性可以用於相關性分析

搜索只能搜索到被索引的詞項(Term),所以文檔的檢索過程會深刻影響搜索結果。比如過濾了 stopword 的搜索引擎如果不做其他處理即使添加相關文章也搜索不到 to be or not to be ,此時應該添加短語提取模型

相關性排序

檢索到的文檔展現給用戶時是有順序的,排序使用的是相關性。ElasticSearch (ES) 中的 relevancy explains,是對檢索結果得分的詳細說明,是底層打分細節,在分析檢索結果時非常有用

搜索理論模型

  1. 布爾查詢,沒有匹配得分。文檔要么匹配要么不匹配查詢語句。細節可以參考 Lucene 布爾查詢。Lucene 的布爾查詢提供了 must(+)、should 和 must_not(-) 三種形式,例如 black -cat +dog
  2. 向量模型,查詢語句和文檔都被轉換為高維向量,查詢結果是兩個向量的距離,比如余弦距離。向量模型常用於查找相似項,因為推薦和搜索息息相關,所以向量模型在推薦系統中也比較常見
  3. 概率模型,計算查詢語句和文檔的相似度,使用概率度量(常基於貝葉斯)。典型的概率模型是 BM25

文本特征化

特征是原始數據的數值表示。在機器學習流程中,特征是數據和模型之間的紐帶,正確的特征可以減輕構建模型的難度,從而使機器學習流程輸出更高質量的結果

上面對特征和機器學習之間關系的描述也適用於相關性搜索,文本特征化是一切搜索的前提。搜索研發很大一部分工作是在做特征提取,發現與構造特征。相關性搜索中常見的特征處理方法有詞袋、N 元詞袋等。基於朴素詞袋的方法無法提取詞語之間的語義,而且如果不做過濾,簡單的詞袋會充斥着大量的無效單詞(比如 stopword)和海量沒有實際意義的短語,造成存儲與計算資源的浪費

分詞

分詞是用來提取語義而非單詞的

分詞一般同時用在查詢語句和待查資料。分詞不是簡單的提取單詞,而是將真實語義轉化為 token,比較常用的技術手段有標准化、差異化、詞干分析和語音分析等。例如 apples 標准化為 apple,甚至直接轉化為詞干 appl。不少應用場景會同時使用多種形式的分析器處理同一個字段(ES 中支持同一個字段使用多個分析器:multi-filed mapping ),從而獲得更為豐富的語義 token

  1. 分隔符處理。例如:縮略詞:I.B.M -> IBM、N.E.W->new enginerring week;不同類型的電話號碼分詞
  2. 獲得同義詞的語義 & 為專指性建模。例如 dog 可以轉化為 beagle、poodle
  3. 分詞應用場景的多樣性:數值、日期、地理位置、圖像、形狀、質地和聲音等等
    1. 簡單的音頻分詞可以直接將音頻轉換為U、D 和 R三種類型的文本序列(Parsons code),U/D/R分別表示當前音相比於前置音高/低/不變。結合 N 元詞袋等方法可實現搜索
短語和詞位置 & PhraseQuery & shingle

單詞的相對位置有時是重要的信息。有些短語不能隨意把單詞拆開作為獨立的單詞進行使用,比如禮服鞋 dress shoes ,如果將短語拆開查詢,返回的頁面可能包含女士服裝,這個一般不是用戶需要的。Lucene 提供了 PhraseQuery 接口用於查詢單詞間有位置關系的短語

ES 提供了 shingle 過濾器,可以個性化生成 n-grams 短語 token

有效的語義 token

為提升詞袋法的有效性,可以使用不同的方法處理原始數據,比如 TF·IDF、詞干提取、word2vec、短語搭配提取(Collocations)等。這里需要注意,不相鄰的單詞也有可能組成有效的短語

以統計方法(包括機器學習)為例,用於訓練短語提取模型的語料庫越豐富,模型的短語提取能力越強。當然,由專業領域語料庫訓練出的模型在其他場景的表現就不一定好。隨着時代的發展,常用短語也在不斷的變化,算法與語料庫的持續更新也是必需的

為提取有效的語義 token,使用不同算法與工具處理相同文本是常用的手段

TF·IDF

不同屬性不同位置的 token 其重要性一般不同,TF·IDF 是用於描述 token 顯著性的一種手段

  1. TF 是單詞在文檔中出現的頻率,出現的越多說明越重要。但對 the、a 等 stopword 無效,所以需要 IDF 降權
  2. IDF 是逆文檔頻率,在大部分文檔中都出現的單詞,其重要性有所降低。IDF 用於描述單詞的罕見程度

朴素的 TFIDF 也有一定的缺點,比如出現在文章標題處的單詞和出現在引文中的單詞,其權值不應該相同,所以各搜索引擎對 TFIDF 做了一定的修正,比如 Lucene 中所使用的 Okapi BM25

  1. 真實的 TF 對評分的影響一般不是線性的,常常被抑制,比如使用 \(sqrt(TF)\)
  2. 文章的長度也應該考慮在內,長文檔出現更多單詞的概率也更大
  3. 相同的單詞出現在文章不同位置分數不同

使用搜索引擎時因為查詢語句較短,所以查詢語句一般不使用 TF 來評價單詞的重要性。搜索引擎一般會提供方式來提升(boost)某些單詞的顯著性

調試搜索結果

graph TB A[概念] B[查全/查准率] C[tools] D[ES/Solr] E[DSL->query Str] F[explain] G[signal] H[boost<br/>shingle] I[best/most<br/>field] J[詞/字段為中心] K[白化象<br/>認知偏差] L[cross field] M[dis max] N[聚合字段<br/>copy to] O[字段同步] P[組合查詢] Q[boost 分層] R[bool<br/>fun score] S[相關性反饋] T[個性化搜索與推薦] A-->B A-->J A-->G J-->K K-->L K-->M G-->P J-->I C-->D D-->E D-->F G-->H K-->N L-->O H-->Q P-->R A-->S

下面的示例源自《相關性搜索》,細節請參考原書

查全率(Recall) & 查准率(Precision)

查全率和查准率有一定的相互作用,相同的算法一個指標的提升有可能會伴隨着另一個指標的下降

  1. 查全率:查詢結果集中返回的相關性文檔占總相關文檔的百分比
  2. 查准率:查詢結果集中具有相關性的文檔在結果集中的百分比

以水果為例,如果單以顏色(黃、綠、紅)作為條件查詢蘋果,那么查全率幾乎是 100%,但櫻桃、橙子、梨、芒果和榴蓮等水果也會混入到結果集中,查准率就比較低。查詢時添加額外的條件可以改變查全率和查准率,比如大小、口感和種屬等

使用朴素詞袋算法進行文本查詢可以獲得較高的查准率,但查全率就會比較低,因為只有精確匹配的文檔才會返回。所以常使用詞干提取、小寫轉換、關鍵字文件等手段對查詢語句和待查文本進行處理從而提高查全率。這從另一個角度說明分析不是簡單的將單詞轉化為 token,而應該是將語義和用戶意圖轉換為 token

ES DSL 與 Query String

ES 提供了一些接口,可以將 ES DSL 查詢轉換為底層 Lucene 使用的查詢語句,示例如下:

GET /tmdb/movie/_validate/query?explain
{
  "query": {
    "multi_match": {
      "query": "basketball with cartoon aliens",
      "fields": ["title^10", "overview"] # 同時搜索標題和簡介,並提升標題重要程度
    }
  }
}

下面是上面調用返回的 Lucene 查詢語句,可以結合 Lucene 布爾查詢 進行分析

"+((overview:basketball overview:with overview:cartoon overview:aliens) | (title:basketball title:with title:cartoon title:aliens)^10.0) #*:*"

從 Lucene 查詢語句可知,單詞 with 是一個非常重要的搜索因子,這是不合理的,從 ES 的返回結果也可以看出:

Num	Relevance Score		Movie Title
1	85.56929		    Aliens
2	73.71077		    The Basketball Diaries
...
9	45.221096		    Friends with Benefits

通過分析,可以修改默認分詞器,從而過濾 stopword。ES 不支持直接在已有索引上修改分析器,一般需要先創建一個新的索引,配置好 mapping 后再 _reindex,細節請參考這里

相關性計算細節

使用查詢接口時如果置 explain 為 true,ES 將返回相關性計算細節,如下所示:

GET /tmdbenglish/_search
{
  "explain": true,
  "size": 100,
  "_source": ["title"],
  "query": { ... }
}

下面是打分過程的部分片段,從 explain 中可以看到 TF 和 IDF 等因素的計算細節

"_explanation" : {
	"value" : 12.882349,
	"description" : "max of:",
	"details" : ...
}

信號建模

A signal is any component of the relevance-scoring calculation corresponding to meaningful, measurable user or business information

一些數據如果不做處理可能不利於搜索引擎索引。保存西方人名的關系型數據庫常將 first/middle/last name 存儲在不同的字段中,直接使用這類數據建立索引將割裂人名各部分的相關性,降低搜索精度

ES 與相關性建模

Elasticsearch has no concept of inner objects. Therefore, it flattens object hierarchies into a simple list of field names and values

相關性建模是搜索引擎和關系數據庫一個非常重要的區別,后者常需要保證字段與字段、表與表之間數據的獨立性,而前者需要建立相關性,將有一定關聯的數據放在一起。例如搜索引擎常合並字段,在為原始數據創建索引時新增或者直接將原始數據的多個分立字段合並為一個新字段,形成一個新的可用信號

ElasticSearch(ES)默認會修改原始 JSON 文檔中內嵌對象的存儲格式,細節請參考 Nested field type。ES 默認會將 JSON 的數組類型扁平化從而加強部分字段的相關性(ES 提供配置方式修改默認數據存儲格式),不過某些場景下會降低部分字段的相關性,具體使用需要看應用場景

boosting

多字段搜索時部分字段應該賦予更高的排序權重,ES 提供了 boosting 方式,方便用戶指定字段的排序權重

shingle

shingle 是 ES 提供的 token filter,可以生成 n-grams 短語 token,方便實現個性化短語查詢

ES 支持為同一個字段使用不同的分析器,所以同一個字段可以同時使用常規分析器和 n-gram 分析器。使用不同分析器的字段會生成不同名稱的索引項,檢索時指定索引項名稱即可,細節請參考 ES:Multi-fields with multiple analyzers

Term/Field Centric

ES 支持詞為中心(term-centric)和字段為中心(field-centric)的兩種搜索排序方式,參考 ES 官網 或者這里以詞為中心的搜索更關注搜索詞而不是內容

ES 中的 multi_match 方法默認使用 best_field 實現 field-centric 查詢,另一種 field-centric 查詢方法是 most_field;term-centric 常用查詢方法是 cross_field。這三種查詢方式的區別可以參考下面的 Lucene 語法。除上面介紹的三種查詢方式, multi match 還支持 phrase/phrase_prefix/bool_prefix

# field-centric
(name:Best name:Laptop ) | (description:Best description:Laptop) # best_field, max(x,y...)
(name:Best name:Laptop ) + (description:Best description:Laptop) # most_field, avg(x,y...)

# term-centric,注意 cross 的表現方式
(name:Best description:Best) + (name:Laptop description:Laptop) # cross_field, avg(x,y...)

不同搜索場景需要使用不同的相關性排序方式,搜索電影的時候用戶可能只想搜索到明確的電影名、演員或者導演,這個時候用 best field 模型比較合適;論文或者網頁搜索的時候用戶更關心返回結果和搜索內容的相關性,此時 most field 可能比較合適;部分場景可以結合 best/most field 模型,比如電商搜索等

best_field 一般只計算最佳字段與搜索內容的相關性得分,ES 支持使用 tie_breaker 加入非最佳字段與搜索內容相關性得分的計算。下面公式假設 title 是最佳匹配字段,最終返回評分同時考慮了最佳字段和其他字段的相關性,只不過非最佳字段的重要程度被 tie_breaker 降權

\[score =S_{title }+ { tie\_breaker } \times(S_{overview}+S_{cast.name}+S_{directors.name}) \]

如果 tie_breaker 被置為 1.0,那么 best_field 就十分類似 most_field

白化象 & 認知偏差

白化象問題(albino elephant problem)是遇見這個問題的工程師對問題描述時舉的例子,后來就用例子來表示這類問題。如下圖所示,從直覺上看本應得高分的右側文檔卻和左側文檔得到了一樣的分數。這是以字段以中心查詢方式固有的缺陷,以字段為中心的查詢方式獨立計算文檔中不同字段的得分,沒有從整個文檔的角度計算相關性

認知偏差被很多中文書籍直譯為信號沖突(signal discordance)。認知偏差是搜索引擎技術實現和用戶對搜索引擎功能認識之間的差別。以論文搜索為例,搜索引擎內部常將論文的標題、簡介、正文等不同部分作為不同的字段分別進行處理,然而在部分用戶的搜索意識里,論文的標題、簡介和正文應該一同處理。因為認知偏差的存在,很多時候搜索引擎會返回一些讓人感覺莫名其妙的搜索結果

Disjunction Max Query

disjunction max query 也被稱為 dis_max 查詢,可以部分解決白化象問題。其中 dis 表示分離(或者離散),max 表示最大,從字面上來講 dis_max 查詢是分別使用子查詢語句查詢文檔,返回分值最高的子查詢分數作為最終分數。示例可以參考這里

下面查詢語句會使用兩條查詢分別查找文檔,使用最大子查詢值作為最終文檔評分。如果將 dis_max 修改為 bool,那么文檔的分數將整合兩條語句的分數作為最終文檔評分

GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "term": { "title": "Quick pets" } },
        { "term": { "body": "Quick pets" } }
      ],
      "tie_breaker": 0.7
    }
  }
}

如果多個字段的查詢語句相同,使用 multi_match 並設置 type 為 best_field 可實現 dis_max 查詢並簡化查詢語句

以詞為中心的搜索

以詞為中心的搜索方式可以部分解決認知偏差和白化象問題。以詞為中心的搜索場景下,每個詞在計算相關性得分時會考慮所有字段

字段同步

字段同步是以詞為中心的查詢方式相比於字段查詢方式的一個缺陷

以詞為中心的查詢方法會使用一個 token 查詢多個字段,不同字段可能有不同的分析器,查詢不同字段具體應該以哪個字段的分析器為准?可以考慮不同字段遍歷一邊所有分析器,這樣時間復雜度就是 \(O(m*n)\),其中 m 表示字段的個數,n 表示所有字段存在的分析器種類個數。\(O(n^2)\) 時間復雜度的算法會極大的影響系統性能,且不同字段使用不同的分析器也會讓文檔的評分變得復雜,所以 ES 使用了最簡單的方式,所有查詢使用默認的分析器,這個現象稱為字段同步

自定義聚合字段

自定義聚合字段就是將多個字段合並成一個字段,在 ES 或者 Solr 中自定義聚合字段也被稱為拷貝字段(Copy field)。拷貝字段雖然造成數據的冗余,但解決了白化象問題

ES 中有關自定義聚合字段的功能可以參考官網 copy_to 。ES 6 之前 ES 默認為每一條記錄添加了一個組合字段 _all,系統自動將其他字段合並到這個字段中,ES 6 之后 _all棄用

合理的對相似字段進行聚合可以減少一些問題,且可以簡化搜索。《相關性搜索》第六章給出了一個例子,將電影中導演的名字和演員的名稱進行了分組與聚合,部分解決了以字段為中心的白化象問題

cross fields

cross fields 方法也有字段同步的問題,如果不同字段之間沒有通用的解析器,查詢可能會報錯。以詞為中心的搜索在 ES 的 multi_match 中通過設置 type 為 corss_field 即可實現。

組合查詢(嚴格&寬松)

利用 ES 提供的聚合查詢接口對同一個查詢語句使用不同的查詢方式,最終綜合查詢結果來解決相關問題

以下面查詢語句為例,第二個 multi_match 用於提升查全率,而第一個 multi_match 用於提升查准率。第二條查詢語句使用了非常寬松貪婪的查詢條件,只要有對應單詞的文檔就會被返回;第一條查詢語句相對比較嚴格,使用了二元 token,匹配的文檔會獲得更高的分數

{"query": {
    "bool": {
      "should": [{
          "multi_match": {
            "query": "usersSearch",
            "fields": ["directors.name.bigramed", "cast.name.bigramed"],
            "type": "cross_fields"}
        },{
          "multi_match": {
            "query": "usersSearch",
            "fields": ["overview", "title", "directors.name", "cast.name"],
            "type": "cross_fields"
}}]}}}

組合查詢也有自身的缺陷,一旦寬松查詢有着比嚴格查詢更顯著的信號,那么查詢結果不會盡如人意。可以使用 boost 減少寬松語句的打分權重,細節可以參考這篇文章

查詢解析器

Lucene 提供了支持 bool 查詢語法的查詢解析器,例如:

(name:Best description:Best) + (name:Laptop description:Laptop)

ES 中使用 query_string 支持 Lucene 的查詢解析器,合理的使用 Lucene 查詢語句可以實現以詞為中心的查詢。ES 版本迭代較快,query_string 在不同版本的功能區別可能較大,比如 ES 6 以后就直接移除了 use_dis_max 屬性,所以 ES6 之前可以使用查詢解析器實現 dismax 查詢,ES6 之后就不行了

評分調整

查詢的排名的優先級在不同的場景下不同,以電影搜索為例:精確匹配 > 部分精確 + 熱度 > 某些特殊字段的匹配 > 其他字段的匹配

新聞搜索應給出時事要聞、餐廳搜索一般把距離較近的餐館放在前面,不同場景對搜索結果有不同的要求,所以搜索也需要一定的定制化,需要對相關性搜索的評分做一些調整

一個簡單的評分調整方式是使用上面介紹的組合查詢:基准(寬松)查詢 + 放大(嚴格)查詢。使用寬松和嚴格的組合查詢方式有個問題,嚴格查詢語句的 boost 值應該設置為多少?這其實是個無法回答的問題,所以盡量還是不要使用這類方式

boost & boost 分層

boost 是最基礎的評分調整手段,即為不同的字段賦予不同的權值,ES 中 bool 查詢是支持 boost 的,其他支持 boost 的查詢可以參考 ES 官網

為不同重要程度的信號賦予有明確層級的 boost 分值,可以很方便區分信號之間的重要程度。比如將最重要的信號 boost 設置為 1000;次要的設置為 100;其他的設置為 10 或者更低

function score

使用 function_score 提升符合一定要求記錄的評分,ES 對 function_score 的支持可以參考官網。使用 function score 方法可以為滿足要求的記錄評分乘以一個 boost 數值,從而提升或者降低相關度。搜索時事新聞時可以使用 function score 根據新聞日期修改文檔得分

function score 提供了現成的工具可以實現按照指定字段屬性的權值衰減,比如按照記錄的時間以指數的形式衰減其搜索評分。細節可參考《相關性搜索》第 7.4.7 小節

filter

使用 filter 可以直接過濾掉滿足要求的記錄

精確匹配

用戶查詢語句精確匹配字段時可以考慮將相關記錄放在返回結果前(給一個相當大 的 boost 值)。以用戶查詢演員名稱而言,精確匹配之后可以按照電影上映時間逆序返回相關電影。為實現精確匹配,我們需要使用一些工具

基於 SENTINEL

在某些 token 的前后插入標記,插入過程可以使用自定義 ES 插件,搜索時使用短語搜索工具,比如 phrase。插入標記的字段在查詢時也需要攜帶標記,比如:SENTINEL_BEG query-string SENTINEL_END

基於 function score

使用自定義的評分過程增加精確匹配的分值

constant score

精確匹配時可以忽略 TFIDF 值,此時可以使用 constant score 查詢

使用已有評分信息

已經上映的電影有用戶的打分,可以考慮直接使用這個分數來修改記錄的查詢得分,此時可以利用 ES 中的 field_value_factor 。其他可用的評分信息還有票房收入、人氣指數和頁面訪問量等等

相關性反饋

相關性反饋通過與搜索用戶的交互來改善用戶的搜索結果和搜索體驗,可用方式如下:

  1. 向用戶解釋查詢與匹配過程,幫助用戶更好的理解搜索結果。例如可以使用 ES 提供的 highlight 實現關系信息的高亮
  2. 糾正用戶的輸入錯誤,在用戶搜索的同時提供一些服務
    1. 實時搜索(使用 match_phrase_prefix 在輸入的同時展示搜索結果);搜索補全與搜索建議(可以利用 ES 提供的 Suggests 工具)
  3. 向用戶推薦多樣化結果,比如其他類型的相關搜索
  4. 向用戶展示搜索結果的分布情況,便於用戶過濾搜索結果
  5. 其他

語義和個性化搜索

個性化搜索會結合用戶的個性與特點返回符合用戶口味的搜索結果,個性化搜索已經開始有了推薦的意味;語義搜索通過分析用戶的意圖,而不是簡單的使用搜索詞

個性化搜索需要結合用戶的歷史行為,這個分析過程和大數據息息相關,本文不做深入介紹。以用戶年齡為例,同齡人有一定的相似性,某個年齡段用戶搜索時可以將同年齡段其他用戶喜歡的產品放在前面。結合協同過濾等推薦算法可以進一步改進搜索結果

給物料文檔建立索引時可以將其受眾信息一同加入到索引中,用戶查詢時系統自動添加用戶信息到查詢語句中,如此便可以實現產品與用戶之間的關聯

構建語義的方法

同義詞 / 機器學習

推薦是廣義的搜索

其他內容

使用 ES 進行實驗

為了簡化操作我們可以使用 ES7 的機器學習模塊導入數據或者直接使用 Dev Tools 上傳,請參考這里,但實驗數據比較大時 Dev Tools 上傳不方便。《相關性搜索》所使用的 代碼倉庫 內容相對比較老,可以下載並使用 anaconda2 的控制台窗口進行實驗。書籍倉庫中的代碼需要做一些修改才能在新版本 ES7 中執行,比如發起 HTTP 請求時如果 http body 中有數據,需要顯式說明 body 數據類型:

resp = requests.post("http://localhost:9200/_bulk", 
                     data=bulkMovies,
                     headers={"Content-Type":"application/json"}) # 需要手動添加
print resp # 查看返回結果

Kibana 返回的數據類型是 JSON 格式,為方便查看,可以使用這個網站將 hits 數組轉換為 CSV

Lucene 簡介

Lucene 是 ES/Solr 底層的存儲與檢索工具,類似於 InnoDB 和 MySQL 的關系。Lucene 除了提供查詢接口,也提供了配置與分析接口,比如運行時索引合並與修改、歷史查詢日志等。Lucene 4 的組合和結構圖可以參考 Apache Lucene 4

概念

  1. IR,information retrieval
  2. Search Query(搜索分析),將用戶輸入的單詞或者短語轉化為 Lucene 可識別的語法
  3. boosting,Lucene 中的 boosting 可以用在索引和查詢文檔時,通過給不同的域設置不同的分數(加權)以提升或降低其在查詢中的相關性。boosting 可以應用在文檔和字段上,以提升某些文檔和字段的相關性
  4. 其他

Lucene 的特點

  1. Lucene 可不保存原始數據。插入 lucene 索引文檔並建立索引的字段,其值域數據並不一定需要寫入索引中。單獨存放的值域數據在查詢時也會被匹配,但默認情況下返回值是不包含這類值域數據的
  2. Lucene 中的數據沒有統一的模式。加入同一個索引的文檔可以有不同的字段
  3. Lucene 支持增量索引。部分搜索庫添加新文檔后需要重新索引所有文檔,lucene 利用段和段的周期性合並實現增量索引
  4. 靈活的配置。相同文檔的不同字段可以有不同的屬性,比如不同的分詞器、是否保存、是否索引等選項

文件檢索過程

  1. 提取,從數據源提取為文檔,比如從數據庫中、從 Web 等。只有數據轉化為文檔才能被搜索引擎索引,ES 使用 JSON 格式的文檔
  2. 充實,將有助於相關性檢索的信息加入到文檔中,例如添加額外的描述
    1. 清理,例如糾正拼寫錯誤、去重、去除 Markup 標記(HTML、XML 標簽)等
    2. 強化(提取詞干、添加同義詞、近音詞、類別屬性等),分類/聚類、情感分析等。一些場景下的索引會提取文檔中特定的內容到指定的字段,比如日期、地址、標題、摘要等等
    3. 合並,填充缺失的一些信息等
  3. 分析,將文檔轉化為可匹配的 token
    1. 任何數據類型都可以被分詞從而生成 token,例如圖片、音頻、視頻等
    2. 搜索引擎在匹配 token 的時候需要逐字節對應,所以分析過程非常重要
      1. 字符過濾。以 HTML 頁面為例,字符過濾將去除頁面中無用的 tag ;有些語言的重音去重操作
      2. 分詞處理。引入同義詞,去除詞根等等
      3. token 過濾。所有單詞小寫,去除 stop words
  4. 索引,生成倒排索引

Lucene 3 索引文件格式

Lucene 底層使用多個索引段(Segment)管理屬於同一個索引的索引數據,每個段文件格式與功能相同,只是存儲着不同的文檔的索引信息,下圖展示了同一個索引下的兩個索引段 0/1,統一個索引段中的文件有着相同的文件前綴。查詢時 Lucene 會訪問所有段目錄,綜合不同段的結果后返回最終結果

索引段默認使用多個不同文件分別保存不同類型的數據,例如項向量(.tvf)、域數據(.fdx & .fdt)、歸一化信息(.nrm)、項頻率(.frq)和項位置(.prx)等,細節可以參考 Lucene 3 官網文檔

段文件(Segments File,.gen & _N 文件)是 Lucene 管理索引段的工具,查詢時 Lucene 會先打開段文件查看哪些段和段文件是有效的,然后對有效的段進行查詢。存在的段目錄並不一定都是有效的,Lucene 在做段合並(merge)時未完成合並的臨時段目錄是無效的。存儲到磁盤上的索引文件是不允許修改的,所以 Lucene 只會在合並時完成修改並在合並完成后刪除舊的段目錄

按照 Lucene 的設計,檢索與刪除可以同時進行,檢索在刪除動作將所有內容提交到磁盤前使用的依舊是老數據。未刷新的 IndexReader 對象可能無法感知文件的刪除動作,所以盡量不要使用持久性質的 IndexReader 對象(隨着Lucene 的發展,IndexReader 可能會自動刷新可用段)

Lucene 索引可以使用 luke 可視化,正排和倒排索引的細節本文不做介紹,可以參考其他資料,例如 Lucene 倒排索引簡述 / 《Practical Apache Lucene 8》,注意不同版本的 Lucene 索引文件有一定的區別。為了提高數據的查詢速度,Lucene 使用了一些不常見的數據結構和算法,比如 Burst-trie 等

新文檔與索引段

每次新增文檔時 Lucene 都會創建一個新段來保存新增文檔的索引信息,Lucene 通過頻繁創建/合並索引段實現增量索引功能

頻繁創建/合並段需要打開大量文件描述符,但大部分系統對單一進程可打開的文件描述符個數有限制。Lucene 可以通過頻繁合並小的索引段減少索引段的個數,但這會增加系統負擔。為減少頻繁創建/合並索引段造成的系統負擔,Lucene 支持復合索引段,即將分立索引段中的多個文件打包成一個文件(.cfs),這樣創建/合並索引時每個線程只需使用三個文件描述符。Lucene 支持在運行時切換分立索引段和符合索引段

刪除文檔

為提升系統性能,對文檔執行但刪除操作並不會實時觸發磁盤操作,Lucene 會先標記文檔已被刪除,后續索引合並時才會回收對應磁盤空間。Lucene 的標記刪除特性非常適合系統的橫向擴展,增加計算機器就可以提升整個系統的查詢吞吐量,而存儲機器在磁盤 IO 沒有占滿的前提下可以不用擴展

標記/合並 刪除

Lucene 使用了和 InnoDB/Redis 類似的刪除策略,刪除數據時數據會被打上刪除標簽,此時其依舊占用磁盤空間。被刪除數據所占用的磁盤空間只有在段合並時才會被釋放。Lucene 3 的索引段使用 .del 文件保存標記刪除信息

部分接口

查詢

Lucene 提供了很多種類的查詢接口,比如:

  1. TermQuery,通過 term(項)進行查詢
  2. TermRangeQuery/NumericRangeQuery/PrefixQuery
  3. BooleanQuery,組合查詢。Lucene 3 組合查詢示例如下
  4. PhraseQuery,短語查詢,查詢的短語其組成單詞不一定相鄰,PhraseQuery 可以設置短語間單詞距離的規則
  5. WildcardQuery,通配符查詢;FuzzyQuery,編輯距離查詢
  6. QueryParser,表達式查詢:+pubdate:[20100101 TO 20101231] Java AND (Lucene OR Apache) 。QueryParser 會將查詢語句轉換為上面介紹的查詢對象然后進行查詢,有相關工具可以查詢轉換過程於細節。Lucene 3.1 時當前對象提供的查詢語法不能覆蓋所有 Lucene 提供的查詢功能
  7. 其他

優化/評分細節

Lucene 提供了 optimize 接口可以用於索引優化,比如指定索引段段個數。索引合並的過程會占用大量的 CPU 和 IO 資源,合並期間需要不小於已有數據 3 倍大小的磁盤空間。索引合並可以提升檢索速度但不會提升索引速度

Lucene 提供 explain 接口用於顯示文檔的評分細節


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM