Reciprocal Rank Fusion (RRF) 算法,它是一种用于合并多个排名列表的方法。下面我将详细解释这个算法的工作原理和实现细节。
算法概述
RRF 是一种简单但有效的排名融合技术,它通过将多个排名列表中的项目位置进行加权组合,生成一个统一的排名。它的主要特点是:
-
不需要预先知道各个排名列表的质量
-
对排名靠前的项目给予更高的权重
-
对异常值有较好的鲁棒性
算法参数
-
lists
: 一个包含多个排名列表的列表,每个子列表都是一个有序的项目集合 -
k
: 一个常数,用于控制低排名项目的贡献(默认值为60)
算法步骤详解
1. 初始化分数字典
rrf_scores = {}
创建一个空字典来存储每个项目的累积分数。
2. 遍历每个排名列表
for rank_list in lists:
对于输入的每一个排名列表进行处理。
3. 遍历列表中的每个项目
for rank, item in enumerate(rank_list, start=1):
使用 enumerate
遍历列表中的每个项目,rank
从1开始表示项目在当前列表中的位置。
4. 计算并累加RRF分数
if item in rrf_scores:
rrf_scores[item] += 1 / (k + rank)
else:
rrf_scores[item] = 1 / (k + rank)
对于每个项目,计算其RRF分数并累加:
-
如果在多个列表中出现,分数会累加
-
RRF分数计算公式:
1 / (k + rank)
-
rank
是项目在当前列表中的位置(从1开始) -
k
是常数,用于控制低排名项目的贡献
-
5. 按分数排序
sorted_items = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
将所有项目按照累积的RRF分数从高到低排序。
6. 格式化输出结果
result = [{"score": score, "document": item, "index": i} for i, (item, score) in enumerate(sorted_items) if item]
将排序后的结果转换为字典列表,每个字典包含:
-
score
: 项目的RRF分数 -
document
: 项目本身 -
index
: 项目在最终排名中的位置
算法特点
-
排名靠前的项目权重更大:由于使用倒数函数,排名越靠前(rank越小),贡献的分数越大
-
参数k的作用:
-
较大的k值会减小不同排名间的分数差异
-
较小的k值会放大高排名项目的优势
-
默认值60是一个经验值,可以根据具体场景调整
-
-
多列表融合:如果一个项目在多个列表中排名靠前,它的总分将更高
示例说明
假设有两个排名列表:
-
列表1: ["A", "B", "C"]
-
列表2: ["B", "A", "D"]
计算过程(k=60):
列表1:
-
A: 1/(60+1) ≈ 0.0164
-
B: 1/(60+2) ≈ 0.0161
-
C: 1/(60+3) ≈ 0.0159
列表2:
-
B: 1/(60+1) ≈ 0.0164
-
A: 1/(60+2) ≈ 0.0161
-
D: 1/(60+3) ≈ 0.0159
累加结果:
-
A: 0.0164 + 0.0161 ≈ 0.0325
-
B: 0.0161 + 0.0164 ≈ 0.0325
-
C: 0.0159
-
D: 0.0159
最终排序可能是[A, B, C, D]或[B, A, C, D](取决于具体实现)
应用场景
RRF常用于:
-
搜索引擎结果融合
-
推荐系统多策略结果合并
-
任何需要合并多个排名列表的场景
这种方法的优势在于它简单有效,不需要训练数据,且对各个输入列表的质量不敏感。
具体代码如下:
def rrf_rescore(lists: list, k=60):
rrf_scores = {}
for rank_list in lists:
for rank, item in enumerate(rank_list, start=1):
if item in rrf_scores:
rrf_scores[item] += 1 / (k + rank)
else:
rrf_scores[item] = 1 / (k + rank)
sorted_items = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
result = [{"score": score, "document": item, "index": i} for i, (item, score) in enumerate(sorted_items) if item]
return result
if __name__ == "__main__":
lists = [
["a", "b", "c", "d"],
["b", "c", "e"],
["c", "f"],
["a", "c", "g"]
]
k = 60
result = rrf_rescore(lists, k)
print(result)
运行结果:
