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-한계와-보완]] — 검색 실패 대응 전략 전반