Reranker

2026-04-07

개념

Reranker는 [[RAG]]의 2차 평가 단계다. 1차로 벡터 검색(Bi-Encoder)으로 후보를 넓게 뽑고, 2차로 Cross-Encoder가 질문-문서 쌍을 직접 비교해서 관련성 점수를 매긴다. 정확도-속도 트레이드오프를 단계별로 푸는 방식.

왜 필요한가

벡터 검색만으로는 "의미가 비슷해 보이지만 정답은 아닌" 문서가 top-k에 올라올 수 있다. Reranker는 이걸 걸러내는 레이어다. 벡터 검색이 100점 만점에 70점 정도로 관련 문서를 추려주면, Cross-Encoder가 그중에서 진짜 관련 있는 걸 95점 수준으로 재정렬한다.

Bi-Encoder (기본 벡터 검색)

질문과 문서를 따로 임베딩해서 거리를 비교한다.

질문 → 벡터 A
문서 → 벡터 B
→ cos(A, B) 거리 비교

문서 임베딩은 미리 계산해둘 수 있어서 빠르다. 하지만 질문과 문서를 독립적으로 봐서 미묘한 관련성을 놓친다.

Cross-Encoder (Reranker)

질문과 문서를 하나로 합쳐서 모델에 넣는다. 관련성 점수(0~1)를 직접 출력한다.

[질문 + 문서] → 모델 → 관련성 점수

질문과 문서를 동시에 보니까 정확하다. 대신 미리 계산해둘 수 없어서 느리다 — 질문이 들어올 때마다 문서 N개에 대해 N번 모델을 돌려야 한다.

왜 둘 다 쓰나

Cross-Encoder가 정확한데 처음부터 안 쓰는 이유는 느리니까다.

문서 1만 개 × Cross-Encoder = 1만 번 비교 → 실시간 불가능
문서 1만 개 × Bi-Encoder    = 사전 계산 + 벡터 거리 계산 → 즉시

그래서 2단계로 쓴다.

질문 → Bi-Encoder로 top-20 (빠르게 후보 축소)
     → Cross-Encoder로 top-3 (정확하게 재평가)
     → LLM에 전달

코드

from sentence_transformers import CrossEncoder

reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")

# 1단계: 벡터 검색으로 후보 20개
results = collection.query(query_embeddings=[vector], n_results=20)

# 2단계: Cross-Encoder로 재평가
pairs = [[question, doc] for doc in results["documents"][0]]
scores = reranker.predict(pairs)

# 점수 높은 순으로 top-3
top_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:3]

cross-encoder/ms-marco-MiniLM-L-6-v2는 약 80MB, 로컬에서 충분히 돌아간다.

효과가 큰 경우

  • 질문이 짧을 때 — 단어 1~2개로 된 질문은 Bi-Encoder가 의미를 잡기 어려움
  • 비슷한 노트가 많을 때 — RBAC vs ABAC처럼 구분이 미세한 주제
  • 여러 주제가 섞인 노트에서 검색할 때 — 문서 내 어느 부분이 진짜 관련 있는지 Cross-Encoder가 더 잘 판단

더 보기

  • [[RAG]] — 기본 파이프라인
  • [[Hybrid-Search]] — Reranker 앞에 붙이면 후보 커버리지 상승
  • [[RAG-한계와-보완]] — 검색 실패 대응 전략 전반

Backlinks

sunshinemoon · 2026