Reranking 19 分钟阅读

RAG中的重排序(Reranking):提升检索准确率的最后一道防线

RAG系统的检索只是第一步,重排序(Reranking)能进一步优化结果质量。本文详解重排序的原理、主流模型对比、实现方法和最佳实践,帮助你将RAG准确率提升到新高度。

7,389 字

RAG系统中,即使使用了混合搜索,检索出的top-K文档中仍可能存在不够相关的内容。重排序(Reranking)作为"最后一道防线",能显著提升最终输入LLM的内容质量,从而改善答案准确性。

为什么需要重排序

向量检索的局限

向量检索使用Bi-Encoder架构:

  1. 查询→向量
  2. 文档→向量
  3. 计算相似度(余弦/点积)

优势:快速,可预先计算文档向量并索引。

局限:查询和文档独立编码,无法捕捉细粒度的交互关系。

示例

  • 查询:"Python API如何调用?"
  • 文档A:"Python SDK安装指南"(包含Python但不相关)
  • 文档B:"API调用示例代码"(高度相关)

向量检索可能给文档A更高分(因为"Python"词频高),而文档B更相关但得分低。

重排序的作用

重排序使用Cross-Encoder架构:

  • 查询和文档一起输入模型
  • 模型直接输出相关性得分

优势:能理解查询-文档之间的深层交互。

流程

1. 检索:返回100个候选文档(快但粗糙)
2. 重排序:精细评估top-100,重新排序
3. 输出:top-5高质量文档给LLM

重排序模型对比

1. Cohere Rerank

特点

  • 商业API,易用
  • 支持多语言
  • 高准确率

使用

import cohere

co = cohere.Client("YOUR_API_KEY")

query = "如何优化LLM成本?"
documents = [
    "GPT-4定价是$0.03/1K tokens",
    "使用更小的模型如GPT-3.5可以降低成本",
    "今天天气很好"  # 不相关文档
]

# 重排序
results = co.rerank(
    query=query,
    documents=documents,
    top_n=2,
    model="rerank-multilingual-v2.0"
)

for result in results:
    print(f"文档{result.index}: 得分{result.relevance_score:.3f}")
    # 文档1: 得分0.987
    # 文档0: 得分0.654
    # 文档2被过滤(得分低)

定价:$2/1000次搜索

2. Sentence-Transformers Cross-Encoder

特点

  • 开源免费
  • 可本地部署
  • 多个预训练模型

使用

from sentence_transformers import CrossEncoder

model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

query = "如何优化LLM成本?"
documents = [
    "GPT-4定价是$0.03/1K tokens",
    "使用更小的模型如GPT-3.5可以降低成本",
    "今天天气很好"
]

# 计算相关性得分
scores = model.predict([(query, doc) for doc in documents])

# 排序
ranked_docs = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True)

for doc, score in ranked_docs:
    print(f"{score:.3f}: {doc}")
    # 0.987: 使用更小的模型...
    # 0.654: GPT-4定价...
    # 0.021: 今天天气很好

3. BGE Reranker(中文优化)

from FlagEmbedding import FlagReranker

reranker = FlagReranker('BAAI/bge-reranker-large', use_fp16=True)

scores = reranker.compute_score([[query, doc] for doc in documents])

模型对比

模型 准确率 速度 成本 多语言
Cohere Rerank ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ 付费
MS-MARCO Cross-Encoder ⭐⭐⭐⭐ ⭐⭐⭐ 免费 ❌(英文)
BGE Reranker ⭐⭐⭐⭐⭐ ⭐⭐⭐ 免费

完整的RAG + Reranking实现

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CohereRerank
from langchain.vectorstores import Pinecone

class AdvancedRAG:
    def __init__(self):
        # 向量检索器
        self.vectorstore = Pinecone.from_existing_index("my-index")
        self.base_retriever = self.vectorstore.as_retriever(search_kwargs={"k": 20})
        
        # 重排序器
        self.reranker = CohereRerank(
            model="rerank-multilingual-v2.0",
            top_n=5
        )
        
        # 组合检索器
        self.retriever = ContextualCompressionRetriever(
            base_compressor=self.reranker,
            base_retriever=self.base_retriever
        )
    
    def query(self, question):
        # 检索 + 重排序
        docs = self.retriever.get_relevant_documents(question)
        
        # 生成答案
        context = "\n\n".join([doc.page_content for doc in docs])
        answer = llm.generate(f"基于以下内容回答:\n{context}\n\n问题:{question}")
        
        return {
            "answer": answer,
            "sources": [doc.metadata for doc in docs]
        }

重排序策略

1. 两阶段重排序

对于超大规模文档库,使用两阶段重排序:

