导航菜单

  • 1.vector
  • 2.milvus
  • 3.pymilvus
  • 4.rag
  • 5.rag_measure
  • ragflow
  • heapq
  • HNSW
  • cosine_similarity
  • math
  • typing
  • etcd
  • minio
  • collections
  • jieba
  • random
  • beautifulsoup4
  • chromadb
  • sentence_transformers
  • numpy
  • lxml
  • openpyxl
  • PyMuPDF
  • python-docx
  • requests
  • python-pptx
  • text_splitter
  • all-MiniLM-L6-v2
  • openai
  • llm
  • BPETokenizer
  • Flask
  • RAGAS
  • BagofWords
  • langchain
  • Pydantic
  • abc
  • faiss
  • MMR
  • scikit-learn
  • Runnable
  • PromptEngineering
  • dataclasses
  • LaTeX
  • rank_bm25
  • TF-IDF
  • asyncio
  • sqlalchemy
  • fastapi
  • Starlette
  • uvicorn
  • argparse
  • Generic
  • ssl
  • urllib
  • python-dotenv
  • RRF
  • CrossEncoder
  • Lost-in-the-middle
  • Jinja2
  • logger
  • io
  • venv
  • concurrent
  • parameter
  • SSE
  • 1. 什么是 RRF?
  • 2. 为什么需要 RRF?
  • 3. 前置知识
    • 3.1 排名列表(Ranking List)
    • 3.2 排名(Rank)
    • 3.3 倒数(Reciprocal)
    • 3.4 Python 基础知识
  • 4. 核心思想与公式
  • 5. 算法步骤
    • 5.1 准备输入
    • 5.2 初始化分数字典
    • 5.3 遍历所有排名列表
    • 5.4 排序输出
  • 6. 代码实现
  • 7. 常见问题
    • 7.1 k 值应该设置为多少?
    • 7.2 如果文档在某个列表中未出现怎么办?
    • 7.3 可以处理多少个排名列表?
    • 7.4 RRF 分数需要归一化吗?
  • 8. 总结

1. 什么是 RRF? #

  • RRF Reciprocal Rank Fusion (RRF),中文译为"倒数排名融合",是一种用于合并多个排名列表的算法。它由 Cormack 等人在 2009 年提出,主要用于信息检索中结合不同检索系统的结果。

想象一下,你同时使用多个搜索引擎(比如百度、谷歌、必应)搜索同一个问题,每个搜索引擎都会返回一个结果列表。RRF 的作用就是将这些不同的结果列表合并成一个统一的、更准确的最终排名列表。

RRF 的核心优势在于:

  • 简单易用:算法逻辑清晰,实现简单
  • 无需训练:不需要训练数据,直接使用
  • 效果稳定:在各种场景下都能取得不错的效果
  • 参数少:只需要一个参数 k(通常固定为 60)

2. 为什么需要 RRF? #

在实际应用中,我们经常会遇到需要合并多个排名列表的场景:

  1. 混合检索系统:结合关键词检索(如 BM25)和语义检索(如向量检索)的结果
  2. 多数据源搜索:从多个数据库或搜索引擎获取结果后需要统一排序
  3. 推荐系统:融合基于内容的推荐、协同过滤推荐等多种策略的结果
  4. 元搜索:聚合多个搜索引擎的结果

如果只是简单地将多个列表合并,可能会出现:

  • 同一个文档在不同列表中排名差异很大
  • 某些重要文档因为只在少数列表中排名靠前而被忽略
  • 无法平衡不同列表的权重

RRF 通过数学公式巧妙地解决了这些问题,让排名靠前的文档获得更高的权重,同时兼顾多个列表的一致性。

3. 前置知识 #

在学习 RRF 之前,我们需要了解一些基础概念:

3.1 排名列表(Ranking List) #

排名列表是一个有序的文档序列,按照相关性或重要性从高到低排列。例如:

列表 A: [文档1, 文档2, 文档3]
列表 B: [文档3, 文档1, 文档4]

在这个例子中:

  • 列表 A 认为文档1最相关,文档2次之,文档3再次之
  • 列表 B 认为文档3最相关,文档1次之,文档4再次之

3.2 排名(Rank) #

