C.W.K.
Stream
Lesson 03 of 05 · published

벡터와 RRF

~13 min · vectors, rrf, fusion, hybrid, embeddings

Level 0불 꺼진 심지
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"키워드 검색은 네가 친 단어를 찾아. 벡터 검색은 네가 뜻한 의미를 찾아. 융합은 둘 중 고르길 멈추는 법이야."

벡터가 더하는 것

임베딩 모델은 텍스트 조각을 고차원 공간의 한 점으로 바꿔 — Lantern 은 1024 차원 벡터를 내는 걸 써. 마법 같은 속성: 이 비슷한 텍스트가 서로 가까이 내려앉아, 단어를 하나도 안 공유해도. "시장 패닉에 어떻게 침착함을 유지하지" 를 검색하면 벡터 검색이 "폭락 중의 감정 규율" 에 관한 구절을 떠올릴 수 있어, 키워드가 하나도 안 겹쳐서 BM25 는 완전히 놓칠 그거. 벡터는 의미로 검색하고, 그게 바로 키워드 검색이 눈먼 경우야.

융합 문제

이제 둘 다 돌려서 순위 매긴 목록 둘이 있어: BM25 의 키워드 히트와 벡터 저장소의 시맨틱 히트. 병합된 순위 하나를 원해. 함정이 여기 있어: 점수가 비교 불가능한 척도에 살아. BM25 점수와 cosine 유사도는 같은 종류의 숫자가 아냐 — 그냥 못 더하고, 공유 범위로 정규화하는 건 까다롭고, 부서지기 쉽고, 데이터가 바뀌면 계속 재튜닝해야 해. 순진한 점수 섞기가 하이브리드 검색이 조용히 틀어지는 곳이야.

RRF: 점수 아니라 순위로 융합

Reciprocal Rank Fusion 은 아이디어 하나로 그 난장판을 통째로 비껴가: 점수를 무시하고, 순위 위치만 써. 각 결과가 나타나는 모든 목록에서 1 / (k + rank) 기여를 받아, 여기서 rank 는 그 목록에서의 위치고 k 는 작은 상수(60 이 닳고 닳은 기본값). 그 기여를 목록 넘어 합치고 정렬해. 어느 목록에서든 #1 인 문서가 점수를 잘 받고, 둘 다에서 높이 순위된 문서가 가장 잘 받아. 점수 정규화는 절대 안 일어나, 점수가 산수에 아예 안 들어오니까.

순위를 결합할 땐 raw 점수가 아니라 순위로 융합해. 다른 검색 방법은 비교하라고 만들어진 적 없는 척도의 점수를 내. 반면 순위 위치는 보편적이야 — 3등은 어느 목록에서든 같은 걸 뜻해. RRF 는 '이 비교 불가능한 시스템들을 결합해' 를 튜닝 악몽에서 그냥 되는 두 줄짜리 파라미터-가벼운 함수로 바꿔. 검색 전체에서 가치-대-복잡도 비율이 가장 높은 것 중 하나야.

이게 영리한 점수 산수를 이기는 이유

최적 가중치를 학습하거나, 점수 분포를 보정하거나, 융합 모델을 훈련할 수 있어. RRF 는 물어: 왜? 파라미터가 사실상 하나고, 학습이 필요 없고, 심하게 다른 점수 척도에 강인하고, 경험적으로 훨씬 화려한 방법과 어깨를 나란히 해. 그 조합 — 거의 공짜, 거의 파라미터 없음, 깨기 어려움 — 이 진지한 하이브리드 엔진에서 기본 융합인 이유야. 벡터 절반은 임베딩 서버도 필요해서, 그게 꺼지면 하이브리드는 키워드-only 로 degrade 하고 그렇다고 말해; RRF 는 그냥 건네받은 목록이 뭐든 융합해.

Code

RRF 통째 — 점수를 절대 비교하지 않고 두 순위를 융합·python
def reciprocal_rank_fusion(ranked_lists: list[list[str]], k: int = 60) -> list[str]:
    """Fuse several ranked lists of chunk_ids into one — by RANK, never by score."""
    scores: dict[str, float] = {}
    for lst in ranked_lists:                 # e.g. [bm25_ids, vector_ids]
        for rank, chunk_id in enumerate(lst, start=1):
            scores[chunk_id] = scores.get(chunk_id, 0.0) + 1.0 / (k + rank)
    # Highest fused score first. Appearing high in BOTH lists wins.
    return sorted(scores, key=scores.get, reverse=True)

# Note what never appears: no BM25 score, no cosine similarity, no normalization.
# The two systems' scores were incomparable — so we simply never compared them.
fused = reciprocal_rank_fusion([bm25_hits, vector_hits], k=60)

External links

Exercise

짧은 순위 목록 둘을 손으로 써 — 같은 쿼리에 키워드 검색과 벡터 검색이 각각 다섯 결과를 돌려주고, 일부 겹치고 일부 고유하다 상상해. 종이에 k=60 으로 RRF 를 적용해: 각 결과에 나타나는 각 목록에서 1/(60+rank) 를 주고, 합치고, 재정렬해. 어느 결과가 오르는지 봐: 두 방법 다 좋아한 것들. 그 떠오르는 합의가 융합의 가치 전부야.
Hint
한 목록에만 나타나는 결과는 1/(60+rank) 항 하나를 받고, 두 목록에 다 있는 결과는 두 항이 합쳐져. 그래서 두 검색기가 동의하는 항목이 위로 떠 — 독립된 두 방법이 둘 다 보증한 것들이니까.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.