def two_stage_rerank(query, all_docs):
    """
    阶段1:快速重排序(粗排)
    阶段2:精细重排序(精排)
    """
    # 阶段1:轻量级模型,从1000个候选中筛选出100个
    stage1_model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-2-v2')  # 小模型,快
    stage1_scores = stage1_model.predict([(query, doc) for doc in all_docs])
    top_100 = sorted(zip(all_docs, stage1_scores), key=lambda x: x[1], reverse=True)[:100]
    
    # 阶段2:重量级模型,从100个中筛选出5个
    stage2_model = CrossEncoder('cross-encoder/ms-marco-TinyBERT-L-6')  # 大模型,准
    stage2_docs = [doc for doc, _ in top_100]
    stage2_scores = stage2_model.predict([(query, doc) for doc in stage2_docs])
    final_top_5 = sorted(zip(stage2_docs, stage2_scores), key=lambda x: x[1], reverse=True)[:5]
    
    return [doc for doc, _ in final_top_5]

2. 多样性重排序

避免返回的文档过于相似(信息冗余):

def diversity_rerank(docs, scores, lambda_param=0.5):
    """
    MMR(Maximal Marginal Relevance)算法
    平衡相关性和多样性
    """
    selected = []
    remaining = list(zip(docs, scores))
    
    while remaining and len(selected) < 5:
        if not selected:
            # 第一个文档:选择最相关的
            selected.append(remaining.pop(0))
        else:
            # 后续文档:平衡相关性和与已选文档的差异性
            mmr_scores = []
            for doc, relevance in remaining:
                # 计算与已选文档的最大相似度
                max_sim = max(
                    compute_similarity(doc, sel_doc)
                    for sel_doc, _ in selected
                )
                
                # MMR得分 = 相关性 - λ * 相似度
                mmr = lambda_param * relevance - (1 - lambda_param) * max_sim
                mmr_scores.append(mmr)
            
            # 选择MMR得分最高的
            best_idx = mmr_scores.index(max(mmr_scores))
            selected.append(remaining.pop(best_idx))
    
    return [doc for doc, _ in selected]

3. 领域自适应

在特定领域数据上微调重排序模型:

from sentence_transformers import CrossEncoder, InputExample
from torch.utils.data import DataLoader

# 准备训练数据
train_samples = [
    InputExample(texts=['查询1', '相关文档'], label=1.0),
    InputExample(texts=['查询1', '不相关文档'], label=0.0),
    # ...
]

# 加载预训练模型
model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

# 微调
train_dataloader = DataLoader(train_samples, shuffle=True, batch_size=16)
model.fit(
    train_dataloader=train_dataloader,
    epochs=3,
    warmup_steps=100
)

# 保存微调后的模型
model.save('./my-domain-reranker')

性能优化

1. 批处理

def batch_rerank(query, docs, batch_size=32):
    """批量处理,提升吞吐量"""
    all_scores = []
    
    for i in range(0, len(docs), batch_size):
        batch = docs[i:i+batch_size]
        scores = reranker.predict([(query, doc) for doc in batch])
        all_scores.extend(scores)
    
    return all_scores

2. 缓存

from functools import lru_cache

@lru_cache(maxsize=1000)
def cached_rerank(query, doc_tuple):
    """缓存重排序结果"""
    return reranker.predict([(query, doc) for doc in doc_tuple])

3. 异步处理

import asyncio

async def async_rerank(query, docs):
    """异步重排序,减少等待时间"""
    loop = asyncio.get_event_loop()
    scores = await loop.run_in_executor(
        None,
        reranker.predict,
        [(query, doc) for doc in docs]
    )
    return scores

效果评估

测试数据集:1000个查询,每个查询检索top-20文档

配置 准确率@5 延迟
仅向量检索 68% 50ms
向量+混合搜索 82% 70ms
向量+混合搜索+重排序 91% 150ms

结论:重排序虽增加延迟,但准确率提升显著(+9个百分点)。

何时使用重排序

适合场景

  • 对准确率要求极高(法律、医疗、金融)
  • 检索候选文档多(>20个)
  • 可接受适度的延迟增加

不适合场景

  • 实时性要求极高(<100ms)
  • 计算资源有限
  • 初次检索已足够准确

实战案例

某法律文书检索系统:

Before:仅向量检索

  • 查询:"劳动合同纠纷判决"
  • 返回文档包含离婚、刑事等无关案例
  • 律师需手动筛选,效率低

After:向量检索 + Cohere Rerank

  • 重排序过滤掉无关案例
  • top-5全部为劳动合同相关
  • 律师工作效率提升3倍

总结

重排序是RAG系统提升准确率的"最后一道防线":

  1. 检索阶段:快速召回候选(向量/混合检索)
  2. 重排序阶段:精细评估,重新排序
  3. 生成阶段:高质量输入→高质量输出

通过合理使用重排序,RAG准确率可提升10-20个百分点。对于高价值应用,这一投入完全值得。


相关资源

RAG优化

检索技术

应用实践

SearchCans提供高性价比的Bing搜索API和Reader API服务,专为AI Agent和开发者打造。立即体验 →

标签:

Reranking RAG优化 检索排序 AI精度提升

准备好用 SearchCans 构建你的 AI 应用了吗?

立即体验我们的 SERP API 和 Reader API。每千次调用仅需 ¥0.56 起,无需信用卡即可免费试用。