top of page

推動 RAG:選擇最佳的嵌入和重新排序模型


在建立檢索增強生成(RAG)流程時,一個關鍵組件是檢索器。我們有多種嵌入模型可供選擇,包括OpenAI、CohereAI和開源的句子嵌入模型...等等。


在建立檢索增強生成(RAG)流程時,一個關鍵組件是檢索器。我們有多種嵌入模型可供選擇,包括OpenAI、CohereAI和開源的句子嵌入模型。此外,CohereAI和句子嵌入模型還提供了多種重新排序器。但是在所有這些選項中,我們如何確定最佳組合以獲得卓越的檢索性能呢?我們如何 知道哪種嵌入模型最適合我們的數據?或者哪個重新排序模型能最大程度地提升我們的結果呢?

在本博客文章中,我們將使用LlamaIndex的檢索評估模塊快速確定最佳的嵌入和重新排序模型組合。讓我們開始吧!


首先,我們要先了解檢索評估中可用的指標。


了解檢索評估中的指標:

為了評估我們的檢索系統的效能,我們主要依賴兩個被廣泛接受的指標:命中率(Hit Rate)和平均倒數排名(MRR)。讓我們深入瞭解這些指標,了解它們的重要性和運作方式。


命中率(Hit Rate):

命中率計算在前k個檢索到的文檔中正確答案的比例。簡單來說,它是關於我們的系統在前幾個猜測中有多少次猜對的問題。


平均倒數排名(MRR):

對於每個查詢,MRR通過查看最高排名的相關文檔的排名來評估系統的準確性。具體來說,它是在所有查詢中這些排名的倒數的平均值。因此,如果第一個相關文檔是最高的結果,倒數排名為1;如果它是第二個,倒數排名為1/2,依此類推。


現在,我們已經確定了範圍並熟悉了這些指標,現在是時候進入實驗了。您也可以使用我們的Google Colab筆記本來進行實際操作。


設置環境!

pip install llama-index sentence-transformers cohere anthropic voyageai protobuf pypdf

設置金鑰

openai_api_key = 'YOUR OPENAI API KEY'
cohere_api_key = 'YOUR COHEREAI API KEY'
anthropic_api_key = 'YOUR ANTHROPIC API KEY'
openai.api_key = openai_api_key


下載數據

我們將使用Llama2論文進行此實驗。讓我們下載這篇論文。

!wget --user-agent "Mozilla" "https://arxiv.org/pdf/2307.09288.pdf" -O "llama2.pdf"


加載數據

讓我們加載數據。我們將使用從開頭到36頁的頁面進行實驗,這些頁面不包括目錄、參考文獻和附錄。

然後,將這些數據解析並轉換為節點,這些節點代表我們想要檢索的數據塊。我們使用chunk_size為512。


documents = SimpleDirectoryReader(input_files=["llama2.pdf"]).load_data()
node_parser = SimpleNodeParser.from_defaults(chunk_size=512)
nodes = node_parser.get_nodes_from_documents(documents)

生成問題-上下文對:

為了進行評估,我們創建了一個問題-上下文對的數據集。這個數據集可以被看作是一組問題和相應的上下文,來自我們的數據。為了消除對嵌入(OpenAI/CohereAI)和重新排序器(CohereAI)的評估的偏見,我們使用Anthropic LLM生成問題-上下文對。

讓我們初始化一個提示模板來生成問題-上下文對。



