一文詳談 20 多種 RAG 優化方法
大規模語言模型(LLMs)已經成爲我們生活和工作的一部分,它們以驚人的多功能性和智能化改變了我們與信息的互動方式。
然而,儘管它們的能力令人印象深刻,但它們並非無懈可擊。這些模型可能會產生誤導性的 “幻覺”,依賴的信息可能過時,處理特定知識時效率不高,缺乏專業領域的深度洞察,同時在推理能力上也有所欠缺。
在現實世界的應用中,數據需要不斷更新以反映最新的發展,生成的內容必須是透明可追溯的,以便控制成本並保護數據隱私。因此,簡單依賴於這些 “黑盒” 模型是不夠的,我們需要更精細的解決方案來滿足這些複雜的需求。正是在這樣的背景下,檢索增強生成技術(Retrieval-Augmented Generation,RAG)應時而生,成爲 LLM 時代的一大趨勢。
從上圖可以看出基礎 RAG 架構的流程是十分簡單的,其最大的特點是數據單向流通。因此搭建一個這樣的系統是十分快捷的,但離真正能投入到生產環境中使用還是很遠的。爲了增強原有架構的文檔召回率和系統魯棒性,其優化路徑大致有兩條:增加召回管道和增加反饋機制。增加召回管道就是查詢變換(子查詢、rag-fusion)、混合檢索這類通過多路召回來最大化召回率的優化方法;增加反饋機制就是 rerank、後退提示、self-rag 這類基於原始結果進行優化來最大化準確率的優化方法。
通過這兩條路徑,原來的 RAG 架構的數據和信息不再是單向流通,而是多向且並行流通。
爲了給讀者重呈現一個清晰的優化思路,本文將按照數據流動的方向從文本預處理、文本分塊、嵌入、檢索和生成等環節依次介紹各個優化方法。
文本數據預處理
不管 RAG 系統結構怎樣複雜,由於其數據驅動的特性,高信噪比的數據仍然是十分重要的。在檢索之前對原始數據的優化包括以下方法:
-
實體解析:消除實體和術語的歧義以實現一致的引用。例如,將 “LLM”、“大語言模型” 和“大模型”標準化爲通用術語。
-
文檔劃分:合理地劃分不同主題的文檔。不同主題的文檔是集中在一處還是分散在多處?如果人類都不能輕鬆地判斷出需要查閱哪個文檔才能回答提問,那麼檢索系統也無法做到。
-
數據增強:使用同義詞、釋義甚至其它語言的翻譯來增加知識庫的多樣性。
-
處理特殊數據:例如時間敏感數據,對於經常更新的主題,實施一種機制來使過時的文檔失效或更新。
-
增加元數據:增加內容摘要、時間戳、用戶可能提出的問題等附加信息來豐富知識庫。
文本分塊
通常被檢索知識庫中的數據量是遠超於 LLM 所能接受的輸入長度的,因此合理的分塊(Chunking)應儘可能做到在不超出 LLM 輸入長度限制的情況下,保證塊之間的差異性和塊內部的一致性。當然這是最理想的狀態,在實際應用中,可能有一篇文檔像散文一般,不同段落之間沒有明顯內容區別,段落內部又特別地 “散”,整篇文檔又特別地長。當然我們不可能先驗地將文本按內容完美分塊,畢竟下游還有 LLM 這樣智能的模型可以發揮其“智慧” 來回答用戶問題,我們提供的塊不過是“提示”。但我們仍然還是需要儘可能提供有用的信息給 LLM,而不是提供無關的信息分散其注意力。因此,可以採用以下高級的分塊方法:
-
句分割:使用 NLTK 或者 spaCy 庫提供的句子分割功能,主流開發框架如 langchain 都有集成。
-
遞歸分割:通過重複地應用分塊規則來遞歸地分解文本。例如,在 langchain 中會先通過段落換行符(
\n\n
)進行分割。然後檢查這些塊的大小,如果大小不超過一定閾值,則該塊被保留。對於超過閾值的塊,使用單換行符(\n
)再次分割。以此類推,不斷根據塊大小更新更小的分塊規則(如空格,句號)。這種方法可以靈活地調整塊的大小。例如,對於文本中的密集信息部分,可能需要更細的分割來捕捉細節;而對於信息較少的部分,則可以使用更大的塊。 -
語義分割:通過計算向量化後的文本的相似度來進行語義層面的分割。
-
特殊結構分割:針對特定結構化內容(例如 Markdown、LaTex、JSON 等)的專門分割器。這些分割器特別設計來處理這些類型的文檔,以確保正確地保留其結構。
分塊還有一個因素比較重要,就是塊的大小。除了嵌入模型,文檔的類型和用戶查詢的長度及複雜性也是決定分塊大小的重要因素。處理長篇文章或書籍時,較大的分塊有助於保留更多的上下文和主題連貫性;而對於社交媒體帖子,較小的分塊可能更適合捕捉每個帖子的精確語義。如果用戶的查詢通常是簡短和具體的,較小的分塊可能更爲合適;相反,如果查詢較爲複雜,可能需要更大的分塊。實際場景中,我們可能還是需要不斷實驗調整,在一些測試中,128 大小的分塊往往是最佳選擇,在無從下手時,可以從這個大小作爲起點進行測試。
嵌入
接下來就是數據處理的最後一個環節,相當於數據的類型轉換,即對文本數據使用嵌入(Embedding)模型進行向量化(Vectorization),以便於在檢索階段使用向量檢索(Vector Retrieval)。嵌入階段有以下幾個可以優化的點:
-
儘量使用動態嵌入:動態嵌入相較於靜態嵌入更能夠處理一詞多義的情況。例如:我買了一張光盤”,這裏 “光盤” 指的是具體的圓形盤片,而在 “光盤行動” 中,“光盤”則指的是把餐盤裏的食物喫光,是一種倡導節約的行爲。語義完全不一樣的詞使用靜態嵌入其向量是固定的。相比之下,引入自注意力機制的模型,如 BERT,能夠提供動態的詞義理解。這意味着它可以根據上下文動態地調整詞義,使得同一個詞在不同語境下有不同的向量表示。
-
微調嵌入:大多數嵌入模型都是在通用語料上進行訓練的,有些項目爲了讓模型對垂直領域的詞彙有更好的理解,會對嵌入模型進行微調。使模型能夠對垂直領域詞彙和通用詞彙一視同仁,不被分散注意力。
-
混合嵌入:對用戶問題和知識庫文本使用不同的嵌入模型。
查詢優化
在實際環境中,可能由於用戶的表述多樣性亦或是模糊的,導致在檢索階段召回率和準確率較低,這時就需要對查詢做一個優化,能夠規範和豐富查詢所包含的信息,便於在系統中檢索到與用戶相關的文檔。對查詢的優化方法有以下幾個:
-
查詢重寫:通過提示 LLM 或者使用專門的 “問題重寫器”(通常是經過微調的小型 Transformer)來對用戶的問題進行改寫。
-
後退提示:提示 LLM 提出一個關於高層次概念或原則的抽象通用問題(稱之爲 “後退” 問題)。後退問題的抽象程度需要根據特定任務進行調整。最終後退問題和原始問題一起進行檢索。例如,對於問題 “Estella Leopold 在 1954 年 8 月至 11 月期間上了哪所學校?” 這個問題很難直接解決,因爲有時間範圍的詳細限制。在這兩種情況下,提出一個後退問題 “Estella Leopold 的教育經歷怎麼樣的?” 則有助於系統的檢索。
-
Follow Up Questions:使用 LLM 針對歷史對話和當前問題生成一個獨立問題。這個方法主要針對以下情況:a. 後續問題建立在前一次對話的基礎上,或引用了前一次談話。例如,如果用戶先問 “我在意大利能做什麼”,然後問“那裏有什麼類型的食物”——如果只嵌入“那裏有哪種類型的食物“,LLM 就不知道“那裏” 在哪裏。b. 嵌入整個對話(或最後 k 條消息)。如果後續問題與之前的對話完全無關,那麼它可能會返回完全無關的結果,從而在生成過程中分散 LLM 的注意力。
-
HyDE:用 LLM 生成一個 “假設” 答案,將其和問題一起進行檢索。HyDE 的核心思想是接收用戶提問後,先讓 LLM 在沒有外部知識的情況下生成一個假設性的回覆。然後,將這個假設性回覆和原始查詢一起用於向量檢索。假設回覆可能包含虛假信息,但蘊含着 LLM 認爲相關的信息和文檔模式,有助於在知識庫中尋找類似的文檔。
-
多問題查詢:基於原始問題,提示 LLM 從不同角度產生多個新問題或者子問題,並使用每一個新問題進行檢索,在後續階段使用 RRF 或者 rerank 合併來自不同問題的檢索結果。例如,對於原始問題:誰最近贏得了總冠軍,紅襪隊還是愛國者隊?,可以生成兩個子問題:a. 紅襪者隊上一次贏得總冠軍是什麼時候?b. 愛國者隊上一次贏得總冠軍是什麼時候?
檢索
檢索(Retrieval)最終的目標就是獲取最相關的文檔或者保證最相關的文檔在獲取的文檔列表中存在。爲了達成這個目標,該環節有以下幾個優化方法:
-
上下文壓縮:當文檔塊過大時,可能包含太多不相關的信息,傳遞這樣的文檔塊可能導致更昂貴的 LLM 調用和更差的響應。上下文壓縮的思想就是通過 LLM 的幫助根據上下文對單個文檔內容進行壓縮,或者對返回結果進行一定程度的過濾僅返回相關信息。
-
句子窗口搜索:相反,文檔文塊太小會導致上下文的缺失。其中一種解決方案就是窗口搜索,該方法的核心思想是當提問匹配好文檔塊後,將該文檔塊周圍的塊作爲上下文一併交給 LLM 進行輸出,來增加 LLM 對文檔上下文的理解。
-
父文檔搜索:父文檔搜索也是一種很相似的解決方案,父文檔搜索先將文檔分爲尺寸更大的主文檔,再把主文檔分割爲更短的子文檔兩個層級,用戶問題會與子文檔匹配,然後將該子文檔所屬的主文檔發送給 LLM。
-
自動合併:自動合併是在父文檔搜索上更進一步的複雜解決方案。同樣地,我們先對文檔進行結構切割,比如將文檔按三層樹狀結構進行切割,頂層節點的塊大小爲 1024,中間層的塊大小爲 512,底層的葉子節點的塊大小爲 128。而在檢索時只拿葉子節點和問題進行匹配,當某個父節點下的多數葉子節點都與問題匹配則將該父節點作爲結果返回。
-
混合檢索:RAG 系統從根本上來說是作爲開放域、基於自然語言的問答系統。爲了獲得開放式用戶查詢的高事實召回率,概括和聚焦應用場景以選擇合適的檢索模式或組合至關重要。在大多數文本搜索場景中,主要目標是確保最相關的結果出現在候選列表中。混合檢索通過混合多個檢索方法來實現不同檢索技術的協同作用從而能夠最大化事實召回率。例如,可以採用向量檢索 + 關鍵詞檢索的組合來構建 RAG 系統的檢索模塊。
-
路由機制:當建立了多個針對不同數據類型和查詢需求的索引後,例如,可能有一個索引專門處理摘要類問題,另一個專門應對直接尋求具體答案的問題,還有一個專門針對需要考慮時間因素的問題。這時就需要使用路由機制來選擇最合適的索引進行數據檢索,從而提升檢索質量和響應速度。
-
使用 Agent:該方法就是使用 Agent 來決定應該採用什麼樣的檢索方法,從不同的檢索方法中選取一種或多種進行召回。同時組合方式也是靈活的,是垂直關係還是平行關係。例如:對於查詢 “最新上映的科幻電影推薦”,Agent 可能首先將其路由至專門處理當前熱點話題的索引,然後利用專注於娛樂和影視內容的索引來生成相關推薦。
檢索後處理
檢索後處理這個概念還是很寬泛的,是對檢索結果進行進一步的處理以便於後續 LLM 更好的生成,比較典型的就是重排序(Rerank)。向量檢索其實就是計算語義層面的相似性,但語義最相似並不總是代表最相關。重排模型通過對初始檢索結果進行更深入的相關性評估和排序,確保最終展示給用戶的結果更加符合其查詢意圖。實現重排序除了可以提示 LLM 進行重排,更多的是使用了專門的重排序模型(例如閉源的有 Cohere,開源有 BAAI 和 IBM 發佈的模型)。這些模型會考慮更多的特徵,如查詢意圖、詞彙的多重語義、用戶的歷史行爲和上下文信息,從而保證最相關的文檔排在結果列表的最前面。
生成
在生成(Generation)階段的優化更多的是考慮用戶體驗,有以下幾點可以供參考:
-
多輪對話:也就是帶聊天曆史的 RAG,以 AI 搜索爲例,明星產品 perplexity 就是支持多輪對話的,這樣用戶可以通過連續對話來深入瞭解解決某個問題。
-
增加追問機制:在 prompt 中加入 “如果無法從背景知識回答用戶的問題,則根據背景知識內容,對用戶進行追問,問題限制在 3 個以內”。這個機制並沒有什麼技術含量,主要依靠大模型的能力。不過大大改善了用戶體驗,用戶在多輪引導中逐步明確了自己的問題,從而能夠得到合適的答案。
-
prompt 優化:RAG 系統中的 prompt 應明確指出回答僅基於搜索結果,不要添加任何其他信息。例如,可以設置 prompt:“你是一名智能客服。你的目標是提供準確的信息,並儘可能幫助提問者解決問題。你應保持友善,但不要過於囉嗦。請根據提供的上下文信息,在不考慮已有知識的情況下,回答相關查詢。” 當然也可以根據場景需要,適當讓模型的回答融入一些主觀性或其對知識的理解。此外,使用 Few-shot 的方法指導 LLM 如何利用檢索到的知識,也是提升 LLM 生成內容質量的有效方法。
-
用戶反饋循環:基於現實世界用戶的反饋不斷更新數據庫,標記它們的真實性。
結語
以上這些方法就是針對基礎 RAG 在各個環節的優化方法,在實際開發過程中並不是所有方法都是有效的,不同問題有不同的解決方案,針對應用場景選擇合適的優化方法組合才能最大限度發揮 RAG 的作用。
這裏再貼一個 Github 鏈接:https://github.com/Jenqyang/LLM-Powered-RAG-System
收集了一些優秀的 RAG 項目以及開發框架,歡迎 Star~✨
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/xMsPh8qicRD395vjFR250A