
RRF는 Reciprocal Rank Fusion의 약자로, 여러 검색 결과 목록을 하나의 최종 순위로 합치는 랭킹 결합 방법입니다.
한국어로는 “역순위 융합” 정도로 이해할 수 있습니다.
예를 들어 하나의 검색 시스템에서 다음 두 가지 검색 결과를 동시에 얻었다고 가정합니다.
- BM25 기반 검색 결과
- 벡터 임베딩 기반 검색 결과
BM25는 정확한 키워드 일치에 강하고, 벡터 검색은 의미적 유사성에 강합니다.
그렇다면 두 결과를 잘 합치면 키워드 검색과 의미 검색의 장점을 함께 사용할 수 있습니다. 이때 자주 쓰이는 방식이 RRF입니다.
핵심 아이디어
RRF의 핵심은 단순합니다. 여러 검색 결과에서 높은 순위에 자주 등장하는 문서를 더 중요하게 봅니다.
즉, 어떤 문서가 BM25 결과에서도 상위권이고, 벡터 검색 결과에서도 상위권이라면 최종 순위에서 높게 올립니다.
반대로 한 검색기에서만 아주 높은 순위이고 다른 검색기에서는 전혀 보이지 않는 문서는 상대적으로 덜 강하게 평가합니다.
RRF는 각 검색기의 실제 점수값을 직접 비교하지 않습니다.
대신 순위(rank) 만 사용합니다.
RRF 공식
RRF(d) = Σ 1 / (k + rank_i(d))
d: 문서
rank_i(d): i번째 검색 시스템에서의 순위
k: 상수 (보통 60)
여기서 의미는 다음과 같습니다.
- d는 점수를 계산할 문서입니다.
- rankᵢ(d)는 i번째 검색 결과 목록에서 문서 d가 차지한 순위입니다.
- k는 순위의 영향력을 완화하는 상수입니다.
- 보통 k = 60을 많이 사용합니다.
예를 들어 어떤 문서가 다음과 같이 검색되었다고 가정합니다.
BM25 순위: 2위
벡터 검색 순위: 5위
k = 60이면 RRF 점수는 다음과 같습니다.
RRF_score = 1 / (60 + 2) + 1 / (60 + 5)
= 1 / 62 + 1 / 65
문서가 여러 검색 결과에서 상위권에 있을수록 RRF 점수는 커집니다.
직관적 이해
각 검색 결과의 순위(rank)만 사용하고, 실제 점수는 무시합니다. 그래서 점수 체계가 다른 검색들을 자연스럽게 결합할 수 있습니다.
예시:
- 문서 A는 벡터 검색 1위, BM25 5위
- 문서 B는 벡터 검색 2위, BM25 1위
RRF (k=60):
- A: 1/(60+1) + 1/(60+5) = 0.0164 + 0.0154 = 0.0318
- B: 1/(60+2) + 1/(60+1) = 0.0161 + 0.0164 = 0.0325
→ B가 1위 (양쪽에서 모두 상위)
k 값의 의미
k는 순위에 대한 페널티 강도를 조절합니다.
k가 작을수록 (예: k=10):
- 1위와 10위의 차이가 큼
- "1위가 절대 우위"
k가 클수록 (예: k=60):
- 1위와 10위의 차이가 작아짐
- "다양한 후보 고려"
→ 일반적으로 k=60이 권장 (Cormack 논문 실험 기준)
RRF의 장점
RRF의 가장 큰 장점은 서로 다른 검색 점수를 정규화하지 않아도 된다는 점입니다. BM25, 벡터 검색, 메타데이터 검색, 추천 모델 등은 각자 점수의 범위와 의미가 다릅니다. 이 점수들을 그대로 합치려면 정규화 방법을 잘 설계해야 합니다. 하지만 정규화는 도메인에 따라 흔들리기 쉽습니다. RRF는 점수를 버리고 순위만 사용합니다. 그래서 구현이 단순하고, 검색기마다 점수 스케일이 달라도 안정적으로 결합할 수 있습니다. 또 다른 장점은 상위 랭킹을 자연스럽게 강조한다는 점입니다. 1위와 10위의 차이는 크게 반영하지만, 90위와 100위의 차이는 작게 반영합니다. 검색에서는 보통 상위 결과가 훨씬 중요하기 때문에 이 성질이 잘 맞습니다. 또한 RRF는 하이브리드 검색에서 매우 잘 어울립니다.
RRF의 약점
RRF는 단순하고 강력하지만 한계도 있습니다. 첫째, 실제 점수 차이를 반영하지 못합니다. 예를 들어 BM25 1위 문서가 2위 문서보다 압도적으로 높은 점수를 받았더라도, RRF는 둘을 단순히 1위와 2위의 차이로만 봅니다. 둘째, 검색 결과 목록에 포함되지 않은 문서는 점수를 받지 못합니다. 따라서 각 검색기에서 가져오는 후보 수가 너무 적으면 좋은 문서가 최종 결합 대상에서 빠질 수 있습니다. 셋째, 모든 검색기를 동일하게 신뢰하는 구조가 될 수 있습니다. 기본 RRF는 각 검색 결과 목록을 같은 비중으로 더합니다. 특정 검색기가 더 신뢰할 만한 경우에는 weighted RRF처럼 가중치를 추가해야 합니다.
RRF 구현
from collections import defaultdict
def reciprocal_rank_fusion(
search_results: list[list[str]],
k: int = 60
) -> list[tuple[str, float]]:
"""
여러 검색 결과를 RRF로 결합한다.
search_results: 각 검색기의 결과 (문서 ID 순위 리스트)
"""
scores = defaultdict(float)
for results in search_results:
for rank, doc_id in enumerate(results, start=1):
scores[doc_id] += 1.0 / (k + rank)
# 점수 내림차순 정렬
return sorted(scores.items(), key=lambda x: x[1], reverse=True)
# 사용 예시
vector_results = ["doc_5", "doc_2", "doc_8", "doc_1", "doc_3"]
bm25_results = ["doc_2", "doc_5", "doc_1", "doc_7", "doc_4"]
fused = reciprocal_rank_fusion([vector_results, bm25_results], k=60)
for doc_id, score in fused:
print(f"{score:.4f} {doc_id}")
# 출력:
# 0.0327 doc_2 (벡터 2위 + BM25 1위)
# 0.0325 doc_5 (벡터 1위 + BM25 2위)
# 0.0319 doc_1
# 0.0161 doc_8
# ...
Qdrant의 네이티브 RRF 지원
Qdrant 1.10부터 RRF가 네이티브로 지원됩니다.
from qdrant_client import QdrantClient
from qdrant_client.models import Prefetch, FusionQuery, Fusion
client = QdrantClient(host="localhost", port=6333)
# Hybrid 검색: 벡터 + BM25(sparse) → RRF 결합
results = client.query_points(
collection_name="docs",
prefetch=[
Prefetch(
query=dense_embedding,
using="dense",
limit=20
),
Prefetch(
query=sparse_vector, # BM25 가중치 벡터
using="sparse",
limit=20
),
],
query=FusionQuery(fusion=Fusion.RRF),
limit=10
)
현대 검색에서 RRF의 위치
RRF는 특히 하이브리드 검색에서 자주 사용됩니다. 하이브리드 검색은 서로 다른 검색 방식의 장점을 함께 활용하는 방식입니다. 대표적인 구조는 다음과 같습니다.
사용자 질문
-> BM25 검색
-> 벡터 검색
-> RRF로 결과 결합
-> reranker로 최종 재정렬
-> LLM 또는 사용자에게 전달
BM25는 정확한 키워드 매칭에 강합니다. 벡터 검색은 의미적으로 비슷한 문서를 찾는 데 강합니다. RRF는 이 둘의 결과를 안정적으로 합칩니다. 이후 Cohere Rerank 같은 reranker를 붙이면, 결합된 후보 문서를 질문과 다시 비교해 최종 순위를 더 정밀하게 만들 수 있습니다.
마무리
RRF는 여러 검색 결과를 결합하는 매우 단순한 방법입니다. 하지만 그 단순함 때문에 실무에서 강력합니다. BM25와 벡터 검색은 서로 다른 점수 체계를 가지고 있습니다. 이 점수들을 억지로 하나의 기준으로 맞추려면 복잡한 정규화가 필요합니다. 하지만 RRF는 점수를 버리고 순위만 사용함으로써 이 문제를 깔끔하게 피해갑니다. 물론 RRF가 모든 것을 해결해 주지는 않습니다. 후보 검색 단계에서 좋은 문서를 충분히 가져와야 하고, 검색기별 중요도가 다르다면 가중치 조정도 필요합니다. 그럼에도 RRF는 하이브리드 검색을 시작할 때 가장 먼저 고려할 만한 결합 방식입니다. 구현은 간단하지만 효과는 크고, BM25와 벡터 검색의 장점을 자연스럽게 이어주는 좋은 연결고리입니다.
'프로그래밍 PROGRAMMING > 인공지능 AI' 카테고리의 다른 글
| Reranker란 무엇인가 — Bi-encoder vs Cross-encoder (0) | 2026.04.28 |
|---|---|
| BM25란 무엇인가 — 30년 묵은 알고리즘이 여전히 강한 이유 (1) | 2026.04.26 |
| RAG 챗봇이 느리고 비싸지는 이유 — 검색, 임베딩, LLM 호출 비용 줄이는 방법 (0) | 2026.04.24 |
| 왜 PDF를 넣으면 답변 품질이 떨어질까 — 표, 이미지, 페이지 구조를 AI가 못 읽는 문제 (1) | 2026.04.23 |
| Claude Design 시작하기: 말로 만들고, 대화로 다듬는 새로운 디자인 방식 (1) | 2026.04.22 |