排名是指文档在列表中的位置,通常从 1 开始计数:

  • 第 1 名:rank = 1(最相关)
  • 第 2 名:rank = 2
  • 第 3 名:rank = 3
  • ...

排名越小,表示文档越相关或越重要。

3.3 倒数(Reciprocal) #

倒数是指一个数的倒数,即 1 除以这个数。例如:

  • 1 的倒数是 1/1 = 1
  • 2 的倒数是 1/2 = 0.5
  • 3 的倒数是 1/3 ≈ 0.333

在 RRF 中,我们使用 1 / (k + rank) 作为分数,这样排名越靠前(rank 越小),分数越高。

3.4 Python 基础知识 #

需要了解:

  • 字典(dict):用于存储文档和分数的映射关系
  • 列表(list):用于存储排名列表
  • enumerate():用于同时获取索引和值
  • sorted():用于排序
  • lambda 函数:用于定义简单的排序规则

4. 核心思想与公式 #

RRF 的核心思想是:排名越靠前的文档,应该获得越高的分数;如果一个文档在多个列表中排名都靠前,它的最终分数应该更高。

RRF 的数学公式如下:

$$ \text{RRF_score}(d) = \sum_{i=1}^{n} \frac{1}{k + \text{rank}_i(d)} $$

其中:

  • $d$:文档(document)
  • $n$:排名列表的数量
  • $\text{rank}_i(d)$:文档 $d$ 在第 $i$ 个排名列表中的位置(从 1 开始计数)
  • $k$:平滑常数,通常取 60,用于避免排名为 1 时分数过大

公式的含义:

  1. 对于每个排名列表,计算文档的倒数排名分数:$1 / (k + \text{rank})$
  2. 将所有列表中该文档的分数相加,得到最终分数
  3. 如果文档在某个列表中未出现,则该项为 0(不参与求和)

为什么使用倒数?

  • 排名 1 的文档:分数 = 1/(60+1) ≈ 0.0164
  • 排名 2 的文档:分数 = 1/(60+2) ≈ 0.0161
  • 排名 10 的文档:分数 = 1/(60+10) ≈ 0.0143

可以看到,排名越靠前,分数越高,但差距会逐渐缩小。这样既能突出高排名文档,又不会让低排名文档完全被忽略。

5. 算法步骤 #

RRF 算法的执行步骤非常简单清晰:

5.1 准备输入 #

输入是多个已经排序的排名列表,每个列表中的文档按照相关性从高到低排列。

5.2 初始化分数字典 #

创建一个空字典,用于存储每个文档的累计分数。

5.3 遍历所有排名列表 #

对于每个排名列表:

  • 遍历列表中的每个文档
  • 计算该文档在当前列表中的 RRF 分数:$1 / (k + \text{rank})$
  • 将分数累加到该文档的总分中

5.4 排序输出 #

按照最终分数从高到低排序,返回排序后的文档列表。

注意事项:

  • 如果文档在某个列表中未出现,则该项不参与计算(相当于加 0)
  • 最终分数越高,表示文档越重要
  • 多个列表都排名靠前的文档,最终分数会更高

6. 代码实现 #

  • RRF
# 定义RRF(Reciprocal Rank Fusion)融合函数,rankings为多个排名列表,k为平滑常数
def reciprocal_rank_fusion(rankings, k=60):
    """
    实现 Reciprocal Rank Fusion 算法

    参数:
        rankings: 多个排名列表的列表,每个列表包含已排序的文档
        k: 平滑常数,默认值为 60

    返回:
        按 RRF 分数降序排列的 (文档, 分数) 元组列表
    """
    # 创建一个空字典存储所有文档的累加分数
    scores = {}

    # 遍历每个排名列表
    for ranking in rankings:
        # 从1开始枚举,对每个文档及其排名遍历
        for rank, doc in enumerate(ranking, 1):
            # 如果该文档还没有加入分数字典,则初始化为0
            if doc not in scores:
                scores[doc] = 0

            # 计算每个文档在此排序中的RRF分数,1/(k+rank)
            rrf_score = 1 / (k + rank)

            # 将RRF分数加到该文档的总分中
            scores[doc] += rrf_score

    # 按照分数从高到低对文档进行排序,获得排序结果
    sorted_results = sorted(scores.items(), key=lambda x: x[1], reverse=True)

    # 返回排序后的文档和分数组成的元组列表
    return sorted_results

