Skip to main content
Dense 검색은 의미를 이해하지만 정확한 키워드를 놓치고, BM25는 키워드를 정확히 찾지만 동의어를 인식하지 못합니다. 하이브리드 검색은 두 방식의 결과를 결합하여 서로의 약점을 보완합니다. 이 페이지에서는 왜 단순 결합이 어려운지, RRF가 이를 어떻게 해결하는지를 구체적으로 다룹니다.

왜 둘 다 필요한가: 구체적 실패 사례

Dense만 사용할 때의 실패

질문: "제품번호 ABC-123의 스펙 시트"
문서: "ABC-123 제품 사양서 — 무게: 2.3kg, 전압: 220V..."

Dense 검색 결과:
1위: "제품 사양 관리 가이드" (cosine: 0.89)     ← "스펙 시트"의 의미는 이해
2위: "제품 카탈로그 개요" (cosine: 0.87)         ← 관련은 있지만 ABC-123이 아님
3위: "ABC-123 제품 사양서" (cosine: 0.85)       ← 정작 원하는 문서가 3위
Dense 검색은 “스펙 시트”와 “사양서”의 의미적 유사성은 잘 잡지만, “ABC-123”이라는 고유 식별자의 정확한 매칭 에는 약합니다.

Sparse(BM25)만 사용할 때의 실패

질문: "고장이 자주 나는 부품을 교체하는 절차"
문서: "결함률이 높은 컴포넌트의 교환 프로세스"

BM25 검색 결과:
1위: "부품 고장 이력 조회" (BM25: 12.3)         ← "고장", "부품" 키워드 매칭
2위: "자주 묻는 질문 (FAQ)" (BM25: 8.7)          ← "자주" 키워드 매칭
→ 정작 원하는 문서는 검색되지 않음!             ← "결함률", "컴포넌트", "교환"은 매칭 실패
BM25는 “고장”=“결함” , “부품”=“컴포넌트” , “교체”=“교환” 같은 동의어 관계를 전혀 이해하지 못합니다.
주의 실무에서 가장 흔한 실수: “Dense 검색이면 충분하다”고 판단하고 BM25를 생략하는 것입니다. 고유명사, 제품 코드, 오류 메시지 등 정확한 문자열 매칭이 중요한 쿼리 가 전체의 20-40%를 차지하는 경우가 많습니다.

점수 결합의 어려움: 왜 단순히 더할 수 없는가

두 검색 결과를 합치려면 점수를 비교해야 합니다. 하지만:
BM25 점수 범위:  0 ~ 25+ (이론적으로 상한 없음)
Cosine 점수 범위: 0 ~ 1.0

문서 A: BM25=15.2, Cosine=0.72
문서 B: BM25=3.1, Cosine=0.95

단순 합산: A=15.92, B=4.05 → A 승리 (BM25가 압도)
가중 합산 (0.5:0.5): A=7.96, B=1.99 → 여전히 A 승리
BM25 점수의 절대값이 훨씬 크기 때문에, 어떤 가중치를 사용해도 BM25가 결과를 지배하게 됩니다. 점수 정규화(Min-Max, Z-score)를 시도할 수 있지만, 쿼리마다 점수 분포가 달라 안정적인 정규화가 어렵습니다. 구체적으로 왜 어려운지 보면: Min-Max 정규화((x - min) / (max - min))를 적용하려면 해당 쿼리의 최솟값/최댓값을 알아야 합니다. 하지만 쿼리 “Delta Lake”는 BM25 점수가 08 범위로 좁고, “the data”는 025 범위로 넓습니다. 같은 BM25 점수 5.0이라도 전자에서는 상위권이고 후자에서는 하위권입니다. Z-score 정규화((x - μ) / σ)도 마찬가지로, 점수 분포의 평균(μ)과 표준편차(σ)가 쿼리마다 크게 달라 쿼리 간 비교가 불안정 합니다. 이것이 점수 기반 결합이 실무에서 잘 동작하지 않는 근본적 이유입니다.

RRF (Reciprocal Rank Fusion) 상세

핵심 아이디어: 점수 대신 순위(rank)를 사용

RRF는 점수의 절대값을 무시하고, 각 검색 방식에서의 순위 만을 사용합니다. 순위는 1, 2, 3, … 으로 항상 같은 스케일이므로 결합이 자연스럽습니다.

수식

RRF_score(d) = Σ 1/(k + rank_i(d))

