構建 LLM 應用:搜索 - 檢索(第五部分)
作者:Vipra Singh
編譯:ronghuaiyang
導讀
在系列博客中,我們通過檢索增強生成(RAG)應用的視角來學習大規模語言模型(LLM)。
讓我們開始在 RAG 應用程序中探索尋找相關數據的旅程。
當用戶輸入查詢時,過程包括對用戶查詢進行分詞,並使用與嵌入原始數據相同的模型進行嵌入。接着,根據與用戶查詢的相似度,從知識庫中提取相關的信息塊。
本篇博客將全面深入地探討搜索過程的複雜細節。下圖具體展示了 “搜索” 在 RAG 流程中的位置。
通過大語言模型(LLM)的上下文窗口提供領域特定信息,檢索增強生成(Retrieval Augmented Generation)降低了產生虛假信息的可能性。
- 介紹 =====
讓我們考慮一個常見場景:使用大語言模型(LLM)開發客戶支持聊天機器人。通常,團隊擁有大量產品文檔,其中包含大量關於其產品、常見問題及使用案例的非結構化數據。
這些數據通過一個稱爲 “分塊” 的過程被分解。數據被拆分後,每一塊都被賦予一個唯一的標識符,並嵌入到向量數據庫中的高維空間裏。這一過程利用先進的自然語言處理技術來理解每個塊的上下文和語義意義。
當客戶的提問進來時,LLM 運用檢索算法迅速識別並從向量數據庫中提取最相關的數據塊。這種檢索基於查詢與數據塊之間的語義相似度,而不僅僅是關鍵詞匹配。
上圖展示瞭如何使用提示模板(如圖中所示的‘4’)結合搜索與檢索生成最終的大語言模型(LLM)提示上下文。上述視圖呈現了搜索與檢索在 LLM 應用中最簡化的形式:文檔被切分爲多個塊,這些塊被嵌入到向量存儲中,隨後搜索與檢索過程依託此上下文框架來塑造 LLM 的輸出。
此方法帶來了多方面優勢。首先,它顯著減少了 LLM 處理大量數據所需的時間和計算資源,因爲模型只需與相關塊交互,無需遍歷整個文檔。
其次,它支持數據庫的實時更新。隨着產品文檔的變化,向量數據庫中的相應塊可以輕鬆更新,確保聊天機器人提供的信息始終最新。
最後,通過聚焦於語義相關的塊,LLM 能夠提供更精確且上下文貼切的回覆,從而提升客戶滿意度。
1.1. 搜索 & 檢索中的問題
儘管搜索與檢索方法極大提升了 LLM 的效率與準確性,但仍存在一些潛在的陷阱。早期識別這些問題可以防止它們影響用戶體驗。
其中一個挑戰出現在用戶輸入的查詢與向量存儲中的任何塊都不緊密匹配時。系統試圖在乾草堆中找到一根針,但實際上卻一無所獲。這種匹配缺失的情況,通常由獨特性或高度特定的查詢引起,可能導致系統不得不依賴可獲得的 “最相似” 塊——那些並非完全相關的塊。
隨之而來的是 LLM 的次優響應。由於 LLM 依賴塊的相關性來生成回覆,缺乏適當匹配可能導致輸出與用戶查詢僅有膚淺聯繫,甚至完全不相干。
LLM 給出的無關或質量欠佳的回覆會令用戶感到沮喪,降低他們的滿意度,並最終導致他們對整個系統和產品失去信任。
監控以下三個方面有助於預防這些問題的發生:
查詢密度(漂移):查詢密度指的是用戶查詢在向量存儲中的覆蓋程度。如果查詢密度發生顯著漂移,這表明我們的向量存儲可能未能涵蓋用戶查詢的全部範圍,從而導致緊密關聯塊的短缺。定期監控查詢密度使我們能夠發現這些缺口或不足之處。有了這些洞察,我們可以通過增加更多相關塊或優化現有塊來增強向量存儲,提高系統根據用戶查詢獲取數據的能力。
排名指標:這些指標評估搜索與檢索系統在選擇最相關塊方面的表現如何。如果排名指標顯示出性能下降,這表明系統區分相關與不相關塊的能力可能需要改進。
用戶反饋:鼓勵用戶提供有關 LLM 回覆質量和相關性的反饋,有助於衡量用戶滿意度並識別改進領域。定期分析這些反饋可以揭示模式和趨勢,進而根據需要調整您的應用程序。
1.2. 優化搜索 & 檢索
在您的 LLM 驅動型應用程序的整個生命週期中,從構建階段到後期製作,搜索與檢索過程的優化應當是一個持續不斷的努力。
在構建階段,應重視制定一套強有力的測試和評估策略。這種方法使我們能夠在早期發現問題並優化策略,爲系統奠定堅實的基礎。
重點需關注的領域包括:
-
分塊策略:評估信息在這一階段是如何被拆分和處理的,可以幫助發現性能改進的空間。
-
檢索性能:評估系統信息檢索的效能可以指示是否需要採用不同的工具或策略,比如上下文排序 (context ranking) 或 HYDE 技術。
發佈後,進入後期生產階段,優化工作應繼續進行。即使在啓動之後,通過明確的評估策略,我們也能主動識別出任何新出現的問題,並持續改進模型的表現。可以考慮的方法包括:
-
擴展知識庫:增加文檔資料能顯著提升系統的回覆質量。擴大的數據集使我們的 LLM 能夠提供更加準確和個性化的回覆。
-
細化分塊策略:進一步調整信息的拆分和處理方式可以帶來顯著的改善。
-
增強上下文理解:融入額外的 “上下文評估” 步驟,幫助系統將最相關的上下文整合進 LLM 的回覆中。
關於這些以及其他持續優化策略的具體細節將在課程的後續部分詳述。記住,我們的目標是創建一個不僅能滿足用戶在啓動時的需求,而且能隨時間與用戶共同發展的系統。
- 搜索的類型 ========
確實,我們必須認識到向量數據庫並非搜索的萬能藥——它們非常擅長_語義_搜索,但在許多情況下,傳統的關鍵詞搜索能產生更相關的結果,從而提高用戶滿意度。這是爲什麼呢?很大程度上是因爲基於餘弦相似度等指標的排名會導致相似度得分較高的結果排在含有特定輸入關鍵詞的部分匹配結果之上,從而降低了對終端用戶的關聯性。
然而,純粹的關鍵詞搜索也有其侷限性——如果用戶輸入的詞語在語義上與存儲的數據相似(但不完全相同),可能有用且相關的結果就不會被返回。鑑於這種權衡,實際應用場景中的搜索與檢索需要關鍵詞搜索和向量搜索的結合,其中向量數據庫構成了關鍵組成部分(因爲它們存儲了嵌入,使得語義相似性搜索成爲可能,並能擴展至非常龐大的數據集)。
總結上述要點:
-
關鍵詞搜索:當用戶_確切知道_他們在尋找什麼,並期望搜索結果與他們的搜索詞中的確切短語相匹配時,能找到相關且有用的結果。不需要向量數據庫。
-
向量搜索:當用戶_不確定_他們確切尋找的內容時,能找到相關結果。需要向量數據庫。
-
混合(關鍵詞 + 向量)搜索:通常結合全文關鍵詞搜索和向量搜索的候選結果,並使用交叉編碼器模型重新排序。需要文檔數據庫和向量數據庫兩者。
這一概念可以通過下圖有效地可視化展示:
2.1. 語義搜索
語義搜索旨在通過理解搜索查詢的內容來提升搜索的準確性。與僅基於詞彙匹配查找文檔的傳統搜索引擎相比,語義搜索還能夠找到同義詞。
2.1.1. 背景
語義搜索背後的理念是將語料庫中的所有條目——不論是句子、段落還是文檔——嵌入到一個向量空間中。在搜索時,查詢也會被嵌入到同樣的向量空間裏,然後從語料庫中找出最接近的嵌入向量。這些條目應該與查詢有很高的語義重疊,意味着它們在意義上與查詢最爲接近,從而爲用戶提供最相關的搜索結果。
2.2. 對稱性 vs. 非對稱性語義搜索
我們的設置中一個關鍵區別在於_對稱性語義搜索_與_非對稱性語義搜索_:
-
對稱性語義搜索中,我們的查詢與語料庫中的條目長度大致相同,包含的信息量也相仿。一個例子是搜索類似的問題:查詢例如可能是 “如何在線學習 Python?” 而我們想要找到諸如 “如何在網絡上學習 Python?” 這樣的條目。對於對稱任務,理論上我們可以將查詢與語料庫中的條目互換位置來進行搜索。
-
非對稱性語義搜索中,我們通常有一個簡短的查詢(如一個問題或幾個關鍵詞),而希望找到一個較長的段落作爲答案。例如,查詢可能是 “Python 是什麼”,我們希望找到這樣的段落:“Python 是一種解釋型、高級、通用的編程語言。Python 的設計哲學……”。對於非對稱任務,交換查詢和語料庫中的條目通常是沒有意義的。
我們需爲不同類型的任務選擇合適的模型。
適合對稱性語義搜索的模型:預訓練的句子嵌入模型
適合非對稱性語義搜索的模型:預訓練的 MS MARCO 模型
- 檢索算法 =======
3.1. 相似度搜索 & 最大化邊際相關性算法(MMR)
在文檔檢索的場景中,大多數方法會使用相似度度量,如餘弦相似度、歐幾里得距離或點積。這些方法都會返回與查詢或問題最相似的文檔。
但是,如果我們希望檢索到的相似文檔之間也具有多樣性怎麼辦?這時,**最大邊際相關性(Maximum Marginal Relevance, MMR)**就派上了用場。
MMR 的目標是在決定返回哪些文檔時,考慮到檢索到的文檔相互之間的相似度。理論上,這樣我們應該能得到一組全面且多樣化的文檔。
在無監督學習的情境下,假設我們的最終關鍵詞短語排名如下:優質產品, 優秀產品, 不錯的產品, 卓越產品, 易安裝, 優良界面, 輕便等
但這種方法存在一個問題,像優質產品, 不錯的產品, 卓越產品
這樣的短語含義相似,都定義了產品的同一屬性,且排名靠前。假定我們只有空間展示 5 個關鍵詞短語,在這種情況下,我們不希望展示所有這些類似的短語。
我們希望充分利用這個有限的空間,使得關鍵詞短語所傳達的關於文檔的信息足夠多樣化。不應該讓相似類型的短語佔據整個空間,而是讓用戶能看到關於文檔的各種信息。
爲了實現這一目標,可以採取以下兩步策略:
-
使用餘弦相似度移除冗餘短語:首先,通過計算短語間的餘弦相似度,找出並移除那些極度相似、表達重複信息的短語,以減少冗餘。
-
使用 MMR 重新排序關鍵詞短語:接下來,應用 MMR 算法對剩餘的關鍵詞短語進行重新排序。MMR 不僅考慮單個短語與查詢的相似度(即相關性),還會考慮短語間差異性(即多樣性)。它通過平衡每個候選短語與已選集合的平均相似度(以避免選擇過於相似的短語)和與查詢的直接相關性來挑選下一個最合適的短語加入結果集,以此達到既相關又多樣性的目的。
通過這樣的策略,我們能在有限展示空間內最大化信息的多樣性和全面性,提供給用戶更爲豐富和均衡的文檔概覽。
上述內容提到了兩種常用的檢索方法。其他諸如_多查詢檢索、長上下文重排序、多向量檢索器、父文檔檢索器、自我查詢、時間加權向量存儲檢索_等,則屬於一些更高級的檢索策略,我們將在另外的博文中深入介紹。
現在,讓我們來探討一下下方的檢索與重排序流程,並瞭解這一流程是如何優化結果的。
- 檢索 & 重排序 ===========
在語義搜索中,我們已經展示瞭如何使用 Sentence Transformers 爲查詢、句子和段落計算嵌入,以及如何利用這些嵌入進行語義搜索。
對於複雜的搜索任務,比如問答檢索,通過採用**檢索與重排序(Retrieve & Re-Rank)**的方法,可以顯著提升搜索效果。
4.1. 檢索 & 重排序管道
一個運作良好的信息檢索 / 問答檢索管道如下。本文提供了所有組件並進行了詳細解釋:
面對搜索查詢,我們首先使用一個檢索系統,該系統會檢索出一大列表示,比如 100 個可能相關的命中項。爲了進行檢索,我們可以使用詞法搜索,例如藉助 ElasticSearch,或者我們可以使用帶有雙編碼器的密集檢索。
然而,檢索系統可能返回與搜索查詢關聯性不強的文檔。因此,在第二階段,我們採用基於交叉編碼器的重排序器來針對給定的搜索查詢對所有候選者的相關性進行打分。
輸出結果將是一個排序後的命中項列表,我們可以將其呈現給用戶。
4.2. 檢索:雙向編碼器
詞法搜索會在我們的文檔集中尋找查詢詞彙的字面匹配。它無法識別同義詞、縮寫或拼寫變體。相比之下,語義搜索(或密集檢索)將搜索查詢編碼到向量空間中,並檢索在向量空間中與其接近的文檔嵌入。
語義搜索克服了詞法搜索的侷限性,能夠識別同義詞和縮略詞
4.3. 重排序器:交叉編碼器
檢索器必須對包含數百萬條目的大型文檔集保持高效。但是,它可能返回與查詢無關的候選文檔。
基於交叉編碼器的重排序器可以顯著提升最終用戶得到的結果。查詢和一個可能的文檔同時傳遞給 Transformer 網絡,然後網絡輸出一個介於 0 和 1 之間的單一分數,表示文檔對於給定查詢的相關程度。
交叉編碼器的優點在於其更高的性能,因爲它們在查詢和文檔之間執行了注意力機制。
對數千或數百萬個(查詢,文檔)對進行評分會相當緩慢。因此,我們使用檢索器來生成例如 100 個可能的候選集,然後由交叉編碼器對這些候選進行重新排序。
4.4. 例子腳本
-
retrieve_rerank_simple_wikipedia.ipynb [Colab 版本]: 此腳本使用規模較小的簡明英語維基百科作爲文檔集合,以回答用戶的查詢或問題。首先,我們將所有維基百科文章拆分爲段落,並使用雙編碼器對它們進行編碼。當輸入新的查詢或問題時,同樣使用雙編碼器對其進行編碼,然後根據餘弦相似度最高的段落進行檢索(參見語義搜索)。接下來,檢索到的候選段落由交叉編碼器重排序器評分,交叉編碼器給出的前 5 個最高分段落將展示給用戶。
-
in_document_search_crossencoder.py: 如果只有少量段落,則不需要檢索階段。例如,當我們想在單個文檔內部執行搜索時就會出現這種情況。在此示例中,取關於歐洲的維基百科文章並將其拆分爲段落。然後,使用交叉編碼器重排序器對搜索查詢 / 問題及所有段落進行評分。與查詢最相關的段落將被返回。
4.5. 預訓練雙向編碼器(檢索)
雙編碼器獨立地爲我們的段落和搜索查詢生成嵌入。我們可以這樣使用它:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('model_name')
docs = ["My first paragraph. That contains information", "Python is a programming language."]
document_embeddings = model.encode(docs)
query = "What is Python?"
query_embedding = model.encode(query)
我們提供了基於以下數據集的預訓練模型:
- MS MARCO: 來自 Bing 搜索引擎的 50 萬條真實用戶查詢。參見 MS MARCO 模型
4.6. 預訓練交叉編碼器(重排器)
對於預訓練模型,我們可以參考: MS MARCO Cross-Encoders
- 信息檢索的評估 ==========
信息檢索(IR)系統的評估對於做出明智的設計決策至關重要。從搜索到推薦,評估指標對於理解檢索中什麼有效、什麼無效是至關重要的。
IR 系統的評估指標可以分爲_兩大類_:_在線_或_離線_度量。
在線度量 是在 IR 系統處於_在線運行_狀態下,實際使用過程中收集的。這些指標考慮了用戶交互,比如用戶是否點擊了 Netflix 推薦的節目,或是從電子郵件廣告中點擊了某個特定鏈接(點擊率或 CTR)。在線度量有很多,但都與某種形式的用戶交互有關。
離線度量 則是在部署新 IR 系統之前,在隔離環境中測量的。這些指標考察的是當使用系統檢索項目時,是否返回了一組特定的_相關_結果。
評估指標可以進一步細分爲離線或在線指標。離線指標又可以細分爲無序感知或有序感知,我們很快將會解釋這兩者。
通常會同時使用離線和在線指標來衡量其 IR 系統的性能。然而,這一過程通常先從使用離線指標預測系統_部署前_的性能開始。
我們將重點介紹一些最有用且流行的離線指標:
-
召回率 @K(Recall@K)
-
平均倒數排名(MRR, Mean Reciprocal Rank)
-
平均精度 @K(MAP@K, Mean Average Precision@K)
-
歸一化折損累積增益(NDCG@K, Normalized Discounted Cumulative Gain)
這些指標看似簡單,卻能爲 IR 系統的性能提供極其寶貴的洞察。
我們可以在不同的評估階段使用這些指標中的一個或多個。在開發 Spotify 的播客搜索功能時,召回率 @K(使用 K=1)被用於 “評估批次” 的訓練過程中;而訓練完成後,兩項指標 — 使用 K=30 的_召回率 @K_ 和 _MRR_— 則被應用於一個大得多的評估集上。
目前,需理解的關鍵點是 Spotify 能夠在向客戶部署任何內容之前預測系統性能。這使得他們能夠實施成功的 A/B 測試,並顯著提升了播客的參與度。
對於這些指標,我們還有兩個子分類:有序感知(order-aware)和無序感知(order-unaware)。這指的是結果順序是否影響指標得分。如果順序影響得分,則該指標爲有序感知;否則,爲無序感知。
5.1. 例子
在整篇文章中,我們將使用一個_非常_小的數據集,包含八張圖片。實際上,這個數量很可能會達到數百萬乃至更多。
8 張圖的可能的排序結果
如果我們搜索 “盒子裏的貓”,我們可能會得到類似上面的結果。數字代表了 IR 系統預測的每張圖片的相關性排名。其他的查詢將產生不同的結果順序。
示例查詢及排名,其中高亮顯示了實際相關的結果。
我們可以看到,編號爲 2、4、5 和 7 的結果是_真正相關_的結果。其他結果則_不相關_,因爲它們顯示的是沒有盒子的貓、沒有貓的盒子,或者是一隻狗。
5.2. 實際 vs. 預測
在評估 IR 系統的性能時,我們將比較實際與預測的情況,其中:
-
實際情況指的是數據集中每個項目的真標籤。如果項目與查詢相關,它們被視爲_正例_(_p_);如果不相關,則視爲_反例_(_n_)。
-
預測情況是由 IR 系統返回的_預測_標籤。如果返回了一個項目,那麼它被預測爲_正例_(_p^_);如果沒有返回,則預測爲_反例_(_n_^__)。
基於這些實際和預測條件,我們創建了一組輸出,從中計算出所有的離線指標。這些輸出包括真正例、假正例、真反例和假反例。
正例_結果關注的是 IR 系統返回的內容。鑑於我們的數據集,我們讓 IR 系統使用 “盒子裏的貓” 查詢返回_兩個_項目。如果返回的是_實際相關_的結果,這就是一個_真正例(pp^);如果返回的是不相關的結果,則是一個_假正例_(_np^_)。
對於_反例_結果,我們需要關注 IR 系統_未返回_的內容。讓我們查詢兩個結果。任何_相關_但_未被返回_的項目都是_假反例_(_pn^_)。那些_未被返回_的不相關項目則是_真反例_(_nn^_)。
考慮到以上所有內容,我們可以開始介紹第一個指標了。
5.3. Recall@K
召回率 @K 是最易解釋且最受歡迎的離線評估指標之一。它衡量了相對於整個數據集中存在的相關項目總數(pp^+pn^__),系統返回了多少相關項目(__pp^)。
這裏的 K 以及所有其他離線指標中的 K,指的是 IR 系統返回的項目數量。在我們的例子中,整個數據集中共有 N = 8 個項目,因此 K 可以是 [1 ,…, N] 之間的任意值。
通過召回率 @2,我們返回預測的最相關前 K = 2 個結果
當 K = 2 時,我們的_召回率 @2_ 得分是通過將返回的相關結果數量除以整個數據集中相關結果的總數來計算的。也就是說:
隨着 K 增大和返回項目範圍的擴大,召回率 @K 的得分也會提高。
隨着 K 的增加,召回率 @K 的得分會上升,這是因爲返回的正例(無論是真是假)數量增多。
我們可以在 Python 中輕鬆計算相同的召回率 @K 得分。爲此,我們將定義一個名爲 recall 的函數,該函數接受_實際情況_和_預測情況_的列表,以及 K 值,並返回召回率 @K 的得分。
# recall@k function
def recall(actual, predicted, k):
act_set = set(actual)
pred_set = set(predicted[:k])
result = round(len(act_set & pred_set) / float(len(act_set)), 2)
return result
利用這個方法,我們將複製包含八個圖像的數據集,其中_實際相關_的結果位於第 2、4、5 和 7 的排名位置。
actual = ["2", "4", "5", "7"]
predicted = ["1", "2", "3", "4", "5", "6", "7", "8"]
for k in range(1, 9):
print(f"Recall@{k} = {recall(actual, predicted, k)}")
輸出:
Recall@1 = 0.0
Recall@2 = 0.25
Recall@3 = 0.25
Recall@4 = 0.5
Recall@5 = 0.75
Recall@6 = 0.75
Recall@7 = 1.0
Recall@8 = 1.0
優缺點
召回率 @K 無疑是易於理解的評估指標之一。我們知道,完美的得分意味着所有相關項目都被返回了。我們也知道,較小的 k 值會讓 IR 系統更難在召回率 @K 上取得好成績。
儘管如此,使用_召回率 @K_ 也存在一些缺點。通過將 _K_ 增加到 _N_ 或接近 _N_,我們每次都能得到完美分數,因此單純依賴召回率 @K 可能會產生誤導。
另一個問題是,它是一個_無序感知指標_。這意味着如果我們使用召回率 @4,並在一個排名位置返回一個相關結果,我們將與在第四個排名位置返回相同結果獲得相同的得分。顯然,在更高的排名位置返回實際相關結果更好,但召回率 @K 無法反映這一點。
5.4. 平均倒數排名(MRR, Mean Reciprocal Rank)
平均倒數排名(MRR, Mean Reciprocal Rank)是一個_有序感知指標_,這意味着與召回率 @K 不同,它認爲在排名第一的位置返回一個實際相關的結果比在排名第四的位置返回得分更高。
MRR 的另一個特點是,它是基於多個查詢計算得出的。具體計算方式爲:
Q 是查詢的數量,q 是一個具體的查詢,而 rank-q 是查詢 q 的第一個_實際相關_結果的排名。我們將逐步解釋這個公式。
以我們之前的例子爲例,即用戶搜索 “盒子裏的貓”。我們再增加兩個查詢,這樣我們就有 Q=3。
我們在計算 MRR 得分時執行三個查詢。
我們爲每個查詢 q 計算排名倒數 1/rankq。對於第一個查詢,第一個實際相關的圖片在位置_二_被返回,因此排名倒數是 1/2。現在,讓我們計算所有查詢的排名倒數:
接下來,我們對所有查詢 q=[1,..., Q](例如,我們的三個查詢)的排名倒數求和:
由於我們計算的是平均倒數排名(MRR),我們必須通過將總倒數排名除以查詢數量 Q 來取平均值:
現在,讓我們將這個過程轉化爲 Python 代碼。我們將重現同樣的場景,其中 Q=3,並使用相同的_實際相關_結果。
# relevant results for query #1, #2, and #3
actual_relevant = [
[2, 4, 5, 7],
[1, 4, 5, 7],
[5, 8]
]
# number of queries
Q = len(actual_relevant)
# calculate the reciprocal of the first actual relevant rank
cumulative_reciprocal = 0
for i in range(Q):
first_result = actual_relevant[i][0]
reciprocal = 1 / first_result
cumulative_reciprocal += reciprocal
print(f"query #{i+1} = 1/{first_result} = {reciprocal}")
# calculate mrr
mrr = 1/Q * cumulative_reciprocal
# generate results
print("MRR =", round(mrr,2))
輸出 :
query #1 = 1/2 = 0.5
query #2 = 1/1 = 1.0
query #3 = 1/5 = 0.2
MRR = 0.57
正如預期的那樣,我們計算出了相同的 MRR 得分 0.57。
優缺點
MRR 有其獨特的一系列優點和缺點。它具有_有序感知_的特點,這對於像聊天機器人或問答系統這樣的應用場景來說是一個巨大的優勢,其中第一個相關結果的排名非常重要。
另一方面,我們只考慮了_第一個_相關項的排名,而不考慮其他。這意味着對於希望返回多個項的應用場景,如推薦系統或搜索引擎,MRR 並不是一個好的指標。例如,如果我們想向用戶推薦大約 10 件商品,我們會要求 IR 系統檢索 10 個項目。我們可能會在排名第一位返回一個實際相關的商品,而不再返回其他相關商品。十個中有九個是不相關的商品,這是一個糟糕的結果,但 MRR 會給出完美的 1.0 得分。
另一個較小的缺點是,相比召回率 @K 這樣的簡單指標,MRR 的可解釋性較差。然而,與其他許多評估指標相比,它的可解釋性仍然更強。
5.5. 平均精度均值(MAP, Mean Average Precision)
平均精度均值 @K(MAP@K, Mean Average Precision@K)是另一個流行的_有序感知_指標。乍一看,它的名字似乎有些奇怪,平均_的_均值?這是有道理的,我們保證。
計算 MAP@K 有幾個步驟。我們從另一個稱爲_精度 @K_ 的指標開始:
你可能會覺得這看起來與_召回率 @K_ 非常相似,確實如此!唯一的區別是我們在這裏將召回率中的 _pn^ _換成了_ np^_。這意味着我們現在只考慮返回項目中的實際相關和非相關結果。在使用_精度 @K_ 時,我們_不考慮_未返回的項。
K = 2 時,召回率 @K 和精度 @K 計算的區別。
讓我們回到 “盒子裏的貓” 的例子。我們將返回 K=2 個項,並計算精度 @2。
K=2 時,計算精度 @K
注意,在精度 @K 中,分母始終等於 K。現在我們已經得到了精度 @K 的值,接下來進行計算平均精度 @K(AP@K)的下一步:
注意,對於 AP@K,我們對 k=[1,..., K] 的所有值計算_精度 @k_ 的平均得分。這意味着對於 AP@5,我們計算 _k_=[1,2,3,4,5] 時的_精度 @k_。
我們之前沒有見過 rel-k 參數。這是一個_相關性_參數,對於 AP@K 而言,當第 k 個項相關時,它的值等於 1;不相關時,值爲 0。
我們迭代地使用 k=[1,...,K] 計算_精度 @k_ 和 _rel-k_。
由於我們對_精度 @k_ 和 _rel-k_ 進行乘法運算,因此我們只需考慮第 _k_ 個項相關的_精度 @k_。
在三個查詢 q = [1, …, Q] 下,所有相關項的精度 @k 和 rel_k 值。
每個單獨的 AP@Kq 計算都會爲每個查詢 q 產生一個單一的平均精度 @K(AP@K)得分。要獲取所有查詢的平均平均精度 @K(MAP@K)得分,我們只需除以查詢的數量 Q:
這樣我們就得到了最終的 MAP@K 得分爲 0.48。要用 Python 計算所有這些,我們編寫:
# initialize variables
actual = [
[2, 4, 5, 7],
[1, 4, 5, 7],
[5, 8]
]
Q = len(actual)
predicted = [1, 2, 3, 4, 5, 6, 7, 8]
k = 8
ap = []
# loop through and calculate AP for each query q
for q in range(Q):
ap_num = 0
# loop through k values
for x in range(k):
# calculate precision@k
act_set = set(actual[q])
pred_set = set(predicted[:x+1])
precision_at_k = len(act_set & pred_set) / (x+1)
# calculate rel_k values
if predicted[x] in actual[q]:
rel_k = 1
else:
rel_k = 0
# calculate numerator value for ap
ap_num += precision_at_k * rel_k
# now we calculate the AP value as the average of AP
# numerator values
ap_q = ap_num / len(actual[q])
print(f"AP@{k}_{q+1} = {round(ap_q,2)}")
ap.append(ap_q)
# now we take the mean of all ap values to get mAP
map_at_k = sum(ap) / Q
# generate results
print(f"mAP@{k} = {round(map_at_k, 2)}")
輸出 :
AP@8_1 = 0.54
AP@8_2 = 0.67
AP@8_3 = 0.23
mAP@8 = 0.48
得到了相同的 MAP@K 得分 0.48。
優缺點
MAP@K 是一個簡單的離線指標,允許我們考慮返回項的順序。這使得它非常適合那些我們期望返回多個相關項的應用場景。
MAP@K 的主要缺點是 rel-K 相關性參數是二進制的。我們必須將項目視爲相關或不相關。它不允許項目比其他項目稍微更相關或不太相關。
5.6. 歸一化折損累積增益(NDCG@K, Normalized Discounted Cumulative Gain)
歸一化的折損的累積的增益 @K(NDCG@K)是我們可以從幾個更簡單的指標推導出的另一個_有序感知指標_。從**累積增益**(_CG@K_)開始,計算如下:
這次的 rel-K 變量有所不同。它是一個相關性等級範圍,其中 0 是最不相關的,而某個較高的值是最相關的。等級的數量並不重要;在我們的例子中,我們使用 0→4 的範圍。
使用 rel_k,我們根據項目與特定查詢 q 的相關性對每個項進行排名。
讓我們嘗試將此應用到另一個例子中。我們使用與之前類似的八個圖像的數據集。圈起來的數字代表 IR 系統預測的排名,而菱形代表 rel-k 實際排名。
一個小型數據集,其中包含了預測排名(圓圈)和實際排名(菱形)。
爲了計算位置 K 處的累積增益(CG@K),我們對預測排名 K 之前的關聯性得分進行求和。當 K=2 時:
需要注意的是,CG@K 並不具備有序感知能力。如果我們交換圖片 1 和 2 的位置,儘管將更相關的項目放在首位,但在 _K_≥2 時,我們仍會得到相同的得分。
圖像 1 和 2 進行了交換
爲了解決這種缺乏有序感知的問題,我們修改指標創建 DCG@K,在公式中以 * log_2(1+k)* 的形式添加懲罰項:
現在,當我們計算 DCG@2 並交換前兩張圖片的位置時,我們得到的得分是不同的:
from math import log2 # initialize variables relevance = [0, 7, 2, 4, 6, 1, 4, 3] K = 8 dcg = 0 # loop through each item and calculate DCG for k in range(1, K+1): rel_k = relevance[k-1] # calculate DCG dcg += rel_k / log2(1 + k)
使用 query#1 時 DCG@K 得分隨着 K 的增加而增加
使用_有序感知_的 DCG@K 指標意味着,經過優選交換的結果會得到更好的得分。
不幸的是,DCG@K 得分很難解釋,因爲其範圍取決於我們爲數據選擇的 rel-k 範圍。我們使用歸一化 DCG@K(NDCG@K)指標來解決這個問題。
NDCG@K 是標準 NDCG 的一個特殊修改版本,它會截斷所有排名大於 K 的結果。這種修改在衡量搜索性能的應用案例中非常常見。
NDCG@K 使用 Ideal DCG@K(IDCG@K)排名對 DCG@K 進行歸一化。對於 IDCG@K,我們假設最相關的項目排名最高,並按相關性順序排列。
計算 IDCG@K 只需要重新排序分配的排名,並執行相同的 DCG@K 計算:
# sort items in 'relevance' from most relevant to less relevant ideal_relevance = sorted(relevance, reverse=True) print(ideal_relevance) idcg = 0 # as before, loop through each item and calculate *Ideal* DCG for k in range(1, K+1): rel_k = ideal_relevance[k-1] # calculate DCG idcg += rel_k / log2(1 + k)
隨着 K 增加,使用查詢 #1 的結果順序計算的 DCG@K 得分與 IDCG@K 得分的對比。
現在,我們只需要使用 IDCG@K 得分對 DCG@K 得分進行歸一化,就可以計算 NDCG@K 了:
dcg = 0 idcg = 0 for k in range(1, K+1): # calculate rel_k values rel_k = relevance[k-1] ideal_rel_k = ideal_relevance[k-1] # calculate dcg and idcg dcg += rel_k / log2(1 + k) idcg += ideal_rel_k / log2(1 + k) # calcualte ndcg ndcg = dcg / idcg
隨着 K 增加,通過使用 IDCG@K 對 DCG@K 進行歸一化來計算 NDCG@K 得分。
使用 NDCG@K,我們得到了一個更易解釋的結果 0.41,我們知道 1.0 是我們可以通過所有項目完美排名(例如,IDCG@K)獲得的_最好_的得分。
優缺點
NDCG@K 是評估 IR 系統,特別是網絡搜索引擎時最流行的離線指標之一。這是因爲 NDCG@K 優化了高度相關的文檔,具有_有序感知_的能力,並且易於解釋。
然而,NDCG@K 有一個顯著的缺點。我們不僅需要知道哪些項目與特定查詢相關,還需要知道每個項目是否比其他項目更相關或不太相關;數據需求更爲複雜。
適用於其他指標的數據示例(左)以及 NDCG@K 所需更復雜數據的示例(右)。
這些是最受歡迎的用於評估信息檢索系統的離線指標。單個指標可以很好地指示系統性能。爲了對檢索性能有更大的信心,我們可以使用多種指標,就像 Spotify 使用 recall@1、recall@30 和 MRR@30 一樣。
在 A/B 測試中,這些指標最好與在線指標結合使用,這是在將檢索系統部署到全局之前的下一步。然而,這些離線指標是任何檢索項目背後的基礎。
無論我們正在原型設計我們的第一個產品,還是評估 Google 搜索的最新迭代,使用這些指標評估我們的檢索系統將幫助我們部署儘可能最好的檢索系統。
結論
在這篇博文中,我們探討了檢索增強生成(RAG)應用中的搜索流程,特別強調了使用向量數據庫的語義搜索。它突出了諸如減少處理時間和實時更新等優勢。面臨的挑戰包括對獨特查詢響應不佳。預防問題的策略包括監控查詢密度和收集用戶反饋。優化工作應集中在構建和後期製作階段,包括擴展知識庫。博文還討論了搜索的類型、語義搜索以及檢索與重排管道。建議使用預訓練模型和諸如召回率 @K 等離線評估指標。總的來說,它強調了 RAG 應用中持續優化和全面評估的必要性。
英文原文:https://medium.com/@vipra_singh/building-llm-applications-retrieval-search-part-5-c83a7004037d
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Uap0-96dKD8xePuXrFO-MQ