RRF 是什么
RRF(Reciprocal Rank Fusion,倒数排名融合)用排名而不是原始分数来融合多路召回:每个候选的得分等于各路「一除以(常数加排名)」之和。常数 k 通常取 60,起缓冲作用,避免某一路的第一名过度主导结果。它最大的好处是免去分数归一化,让量纲完全不同的检索系统也能安全合并。
基本信息
| 公式 | score(d) = Σ_i w_i / (k + rank_i(d)) |
|---|---|
| k | 缓冲常数,论文取 60 |
| 权重 | 可按路加权,默认各路等权 |
| 优点 | 免分数归一化,分数不可比的系统也能合并 |
| 局限 | 只看排名,丢掉分数里蕴含的强弱信息 |
RRF 融合实现
RRF 融合实现
def rrf(rankings, k=60, weights=None):
scores = {}
for i, ranked in enumerate(rankings):
w = 1.0 if weights is None else weights[i]
for rank, doc_id in enumerate(ranked, start=1):
scores[doc_id] = scores.get(doc_id, 0.0) + w / (k + rank)
return sorted(scores, key=scores.get, reverse=True)使用要点
- 先确认两路的文档标识统一,否则同一片段会被当成两个候选
- 各路候选深度要接近:一路只给十几条、另一路给上百条,融合结果会被后者主导
- 排名从 1 开始计数,从 0 开始会让头部得分出现偏差
- k 值按候选深度调:候选很多时可略大以避免头部扎堆
- 融合后按内容哈希去重再截断,控制进入重排的条数
与分数融合的对比
| 对比项 | RRF | 加权分数融合 |
|---|---|---|
| 输入 | 排名 | 归一化后的分数 |
| 调参 | 几乎不用调,跨系统稳定 | 权重敏感,需要反复试验 |
| 信息量 | 丢弃分数强弱 | 保留分数强弱 |
| 适用 | 两路分数不可比时首选 | 同源或可比的分数上更精细 |
RRF 只看排名:某一路候选很少或分数标定很差时,融合后仍会退化为单路主导,记得与单路基线做对比。
不要一边用 RRF 一边又叠加原始分数做二次排序:两套口径混用会让排序结果难以解释,出问题时无法归因。
落地建议
- 先记录各路的独立召回率,确认两路都有贡献再引入融合
- 融合结果与单路结果都打印出来做人工对比,样本不必多,二十条就能看出问题
- 权重调整一次只改一路,避免同时变动导致无法归因
- 把融合前后的候选条数写进日志,条数异常往往先于效果异常出现
- 上线后保留关闭融合的开关,出现排序异常时可快速退化为单路