- d: 문서
- k: 상수 (기본값 60)
- rank_i(d): i번째 검색 방식에서 문서 d의 순위 (1부터 시작)
- Σ: 모든 검색 방식에 대해 합산

k=60의 의미

k 값은 상위 순위와 하위 순위의 차이를 얼마나 부드럽게 할 것인지 조절합니다:
k=60일 때:
1위의 기여: 1/(60+1) = 0.01639
2위의 기여: 1/(60+2) = 0.01613  → 1위와 차이: 1.6%
5위의 기여: 1/(60+5) = 0.01538  → 1위와 차이: 6.2%
50위의 기여: 1/(60+50) = 0.00909 → 1위와 차이: 44.5%

k=1일 때:
1위의 기여: 1/(1+1) = 0.500
2위의 기여: 1/(1+2) = 0.333  → 1위와 차이: 33.4%
→ 순위 차이가 극적으로 반영됨 (너무 공격적)
k가 클수록 순위 간 차이가 완만해지고, 작을수록 1위에 대한 보상이 극적으로 커집니다. k=60은 왜 60인가? 이 값은 2009년 Cormack 등의 논문(“Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods”)에서 제안되었으며, 다양한 TREC(Text REtrieval Conference) 벤치마크에서 k=10~100 범위를 실험한 결과 60 근처에서 가장 안정적인 성능을 보였습니다. 직관적으로 설명하면, k=60은 상위 60위까지의 순위 차이를 적당히 부드럽게 만들면서도 하위권과의 차별은 유지 하는 균형점입니다. k가 너무 작으면(예: 1) 1위와 2위의 점수 차이가 너무 커서 한 검색 방식의 1위가 결과를 지배하고, k가 너무 크면(예: 1000) 모든 순위의 점수가 거의 같아져 순위 정보가 사실상 무시됩니다.

예시: 어느 문서가 최종 상위?

검색 결과:
- 문서 X: BM25 1위, Dense 5위
- 문서 Y: BM25 3위, Dense 2위
- 문서 Z: BM25 2위, Dense 10위

RRF 계산 (k=60):
문서 X: 1/(60+1) + 1/(60+5) = 0.01639 + 0.01538 = 0.03177
문서 Y: 1/(60+3) + 1/(60+2) = 0.01587 + 0.01613 = 0.03200 ← 최고점!
문서 Z: 1/(60+2) + 1/(60+10) = 0.01613 + 0.01429 = 0.03042

최종 순위: Y > X > Z
문서 Y가 승리합니다. BM25에서 1위인 문서 X보다, 양쪽에서 고르게 상위에 있는 문서 Y가 더 높은 점수를 받습니다. 이것이 RRF의 핵심 철학입니다: 한쪽에서만 극단적으로 높은 것보다, 양쪽에서 골고루 인정받는 것이 더 낫다.
참고 비유: 두 명의 심사위원이 독립적으로 순위를 매긴 후, 양쪽 모두에게 높은 평가를 받은 후보를 최종 선발합니다. 한 심사위원에게만 극찬받은 후보보다, 두 심사위원 모두에게 좋은 평가를 받은 후보가 선발됩니다.

Databricks Vector Search의 하이브리드 검색

Databricks Vector Search는 query_type="hybrid" 설정으로 하이브리드 검색을 지원합니다:
results = vs_index.similarity_search(
    query_text="제품번호 ABC-123 스펙",
    columns=["content", "doc_id"],
    num_results=5,
    query_type="hybrid"  # Dense + BM25 + RRF 자동 적용
)

현재 제약 사항과 해결

제약설명해결 방법
BM25 가중치 조절 불가Dense:Sparse 비율을 직접 조절할 수 없음현재는 기본 RRF에 의존
한국어 토큰화내장 BM25가 공백 기반 토큰화 사용Kiwi 전처리 후 토큰화된 텍스트를 별도 컬럼으로 저장
필터 + 하이브리드필터 조건과 하이브리드를 동시에 적용 시 성능 이슈 가능필터 범위를 좁게 설정
참고 실무 팁: 한국어 환경에서 Databricks VS의 하이브리드 검색 BM25 품질을 높이려면, 원본 텍스트와 함께 Kiwi로 형태소 분석한 텍스트를 별도 컬럼에 저장하고, 해당 컬럼을 BM25 소스로 지정하는 방법을 고려하세요. 자세한 내용은 한국어 RAG 최적화를 참고하세요.

다음: 검색 결과를 더 정밀하게 정렬하고 싶다면? → Re-ranking