RAG系統中的“幕后英雄”:重排器如何提升信息檢索的精準度? 原創
在信息爆炸的時代,我們每天都在海量的數據中穿梭,試圖找到真正有價值的信息。而Retrieval Augmented Generation(RAG)系統的出現,就像是一盞明燈,照亮了我們在信息海洋中的前行之路。但你有沒有想過,RAG系統之所以能夠精準地為我們提供有用的信息,背后其實有一個關鍵的“把關人”——重排器(Reranker)。今天,就讓我們一起深入探索重排器的世界,看看它是如何在RAG系統中發揮著不可或缺的作用。
一、什么是RAG中的重排器?
想象一下,當你在搜索引擎中輸入一個關鍵詞,系統會在瞬間從龐大的數據庫中檢索出成千上萬條相關信息。但這些信息的質量參差不齊,有的可能與你的需求高度相關,有的則可能完全不沾邊。這就需要重排器登場了。
重排器就像是一個嚴格的質檢員,它會對初步檢索出來的文檔進行二次篩選和排序。在初步檢索階段,系統可能通過語義搜索或關鍵詞搜索等方法,快速地找到一批與查詢內容有一定關聯的文檔。但這些文檔就像是未經篩選的原材料,其中可能夾雜著許多無關緊要的信息。而重排器的任務,就是從這些文檔中挑選出最符合用戶查詢意圖的那部分,將它們放在最前面,從而提高搜索結果的質量。
舉個簡單的例子,假設你正在寫一篇關于“人工智能在醫療領域的應用”的論文,你通過RAG系統檢索相關資料。初步檢索可能給你找到了幾十篇論文、新聞報道和研究報告,這些內容可能涵蓋了人工智能在醫療影像診斷、疾病預測、藥物研發等多個方面的應用。但有些內容可能只是簡單地提到了人工智能和醫療這兩個詞,而并沒有深入探討它們之間的關系。重排器就會對這些文檔進行仔細分析,將那些真正詳細闡述了人工智能在醫療領域具體應用案例、技術原理和效果評估的文檔排在前面,讓你能夠更快地找到對你最有幫助的信息。
二、為什么要在RAG中使用重排器?
(一)減少“幻覺”現象
在RAG系統中,有一個常見的問題叫做“幻覺”(Hallucination)。簡單來說,就是系統生成了一些與事實不符或者毫無意義的回答。這通常是由于檢索到的文檔中包含了大量無關的信息,導致系統在生成回答時被誤導。而重排器可以有效地過濾掉這些無關文檔,就像在食材中挑出壞掉的部分一樣,從而減少“幻覺”現象的發生。
(二)節省成本
你可能會覺得,RAG系統檢索文檔的速度這么快,多檢索一些文檔也沒什么大不了的。但實際上,處理這些文檔需要消耗大量的計算資源和API調用費用。如果能夠通過重排器精準地篩選出最相關的文檔,就可以減少系統需要處理的信息量,從而節省成本。這就好比你在購物時,如果能夠精準地找到自己想要的商品,就不需要浪費時間和精力去瀏覽大量的無關商品,同時也減少了購物成本。
(三)彌補嵌入向量的局限性
在RAG系統中,嵌入向量(Embedding)是一種常用的信息表示方法。它將文檔和查詢內容映射到一個低維的向量空間中,通過計算向量之間的相似度來判斷文檔與查詢的相關性。但這種方法也存在一些局限性。首先,嵌入向量可能無法準確捕捉到語義的細微差別。比如,“我喜歡吃蘋果”和“我喜歡吃蘋果派”這兩個句子在語義上雖然有聯系,但也有明顯的區別,但嵌入向量可能無法很好地區分它們。其次,將復雜的信息壓縮到低維空間中,可能會導致信息丟失。最后,嵌入向量在處理超出其訓練數據范圍的信息時,可能會出現泛化能力不足的問題。而重排器可以彌補這些不足,它通過更復雜的匹配技術,對文檔進行更細致的分析和排序。
三、重排器的優勢
(一)“詞袋嵌入”方法
重排器不會像嵌入向量那樣,將整個文檔簡單地映射到一個單一的向量上。而是將文檔分解成更小的、具有上下文信息的單元,比如句子或短語。這樣,它就可以更準確地理解文檔的語義。就像在閱讀一篇文章時,我們不會只看文章的標題就下結論,而是會仔細閱讀文章中的每一個段落、每一句話,從而更好地理解文章的主旨。
(二)語義關鍵詞匹配
重排器結合了強大的編碼器模型(如BERT)和基于關鍵詞的技術,既能夠捕捉到文檔的語義含義,又能夠關注到關鍵詞的相關性。這就像是在尋找一本書時,我們不僅會看書的標題和內容簡介,還會查看書中的關鍵詞索引,從而更準確地判斷這本書是否符合我們的需求。
(三)更好的泛化能力
由于重排器關注的是文檔中的小單元和上下文信息,它在處理未見過的文檔和查詢時,表現得更加出色。這就像是一個經驗豐富的偵探,他可以根據現場的細微線索,推斷出案件的真相,即使他以前從未遇到過類似的案件。
四、重排器的類型
重排器的世界是豐富多彩的,不斷有新的技術和方法涌現。下面我們就來了解一下幾種常見的重排器類型。
(一)交叉編碼器(Cross-Encoder)
交叉編碼器是一種深度學習模型,它會對查詢和文檔這對數據進行分類分析,深入理解它們之間的關系。這就像是一個專業的翻譯人員,他不僅能夠理解兩種語言的字面意思,還能夠準確地把握它們之間的語義聯系。交叉編碼器在精確的相關性評分方面表現出色,但它的缺點是需要大量的計算資源,不太適合實時應用。
舉個例子,假設我們使用FlashrankRerank作為重排器,結合ContextualCompressionRetriever來提高檢索文檔的相關性。代碼如下:
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import FlashrankRerank
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(temperature=0)
compressor = FlashrankRerank()
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor, base_retriever=retriever
)
compressed_docs = compression_retriever.invoke(
"What did the president say about Ketanji Jackson Brown"
)
print([doc.metadata["id"] for doc in compressed_docs])
pretty_print_docs(compressed_docs)
這段代碼利用FlashrankRerank對基礎檢索器(retriever)檢索到的文檔進行重排,根據它們與查詢“總統對Ketanji Jackson Brown說了些什么”的相關性進行排序。最終,它會打印出文檔的ID和經過壓縮、重排后的文檔內容。
(二)多向量重排器(Multi-Vector Reranker)
多向量模型,如ColBERT,采用了一種延遲交互的方法。查詢和文檔的表示是獨立編碼的,它們的交互發生在處理過程的后期。這種方法允許預先計算文檔的表示,從而加快檢索速度,減少計算需求。
使用ColBERT重排器的代碼如下:
pip install -U ragatouille
from ragatouille import RAGPretrainedModel
from langchain.retrievers import ContextualCompressionRetriever
RAG = RAGPretrainedModel.from_pretrained("colbert-ir/colbertv2.0")
compression_retriever = ContextualCompressionRetriever(
base_compressor=RAG.as_langchain_document_compressor(), base_retriever=retriever
)
compressed_docs = compression_retriever.invoke(
"What animation studio did Miyazaki found"
)
print(compressed_docs[0])
這段代碼通過ColBERT重排器,對檢索到的文檔進行壓縮和重排,以回答“宮崎駿創立了哪家動畫工作室”這個問題。輸出的文檔內容中包含了宮崎駿創立吉卜力工作室的相關信息,以及該工作室的背景和第一部電影的相關內容。
(三)微調大型語言模型(Fine-tuned LLM)重排器
對大型語言模型(LLM)進行微調,是提高其在重排任務中表現的關鍵。預訓練的LLM本身并不擅長衡量查詢與文檔之間的相關性。通過對它們在特定的排名數據集(如MS MARCO段落排名數據集)上進行微調,我們可以增強它們在文檔排名方面的性能。
根據模型結構的不同,監督式重排器主要有兩種類型:
- 編碼器 - 解碼器模型:這些模型將文檔排名視為一個生成任務,使用編碼器 - 解碼器框架來優化重排過程。例如,RankT5模型被訓練成生成標記,以將查詢 - 文檔對分類為相關或不相關。
- 僅解碼器模型:這種方法側重于微調僅使用解碼器的模型,如LLaMA。RankZephyr和RankGPT等模型探索了在這種情況下計算相關性的不同方法。
通過應用這些微調技術,我們可以提高LLM在重排任務中的性能,使它們更有效地理解和優先考慮相關文檔。
使用RankZephyr的代碼如下:
pip install --upgrade --quiet rank_llm
from langchain.retrievers.contextual_compression import ContextualCompressionRetriever
from langchain_community.document_compressors.rankllm_rerank import RankLLMRerank
compressor = RankLLMRerank(top_n=3, model="zephyr")
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor, base_retriever=retriever
)
compressed_docs = compression_retriever.invoke(query)
pretty_print_docs(compressed_docs)
這段代碼利用RankZephyr對檢索到的文檔進行重排,根據查詢的相關性選擇最相關的3個文檔。輸出的文檔內容中包含了與查詢相關的各種信息,如對俄羅斯實施經濟制裁、關閉美國領空對俄羅斯航班等內容。
(四)將LLM作為“裁判”的重排方法
大型語言模型可以通過提示策略(如逐點、逐列表和成對方法)自主地改進文檔重排。這些方法利用LLM的推理能力(將LLM作為“裁判”),直接評估文檔與查詢的相關性。雖然這些方法在有效性方面具有競爭力,但與LLM相關的高計算成本和延遲可能會阻礙實際應用。
- 逐點方法:逐點方法評估單個文檔與查詢的相關性。它包括兩個子類別:相關性生成和查詢生成。這兩種方法都適用于零樣本文檔重排,即在沒有特定示例的先驗訓練的情況下對文檔進行排名。
- 逐列表方法:逐列表方法通過將查詢和文檔列表包含在提示中來對文檔列表進行排名。然后指示LLM輸出重排文檔的標識符。由于LLM的輸入長度有限,通常不可能一次性包含所有候選文檔。為了管理這種情況,逐列表方法采用滑動窗口策略。這種方法一次對文檔的一個子集進行排名,從后向前移動窗口,并且只對當前窗口內的文檔進行重排。
- 成對方法:在成對方法中,LLM接收一個提示,其中包含查詢和一對文檔。模型的任務是確定哪個文檔更相關。為了聚合結果,可以使用如AllPairs等方法。AllPairs生成所有可能的文檔對,并為每個文檔計算最終的相關性得分。高效的排序算法,如堆排序和冒泡排序,有助于加快排名過程。
使用OpenAI的GPT-4-turbo模型進行逐點、逐列表和成對重排的代碼如下:
import openai
# Set your OpenAI API key
openai.api_key = 'YOUR_API_KEY'
def pointwise_rerank(query, document):
prompt = f"Rate the relevance of the following document to the query on a scale from 1 to 10:\n\nQuery: {query}\nDocument: {document}\n\nRelevance Score:"
response = openai.ChatCompletion.create(
model="gpt-4-turbo",
messages=[{"role": "user", "content": prompt}]
)
return response['choices'][0]['message']['content'].strip()
def listwise_rerank(query, documents):
# Use a sliding window approach to rerank documents
window_size = 5
reranked_docs = []
for i in range(0, len(documents), window_size):
window = documents[i:i + window_size]
prompt = f"Given the query, please rank the following documents:\n\nQuery: {query}\nDocuments: {', '.join(window)}\n\nRanked Document Identifiers:"
response = openai.ChatCompletion.create(
model="gpt-4-turbo",
messages=[{"role": "user", "content": prompt}]
)
ranked_ids = response['choices'][0]['message']['content'].strip().split(', ')
reranked_docs.extend(ranked_ids)
return reranked_docs
def pairwise_rerank(query, documents):
scores = {}
for i in range(len(documents)):
for j in range(i + 1, len(documents)):
doc1 = documents[i]
doc2 = documents[j]
prompt = f"Which document is more relevant to the query?\n\nQuery: {query}\nDocument 1: {doc1}\nDocument 2: {doc2}\n\nAnswer with '1' for Document 1, '2' for Document 2:"
response = openai.ChatCompletion.create(
model="gpt-4-turbo",
messages=[{"role": "user", "content": prompt}]
)
winner = response['choices'][0]['message']['content'].strip()
if winner == '1':
scores[doc1] = scores.get(doc1, 0) + 1
scores[doc2] = scores.get(doc2, 0)
elif winner == '2':
scores[doc2] = scores.get(doc2, 0) + 1
scores[doc1] = scores.get(doc1, 0)
# Sort documents based on scores
ranked_docs = sorted(scores.items(), key=lambda item: item[1], reverse=True)
return [doc for doc, score in ranked_docs]
# Example usage
query = "What are the benefits of using LLMs for document reranking?"
documents = [
"LLMs can process large amounts of text quickly.",
"They require extensive fine-tuning for specific tasks.",
"LLMs can generate human-like text responses.",
"They are limited by their training data and may produce biased results."
]
# Pointwise Reranking
for doc in documents:
score = pointwise_rerank(query, doc)
print(f"Document: {doc} - Relevance Score: {score}")
# Listwise Reranking
reranked_listwise = listwise_rerank(query, documents)
print(f"Listwise Reranked Documents: {reranked_listwise}")
# Pairwise Reranking
reranked_pairwise = pairwise_rerank(query, documents)
print(f"Pairwise Reranked Documents: {reranked_pairwise}")
這段代碼分別使用逐點、逐列表和成對方法對一組文檔進行重排。輸出結果顯示了每個文檔的相關性評分以及通過不同方法重排后的文檔順序。
(五)重排API
私有的重排API為希望在不進行大量基礎設施投資的情況下,增強搜索系統語義相關性的組織提供了一個便捷的解決方案。像Cohere、Jina和Mixedbread等公司提供了這些服務。
Cohere:為英語和多語言文檔提供定制模型,自動對文檔進行分塊,并將相關性得分標準化在0到1之間。
Jina:專注于通過語義理解增強搜索結果,并提供更長的上下文長度。
Mixedbread:提供一系列開源的重排模型,為集成到現有搜索基礎設施中提供了靈活性。
使用Cohere的代碼如下:
pip install --upgrade --quiet cohere
from langchain.retrievers.contextual_compression import ContextualCompressionRetriever
from langchain_cohere import CohereRerank
from langchain_community.llms import Cohere
from langchain.chains import RetrievalQA
llm = Cohere(temperature=0)
compressor = CohereRerank(model="rerank-english-v3.0")
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor, base_retriever=retriever
)
chain = RetrievalQA.from_chain_type(
llm=Cohere(temperature=0), retriever=compression_retriever
)
這段代碼通過Cohere的重排模型,對檢索到的文檔進行重排,以回答關于Ketanji Brown Jackson的查詢。輸出結果中包含了總統對Ketanji Brown Jackson的高度評價,以及她獲得的支持等內容。
五、如何選擇適合RAG的重排器?
選擇最適合RAG系統的重排器需要綜合考慮多個因素:
(一)相關性提升
重排器的主要目標是提高搜索結果的相關性。可以使用諸如歸一化折損累積增益(NDCG)或歸因等指標來評估重排器對相關性的提升效果。就像在評價一個廚師的手藝時,要看他做出來的菜肴是否符合客人的口味一樣,我們也要看重排器是否能夠真正提高搜索結果的質量。
(二)延遲
延遲是指重排器給搜索過程增加的額外時間。要確保這個時間在你的應用要求的可接受范圍內。如果一個重排器雖然能夠提高相關性,但需要花費過多的時間,那么在一些對實時性要求較高的場景中,它可能就不太適用了。就像在一場緊張的比賽中,你需要在短時間內做出正確的決策,如果一個工具需要花費太多時間來提供幫助,那么它可能就不是最佳選擇。
(三)上下文理解能力
要考慮重排器對不同長度上下文的處理能力。在一些復雜的查詢中,可能需要考慮較長的上下文信息,而有些重排器可能在這方面表現得更好。這就像是在閱讀一篇文章時,有些讀者能夠更好地理解文章中的長難句和復雜的上下文關系,而有些讀者則可能只能理解簡單的句子。
(四)泛化能力
要確保重排器在不同的領域和數據集上都能有良好的表現,避免出現過擬合的情況。如果一個重排器只在特定的領域或數據集上表現良好,而在其他情況下表現不佳,那么它可能就不太可靠。這就像是一個學生,如果他只在某一個科目的考試中表現出色,但在其他科目的考試中成績很差,那么他可能就不是一個全面發展的學生。
六、最新研究進展
(一)交叉編碼器嶄露頭角
最近的研究表明,交叉編碼器在與強大的檢索器結合使用時,展現出了高效性和有效性。雖然在領域內的性能差異可能不太明顯,但在領域外的場景中,重排器的影響則更加顯著。交叉編碼器在重排任務中通常能夠超越大多數LLM(除了在某些情況下的GPT-4),并且效率更高。
七、結語
選擇合適的RAG重排器對于提升系統的性能和確保準確的搜索結果至關重要。隨著RAG領域的發展,清晰地了解整個流程對于團隊構建有效的系統至關重要。通過應對過程中的挑戰,團隊可以提高性能。了解不同類型的重排器及其優缺點是必不可少的。仔細選擇和評估RAG的重排器可以增強RAG應用的準確性和效率。這種深思熟慮的方法將帶來更好的結果和更可靠的系統。
在RAG的世界里,重排器就像是一個默默無聞的英雄,雖然它不直接與用戶交互,但卻在背后為用戶提供著精準、高效的信息服務。希望通過對重排器的深入了解,我們能夠更好地利用RAG系統,在信息的海洋中暢游無阻,找到那些真正有價值的知識寶藏。
以上就是關于RAG系統中重排器的詳細介紹啦!如果你對這個話題感興趣,或者在實際應用中遇到了相關的問題,歡迎在評論區留言討論哦!讓我們一起探索更多關于RAG系統的奧秘,共同進步!
本文轉載自公眾號Halo咯咯 作者:基咯咯