# 定义关键词检索结果列表,模拟基于关键词检索的返回(如BM25)
keyword_results = [
    "doc1",
    "doc2",
    "doc3"
]

# 定义向量检索结果列表,模拟基于语义向量的检索结果
vector_results = [
    "doc3",
    "doc2",
    "doc4"
]

# 将关键词检索结果和向量检索结果进行RRF融合
fused_results = reciprocal_rank_fusion([keyword_results, vector_results], k=60)

# 打印关键词检索结果
print("关键词检索结果:", keyword_results)
# 打印向量检索结果
print("向量检索结果:", vector_results)
# 打印融合后的最终排序结果
print("\n融合后的最终结果:")
# 枚举输出融合后的文档及其RRF得分
for i, (doc, score) in enumerate(fused_results, 1):
    print(f"{i}. {doc} (分数: {score:.6f})")

7. 常见问题 #

7.1 k 值应该设置为多少? #

k 值是一个平滑常数,用于控制排名对分数的影响程度:

  • k 值越小:排名差异对分数的影响越大,高排名文档的优势更明显
  • k 值越大:排名差异对分数的影响越小,排名靠后的文档也能获得一定分数

建议:

  • 默认值:60(这是经过大量实验验证的最优值)
  • 可调范围:30-100
  • 对于大多数场景,使用默认值 60 即可,无需调整

7.2 如果文档在某个列表中未出现怎么办? #

如果文档在某个排名列表中未出现,则该项不参与计算(相当于加 0)。例如:

  • 文档 A 在列表 1 中排名第 2,在列表 2 中未出现
  • 文档 A 的分数 = 1/(60+2) + 0 = 1/62

7.3 可以处理多少个排名列表? #

理论上可以处理任意多个列表,但实际应用中:

  • 推荐范围:2-10 个列表
  • 最佳效果:3-5 个列表
  • 列表过多可能导致计算复杂度增加,但效果提升有限

7.4 RRF 分数需要归一化吗? #

通常不需要。RRF 分数本身已经具有可比性,直接按分数排序即可。如果确实需要归一化(例如与其他算法结合),可以使用:

# 归一化示例(可选)
def normalize_scores(results):
    """
    将 RRF 分数归一化到 [0, 1] 区间
    """
    if not results:
        return []

    # 获取最高分和最低分
    max_score = max(score for _, score in results)
    min_score = min(score for _, score in results)

    # 如果所有分数相同,返回原结果
    if max_score == min_score:
        return [(doc, 1.0) for doc, _ in results]

    # 归一化公式:(score - min) / (max - min)
    normalized = [
        (doc, (score - min_score) / (max_score - min_score))
        for doc, score in results
    ]

    return normalized

# 使用示例
results = reciprocal_rank_fusion([list1, list2], k=60)
normalized = normalize_scores(results)
print("归一化后的分数:")
for doc, score in normalized:
    print(f"{doc}: {score:.4f}")

8. 总结 #

Reciprocal Rank Fusion (RRF) 是一种简单而有效的排名列表融合算法,具有以下特点:

优势:

  1. 简单易用:算法逻辑清晰,实现简单,只需几行代码
  2. 无需训练:不需要训练数据,可以直接使用
  3. 参数少:只需要一个参数 k,且通常使用默认值 60
  4. 效果稳定:在各种场景下都能取得不错的效果
  5. 计算高效:时间复杂度为 O(n),n 为所有列表中的文档总数

适用场景:

  • 混合检索系统(关键词 + 向量检索)
  • 多搜索引擎结果融合
  • 推荐系统多策略融合
  • 联邦搜索(多数据源结果合并)

核心思想:

  • 使用倒数排名作为分数,排名越靠前分数越高
  • 将多个列表中的分数累加,得到最终分数
  • 按最终分数排序,得到融合后的排名

使用建议:

  • k 值使用默认值 60,通常无需调整
  • 适合融合 2-10 个排名列表
  • 确保输入列表已经按相关性排序
  • 直接使用 RRF 分数排序即可,通常不需要归一化
← 上一节 requests 下一节 Runnable →

访问验证

请输入访问令牌

Token不正确,请重新输入