# 生成問題的提示
qa_generate_prompt_tmpl = """\
下面是上下文信息。
---------------------
{context_str}
---------------------
基於上下文信息生成只有以下查詢的問題。
您是一位教授。您的任務是為即將到來的測驗/考試設置{num_questions_per_chunk}個問題。問題的性質應該多樣化,涵蓋整個文檔 。問題不應該包含選項,不應該以Q1/Q2開頭。將問題限制在所提供的上下文信息中。
\"\"\"
llm = Anthropic(api_key=anthropic_api_key)
qa_dataset = generate_question_context_pairs(
    nodes, llm=llm, num_questions_per_chunk=2)


清理數據集的函數,以過濾掉包含“以下是基於提供的上下文的2個問題”的句子

# 清理數據集的函數
def filter_qa_dataset(qa_dataset):
    """
    從qa_dataset中過濾掉包含特定短語的查詢和相應的文檔條目,並使用過濾後的數據創建一個新的EmbeddingQAFinetuneDataset 對象。
    :param qa_dataset: 具有'queries'、'corpus'和'relevant_docs'屬性的對象。
    :return: 具有過濾後的查詢、語料庫和相應文檔的EmbeddingQAFinetuneDataset對象。
    """
    # 從查詢和相應文檔中提取需要刪除的鍵
    queries_relevant_docs_keys_to_remove = {
        k for k, v in qa_dataset.queries.items()
        if '以下是基於提供的上下文的2個問題' in v or '以下是基於提供的上下文的兩個問題' in v
    }
    # 使用字典推導式過濾查詢和相應文檔
    filtered_queries = {
        k: v for k, v in qa_dataset.queries.items()
        if k not in queries_relevant_docs_keys_to_remove
    }
    filtered_relevant_docs = {
        k: v for k, v in qa_dataset.relevant_docs.items()
        if k not in queries_relevant_docs_keys_to_remove
    }
    # 使用過濾後的數據創建EmbeddingQAFinetuneDataset的新實例
    return EmbeddingQAFinetuneDataset(
        queries=filtered_queries,
        corpus=qa_dataset.corpus,
        relevant_docs=filtered_relevant_docs
    )

# 過濾掉包含“以下是基於提供的上下文的2個問題”短語的對
qa_dataset = filter_qa_dataset(qa_dataset)

自定義檢索器:

為了找到最佳的檢索器,我們結合了一個嵌入模型和一個重排序模型。最初,我們建立了一個基礎的 VectorIndexRetriever。在檢索 到節點後,我們引入了一個重排序模型來進一步優化結果。值得注意的是,在這個特定的實驗中,我們將 similarity_top_k 設置為10 ,並使用 reranker 選擇前5個。然而,根據你具體實驗的需求,你可以自由調整這個參數。這裡我們展示了使用 OpenAIEmbedding 的代碼,其他嵌入的代碼請參考筆記本。

embed_model = OpenAIEmbedding()
service_context = ServiceContext.from_defaults(llm=None, embed_model = embed_model)
vector_index = VectorStoreIndex(nodes, service_context=service_context)
vector_retriever = VectorIndexRetriever(index=vector_index, similarity_top_k = 10)

class CustomRetriever(BaseRetriever):
    """執行向量搜索和知識圖搜索的自定義檢索器"""
    def __init__(
        self,
        vector_retriever: VectorIndexRetriever,
    ) -> None:
        """初始化參數。"""
        self._vector_retriever = vector_retriever

    def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:
        """根據查詢檢索節點。"""
        retrieved_nodes = self._vector_retriever.retrieve(query_bundle)

        if reranker != 'None':
            retrieved_nodes = reranker.postprocess_nodes(retrieved_nodes, query_bundle)
        else:
            retrieved_nodes = retrieved_nodes[:5]

        return retrieved_nodes

    async def _aretrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:
        """異步根據查詢檢索節點。由用戶實現。"""
        return self._retrieve(query_bundle)

    async def aretrieve(self, str_or_query_bundle: QueryType) -> List[NodeWithScore]:
        if isinstance(str_or_query_bundle, str):
            str_or_query_bundle = QueryBundle(str_or_query_bundle)
        return await self._aretrieve(str_or_query_bundle)

custom_retriever = CustomRetriever(vector_retriever)

評估:

為了評估我們的檢索器,我們計算了平均倒數排名(MRR)和命中率(Hit Rate)這兩個指標。

retriever_evaluator = RetrieverEvaluator.from_metric_names(
    ["mrr", "hit_rate"], retriever=custom_retriever)
eval_results = await retriever_evaluator.aevaluate_dataset(qa_dataset)

結果:

我們對各種嵌入模型和重新排序器進行了測試。以下是我們考慮的模型:

嵌入模型:

  • OpenAI Embedding

  • Voyage Embedding

  • CohereAI Embedding(v2.0/ v3.0)

  • Jina Embedding

  • BAAI/bge-large-en

  • 重新排序器

    • CohereAIbge-reranker-base

    • CohereAIbge-reranker-large


值得一提的是,這些結果提供了對於這個特定數據集和任務的性能的深入洞察。然而,實際結果可能會根據數據特徵、數據集大小和其他變量(如chunk_size、similarity_top_k等)而有所不同。


下表展示了根據命中率(Hit Rate)和平均倒數排名(MRR)這兩個指標的評估結果:


分析:

Embedding model 的性能:

  • OpenAI:展示了頂級性能,尤其是與CohereRerank(0.926966命中率,0.865262 MRR)和bge-reranker-large(0.910112命中率,0.853993 MRR)組合使用時,表明與重新排序工具的良好兼容性。

  • bge-large:在重新排序器的幫助下有顯著的改善,最佳結果來自CohereRerank(0.865169命中率,0.805618 MRR)。

  • llm-embedder:在重新排序器的幫助下獲益良多,特別是與CohereRerank(0.887640命中率,0.825843 MRR)一起,可以大幅提升性能。

  • Cohere:Cohere的最新v3.0嵌入優於v2.0,並且通過集成本地的CohereRerank,顯著改善了指標,擁有0.876404的命中率和0.832584 的MRR。

  • Voyage:初始性能強大,並且通過CohereRerank進一步提升(0.915730命中率,0.847940 MRR),表明對重新排序敏感性高。

  • JinaAI:起點較低,但通過bge-reranker-large的顯著增長(0.601124命中率,0.578652 MRR),表明重排序顯著提升了 其性能。其表現不佳的原因可能是該嵌入模型優化了8K的上下文長度。


重新排序器的影響:

  • 沒有重新排序器:每個嵌入的基準性能。

  • bge-reranker-base:通常改善了所有嵌入的命中率和MRR。

  • bge-reranker-large:對於多個嵌入模型,其性能媲美或超越CohereRerank,是最高或接近最高的MRR。

  • CohereRerank:持續提升了所有嵌入模型的性能,通常提供了最佳或接近最佳的結果。


重新排序器的必要性:

數據清楚地表明重排序器在優化搜索結果方面的重要性。幾乎所有的嵌入模型都受益於重排序,顯示出命中率和MRR的改善。


重新排序器,尤其是CohereRerank,已經證明了它們將平均性能一般的嵌入轉變為有競爭力的嵌入,這一點在JinaAI中可以看到。


總體優越性:

綜合考慮命中率和MRR,OpenAI + CohereRerank和Voyage + bge-reranker-large的組合成為頂尖的競爭者。


然而,CohereRerank/ bge-reranker-large 重排序器在各種嵌入模型中持續改善的表現使它們成為提升搜索質量的明星選擇,無論使用哪種嵌入模型。


總結:

在本篇文章中,我們展示了如何使用各種嵌入模型和重排序模型來評估和提升檢索器的性能。以下是我們的最終結論:


  1. 嵌入模型:OpenAI和Voyage嵌入模型,特別是與CohereRerank/ bge-reranker-large重排序模型結合使用,為命中率和MRR設立了標杆。

  2. 重排序模型:重排序模型的影響,特別是CohereRerank/ bge-reranker-large,不可低估。它們在改善許多嵌入模型的MRR方面發揮著關鍵作用,顯示了它們在改進搜索結果方面的重要性。

  3. 基礎是關鍵:選擇正確的嵌入模型對於初始搜索非常重要;即使是最好的重排序模型也無法幫助太多,如果基本搜索結果不好。

  4. 共同合作:為了充分發揮檢索器的潛力,找到合適的嵌入模型和重排序模型的組合非常重要。這項研究顯示了仔細測試並找到最佳配 對的重要性。


Коментари


bottom of page