基于Reranker方法增强RAG的检索效果

一、为什么需要Reranker

检索增强生成(RAG)是一种新兴的 AI 技术栈,通过为大型语言模型(LLM)提供额外的“最新知识”来增强其能力。

基本的 RAG 应用包括四个关键技术组成部分:

  • Embedding 模型:用于将外部文档和用户查询转换成 Embedding 向量
  • 向量数据库:用于存储 Embedding 向量和执行向量相似性检索(检索出最相关的 Top-K 个信息)
  • 提示词工程(Prompt engineering):用于将用户的问题和检索到的上下文组合成大模型的输入
  • 大语言模型(LLM:用于生成回答

上述的基础 RAG 架构可以有效解决 LLM 产生“幻觉”、生成内容不可靠的问题。

二、Reranker介绍

Reranker 是信息检索(IR)生态系统中的一个重要组成部分,用于评估搜索结果,并进行重新排序,从而提升查询结果相关性。在 RAG 应用中,主要在拿到向量查询(ANN)的结果后使用 Reranker,能够更有效地确定文档和查询之间的语义相关性,更精细地对结果重排,最终提高搜索质量。

目前,Reranker 类型主要有两种——基于统计和基于深度学习模型的 Reranker:

  • 基于统计的 Reranker 会汇总多个来源的候选结果列表,使用多路召回的加权得分或倒数排名融合(RRF)算法来为所有结果重新算分,统一将候选结果重排。这种类型的 Reranker 的优势是计算不复杂,效率高,因此广泛用于对延迟较敏感的传统搜索系统中。
  • 基于深度学习模型的 Reranker,通常被称为 Cross-encoder Reranker。由于深度学习的特性,一些经过特殊训练的神经网络可以非常好地分析问题和文档之间的相关性。这类 Reranker 可以为问题和文档之间的语义的相似度进行打分。因为打分一般只取决于问题和文档的文本内容,不取决于文档在召回结果中的打分或者相对位置,这种 Reranker 既适用于单路召回也适用于多路召回。

三、Reranker实现

1、导入模块和库

import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from typing import Any, List, Optional
from sentence_transformers import CrossEncoder
from typing import Optional, Sequence
from langchain_core.documents import Document
from langchain.callbacks.manager import Callbacks
from langchain.retrievers.document_compressors.base import BaseDocumentCompressor
from llama_index.bridge.pydantic import Field, PrivateAttr
  • 这部分代码导入了必要的Python标准库以及第三方库和模块,例如 sysos 和 typing
  • sys.path.append() 用于将上级目录添加到Python解释器的搜索路径中,以便导入自定义模块。
  • CrossEncoder 是从 sentence_transformers 库中导入的模型,用于文本对的编码。

二、定义 LangchainRerank类

class LangchainReranker(BaseDocumentCompressor):
    """Document compressor that uses `Cohere Rerank API`."""
    model_name_or_path: str = Field()
    _model: Any = PrivateAttr()
    top_n: int = Field()
    device: str = Field()
    max_length: int = Field()
    batch_size: int = Field()
    # show_progress_bar: bool = None
    num_workers: int = Field()
  • LangchainReranker 类继承自 BaseDocumentCompressor,这个基类可能定义了一些文档压缩器的通用行为和方法。
  • 类有几个属性:model_name_or_pathtop_ndevicemax_lengthbatch_size 和 num_workers,它们分别用于指定模型的名称或路径、返回的顶部文档数、使用的设备、文本的最大长度、批处理大小和并行工作者数量。

三、初始化方法 __init__

def __init__(self,
                 model_name_or_path: str,
                 top_n: int = 3,
                 device: str = "cuda",
                 max_length: int = 1024,
                 batch_size: int = 32,
                 # show_progress_bar: bool = None,
                 num_workers: int = 0,
                 # activation_fct = None,
                 # apply_softmax = False,
                 ):
        # self.top_n=top_n
        # self.model_name_or_path=model_name_or_path
        # self.device=device
        # self.max_length=max_length
        # self.batch_size=batch_size
        # self.show_progress_bar=show_progress_bar
        # self.num_workers=num_workers
        # self.activation_fct=activation_fct
        # self.apply_softmax=apply_softmax

        self._model = CrossEncoder(model_name=model_name_or_path, max_length=1024, device=device)
        super().__init__(
            top_n=top_n,
            model_name_or_path=model_name_or_path,
            device=device,
            max_length=max_length,
            batch_size=batch_size,
            # show_progress_bar=show_progress_bar,
            num_workers=num_workers,
            # activation_fct=activation_fct,
            # apply_softmax=apply_softmax
        )
  • __init__ 方法是类的构造函数,在创建类的实例时被调用。
  • 它初始化了 LangchainReranker 类的实例属性,包括创建了一个 CrossEncoder 模型实例 _model,并传入了指定的模型名称或路径、设备类型、文本最大长度等参数。

四、方法 compress_documents

def compress_documents(
            self,
            documents: Sequence[Document],
            query: str,
            callbacks: Optional[Callbacks] = None,
    ) -> Sequence[Document]:
        """
        Compress documents using Cohere's rerank API.

        Args:
            documents: A sequence of documents to compress.
            query: The query to use for compressing the documents.
            callbacks: Callbacks to run during the compression process.

        Returns:
            A sequence of compressed documents.
        """
        if len(documents) == 0:  # to avoid empty api call
            return []
        doc_list = list(documents)
        _docs = [d.page_content for d in doc_list]
        sentence_pairs = [[query, _doc] for _doc in _docs]
        results = self._model.predict(sentences=sentence_pairs,
                                      batch_size=self.batch_size,
                                      #  show_progress_bar=self.show_progress_bar,
                                      num_workers=self.num_workers,
                                      #  activation_fct=self.activation_fct,
                                      #  apply_softmax=self.apply_softmax,
                                      convert_to_tensor=True
                                      )
        top_k = self.top_n if self.top_n < len(results) else len(results)

        values, indices = results.topk(top_k)
        final_results = []
        for value, index in zip(values, indices):
            doc = doc_list[index]
            doc.metadata["relevance_score"] = value
            final_results.append(doc)
        return final_results
  • compress_documents 方法接收一组 Document 对象和一个查询字符串 query,用于对文档进行压缩和排序。
  • 它首先检查文档列表是否为空,如果是则直接返回空列表。
  • 将文档列表转换为文本内容列表 _docs,然后构建查询和文档内容的句对。
  • 使用 _model(即 CrossEncoder 模型)对查询和文档句对进行预测,获取相关性分数 results
  • 根据 top_n 参数选择最相关的文档结果,并将其排序后返回。

五、主程序入口 __main__

if __name__ == "__main__":
    from configs import (LLM_MODELS,
                         VECTOR_SEARCH_TOP_K,
                         SCORE_THRESHOLD,
                         TEMPERATURE,
                         USE_RERANKER,
                         RERANKER_MODEL,
                         RERANKER_MAX_LENGTH,
                         MODEL_PATH)
    from server.utils import embedding_device

    if USE_RERANKER:
        reranker_model_path = MODEL_PATH["reranker"].get(RERANKER_MODEL, "BAAI/bge-reranker-large")
        print("-----------------model path------------------")
        print(reranker_model_path)
        reranker_model = LangchainReranker(top_n=3,
                                           device=embedding_device(),
                                           max_length=RERANKER_MAX_LENGTH,
                                           model_name_or_path=reranker_model_path
                                           )
  • 这部分代码在 __main__ 块中,用于当直接执行脚本时初始化并使用 LangchainReranker 类。
  • 导入了必要的配置和工具函数,如模型路径、设备选择函数等。
  • 根据 USE_RERANKER 的配置,如果需要使用重排序器,就初始化 LangchainReranker 实例 reranker_model,并传入相应的参数。

整体代码

import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from typing import Any, List, Optional
from sentence_transformers import CrossEncoder
from typing import Optional, Sequence
from langchain_core.documents import Document
from langchain.callbacks.manager import Callbacks
from langchain.retrievers.document_compressors.base import BaseDocumentCompressor
from llama_index.bridge.pydantic import Field, PrivateAttr


class LangchainReranker(BaseDocumentCompressor):
    """Document compressor that uses `Cohere Rerank API`."""
    model_name_or_path: str = Field()
    _model: Any = PrivateAttr()
    top_n: int = Field()
    device: str = Field()
    max_length: int = Field()
    batch_size: int = Field()
    # show_progress_bar: bool = None
    num_workers: int = Field()

    # activation_fct = None
    # apply_softmax = False

    def __init__(self,
                 model_name_or_path: str,
                 top_n: int = 3,
                 device: str = "cuda",
                 max_length: int = 1024,
                 batch_size: int = 32,
                 # show_progress_bar: bool = None,
                 num_workers: int = 0,
                 # activation_fct = None,
                 # apply_softmax = False,
                 ):
        # self.top_n=top_n
        # self.model_name_or_path=model_name_or_path
        # self.device=device
        # self.max_length=max_length
        # self.batch_size=batch_size
        # self.show_progress_bar=show_progress_bar
        # self.num_workers=num_workers
        # self.activation_fct=activation_fct
        # self.apply_softmax=apply_softmax

        self._model = CrossEncoder(model_name=model_name_or_path, max_length=1024, device=device)
        super().__init__(
            top_n=top_n,
            model_name_or_path=model_name_or_path,
            device=device,
            max_length=max_length,
            batch_size=batch_size,
            # show_progress_bar=show_progress_bar,
            num_workers=num_workers,
            # activation_fct=activation_fct,
            # apply_softmax=apply_softmax
        )

    def compress_documents(
            self,
            documents: Sequence[Document],
            query: str,
            callbacks: Optional[Callbacks] = None,
    ) -> Sequence[Document]:
        """
        Compress documents using Cohere's rerank API.

        Args:
            documents: A sequence of documents to compress.
            query: The query to use for compressing the documents.
            callbacks: Callbacks to run during the compression process.

        Returns:
            A sequence of compressed documents.
        """
        if len(documents) == 0:  # to avoid empty api call
            return []
        doc_list = list(documents)
        _docs = [d.page_content for d in doc_list]
        sentence_pairs = [[query, _doc] for _doc in _docs]
        results = self._model.predict(sentences=sentence_pairs,
                                      batch_size=self.batch_size,
                                      #  show_progress_bar=self.show_progress_bar,
                                      num_workers=self.num_workers,
                                      #  activation_fct=self.activation_fct,
                                      #  apply_softmax=self.apply_softmax,
                                      convert_to_tensor=True
                                      )
        top_k = self.top_n if self.top_n < len(results) else len(results)

        values, indices = results.topk(top_k)
        final_results = []
        for value, index in zip(values, indices):
            doc = doc_list[index]
            doc.metadata["relevance_score"] = value
            final_results.append(doc)
        return final_results


if __name__ == "__main__":
    from configs import (LLM_MODELS,
                         VECTOR_SEARCH_TOP_K,
                         SCORE_THRESHOLD,
                         TEMPERATURE,
                         USE_RERANKER,
                         RERANKER_MODEL,
                         RERANKER_MAX_LENGTH,
                         MODEL_PATH)
    from server.utils import embedding_device

    if USE_RERANKER:
        reranker_model_path = MODEL_PATH["reranker"].get(RERANKER_MODEL, "BAAI/bge-reranker-large")
        print("-----------------model path------------------")
        print(reranker_model_path)
        reranker_model = LangchainReranker(top_n=3,
                                           device=embedding_device(),
                                           max_length=RERANKER_MAX_LENGTH,
                                           model_name_or_path=reranker_model_path
                                           )

总结

LangchainReranker 类实现了基于 CrossEncoder 的文档压缩和重排序功能,能够根据给定的查询对一组文档进行相关性评分,并返回按相关性排序的文档列表。主程序入口部分则展示了如何配置和使用这个类来实现文档重排序的功能。

<think>好的,我需要帮助用户了解或实现RAG检索增强生成)的相关技术。首先,我应该明确RAG的基本概念。根据引用[2],RAG结合了检索外部知识库和大语言模型的生成能力,类似于开卷考试,让模型在生成答案时可以参考外部信息。接下来,用户可能需要一个具体的实现步骤或教程,所以我得整理出一个清晰的流程。 第一步是确定知识库的来源。用户需要收集和预处理相关数据,比如PDF、网页文本或数据库内容。然后,将这些数据转化为嵌入向量,可能要用到像Sentence-BERT这样的模型。这里需要提到向量数据库的重要性,比如FAISS或Pinecone,用于高效检索。 接下来是检索阶段。当用户输入查询时,系统需要从知识库中找到最相关的文档片段。这部分需要说明如何计算相似度,比如余弦相似度,以及如何选择top-k个结果。 然后是生成部分。将检索到的上下文和用户问题一起输入大模型,比如GPT-3或Llama,生成最终答案。这里可以举例说明不同的提示模板,比如LangChainRAG链。 此外,用户可能关心如何优化RAG系统。可以提到调整检索策略,比如混合搜索或重新排序,以及评估检索结果的质量指标。引用[3]提到的Langchain和llama_index作为工具,应该给出具体的代码示例,比如使用LangChain的VectorstoreIndexCreator。 还要注意常见问题,比如数据预处理不当导致检索效果差,或者大模型生成时的幻觉问题。需要强调评估和迭代的重要性,引用[1]提到的上下文过滤可能也是优化点。 最后,生成相关问题,覆盖RAG的原理、工具选择、评估方法、优化技巧和应用场景,帮助用户进一步探索。确保所有数学表达式使用正确的LaTeX格式,比如嵌入向量的距离计算$d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}$,并检查引用标记的正确添加。</think>以下分步说明如何实现一个基础的RAG系统,并结合代码示例和关键概念解析: ### 一、RAG实现核心步骤 1. **知识库构建** - 数据收集:从PDF/网页/数据库获取原始数据,格式如: ```python documents = [ "RAG通过检索外部知识增强生成...", "LangChain提供RAG工具链..." ] ``` - 文本分块:使用LangChain的`RecursiveCharacterTextSplitter` ```python from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter(chunk_size=500) chunks = text_splitter.split_documents(documents) ``` 2. **向量化处理** $$ \text{embed}(text) \rightarrow \mathbb{R}^d $$ 使用HuggingFace嵌入模型: ```python from sentence_transformers import SentenceTransformer encoder = SentenceTransformer('all-MiniLM-L6-v2') embeddings = encoder.encode(chunks) ``` 3. **向量数据库** 使用FAISS实现相似度检索: ```python import faiss index = faiss.IndexFlatL2(384) index.add(embeddings) ``` 4. **检索-生成流程** ```python query = "如何实现RAG系统?" query_embed = encoder.encode(query) D, I = index.search(query_embed, k=3) # 取top3 context = [chunks[i] for i in I[0]] ``` 5. **提示工程** LangChainRAG提示模板: ```python from langchain.prompts import PromptTemplate template = """基于以下上下文回答: {context} 问题:{question}""" prompt = PromptTemplate(template=template, input_variables=["context", "question"]) ``` ### 二、关键技术组件 1. **检索优化** - 混合搜索:结合$BM25$与向量相似度 - 重新排序:使用`bge-reranker`等模型 2. **生成控制** 通过温度参数调节创造性: $$ P(w_t|w_{<t},c) = \text{softmax}(f(w_{<t},c)/\tau) $$ 3. **评估指标** - 检索召回率:$Recall@k = \frac{\text{相关文档命中数}}{总相关文档数}$ - 生成质量:BLEU、ROUGE等 ### 三、完整代码示例(LangChain实现) ```python from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import FAISS from langchain.llms import LlamaCpp # 知识库构建 embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2") vector_store = FAISS.from_texts(chunks, embeddings) # 检索增强生成 retriever = vector_store.as_retriever(search_kwargs={"k": 3}) llm = LlamaCpp(model_path="llama-2-7b.Q4_K_M.gguf") from langchain.chains import RetrievalQA qa_chain = RetrievalQA.from_chain_type( llm=llm, retriever=retriever, chain_type="stuff" ) print(qa_chain.run("RAG的实现步骤有哪些?")) ``` ### 四、常见问题与优化 1. **冷启动问题**:可使用Wikipedia预构建索引 2. **长上下文处理**:采用`sliding window`分块策略 3. **多模态扩展**:CLIP支持图像检索增强
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值