Tan Kim

reciprocal-rank-fusion

Reciprocal Rank Fusion (RRF)

Reciprocal Rank Fusion(RRF)은 복수의 retriever가 반환한 결과를 병합할 때, 절대 점수 대신 각 retriever에서의 순위(rank)를 기반으로 통합 점수를 계산하는 방식이다. 점수 스케일 차이를 완전히 무시하기 때문에 Score Fusion의 정규화 문제를 원천적으로 해결하며, 실용적이면서도 강건한(robust) 성능으로 널리 채택된다.

핵심 공식

RRF 점수는 각 retriever에서의 순위에 역수를 취해 합산한다.

$$\text{RRF}(d) = \sum_{r \in R} \frac{1}{k + \text{rank}_r(d)}$$

  • $R$: retriever 집합
  • $\text{rank}_r(d)$: retriever $r$에서 문서 $d$의 순위 (1-indexed)
  • $k$: 순위 영향력을 조절하는 상수 (일반적으로 60)

k=60은 경험적으로 검증된 기본값이다. 상위 랭크의 영향력을 완화해 단일 retriever에서 1위인 문서가 다른 retriever에서 전혀 등장하지 않더라도 지나치게 유리해지지 않도록 한다.

구현

def reciprocal_rank_fusion(
    results: list[list[Document]],
    k: int = 60
) -> list[Document]:
    rrf_scores: dict[str, float] = {}
    doc_map: dict[str, Document] = {}
 
    for result_list in results:
        for rank, doc in enumerate(result_list, start=1):
            rrf_scores[doc.id] = rrf_scores.get(doc.id, 0) + 1 / (k + rank)
            doc_map[doc.id] = doc
 
    ranked_ids = sorted(rrf_scores, key=rrf_scores.get, reverse=True)
    return [doc_map[id] for id in ranked_ids]

Score Fusion과의 비교

항목 Score Fusion RRF
입력 정규화된 점수 순위
스케일 문제 정규화 필요 없음
이상값 민감도 높음 낮음
직관성 높음 중간
튜닝 파라미터 가중치 + 정규화 방법 k값 하나
실전 성능 케이스에 따라 다름 대체로 안정적

RRF의 강점은 단순성과 강건성이다. 정규화 방법이나 retriever별 가중치를 튜닝하지 않아도 안정적인 성능을 낸다.

k 값의 효과

k 값이 클수록 순위의 영향력이 평탄해진다. k=0이면 1위 문서가 압도적으로 유리하고, k가 커질수록 10위와 1위의 점수 차이가 줄어든다.

k=0:  rank 1 → 1.0,  rank 10 → 0.10  (10배 차이)
k=60: rank 1 → 0.016, rank 10 → 0.014 (1.14배 차이)

k=60에서는 상위 랭크 간 점수 차이가 크지 않으므로, 여러 retriever에서 중간 순위에 반복 등장하는 문서가 한 retriever에서만 1위인 문서를 앞설 수 있다. 이 특성이 앙상블 효과를 만든다.

가중치 부여 (Weighted RRF)

retriever별로 중요도 차이를 두고 싶을 때 가중 RRF를 사용한다.

def weighted_rrf(
    results: list[list[Document]],
    weights: list[float],
    k: int = 60
) -> list[Document]:
    rrf_scores: dict[str, float] = {}
    doc_map: dict[str, Document] = {}
 
    for result_list, weight in zip(results, weights):
        for rank, doc in enumerate(result_list, start=1):
            rrf_scores[doc.id] = rrf_scores.get(doc.id, 0) + weight / (k + rank)
            doc_map[doc.id] = doc
 
    ranked_ids = sorted(rrf_scores, key=rrf_scores.get, reverse=True)
    return [doc_map[id] for id in ranked_ids]

실전 활용

RRF는 Hybrid Retrieval(BM25 + Dense)의 결과 병합에서 사실상 표준으로 사용된다. Elasticsearch, Weaviate, Qdrant 등 주요 벡터 DB가 Hybrid Search 기능에 RRF를 내장하고 있다.

관련 개념

  • Score Fusion: 정규화된 절대 점수를 가중합산하는 병합 방식
  • Naive Merge: 순위·점수 없이 단순 concatenation하는 방식
  • Reranker-centric: RRF 등으로 1차 병합 후 reranker로 최종 정렬하는 방식