Field Log · Entry
Reranking: Cross-Encoder·monoT5·ColBERT의 역할 (7/10)
오늘의 결론
- Retriever는 corpus에서 놓치지 않고 후보를 모으고, reranker는 작은 후보 안에서 정확한 순서를 만듭니다.
- Cross-Encoder는 query와 document token을 함께 attention해 세밀하지만 문서 vector를 미리 계산할 수 없습니다.
- monoT5는 query-document relevance를 text token 확률로 표현하는 reranker입니다.
- ColBERT는 document token vector를 미리 저장하고 query token별 MaxSim을 합치는 중간 지점입니다.
- reranker는 candidate set에 없는 정답을 복구하지 못하므로 후보 Recall을 먼저 확인합니다.
앞 글의 hybrid search는 BM25와 dense 결과를 합쳐 넓은 후보를 만들었습니다. 그러나 E104 인증 만료와 E104 연결 시간 초과처럼 비슷한 후보의 세부 조건을 1차 score만으로 구분하기 어려울 수 있습니다.
Reranking은 top-50이나 top-100 같은 작은 후보 집합을 더 비싼 model로 다시 점수화합니다.
그림 1. Bi-Encoder는 passage vector를 재사용해 넓게 찾고, Cross-Encoder는 query-document를 함께 읽으며, ColBERT는 사전 계산한 token vector와 MaxSim으로 두 극단 사이를 잇는다.
두 단계 검색의 역할 분담
Stage 1 · Candidate retrieval
BM25 / Dense ANN / Hybrid
corpus 10,000,000개 → candidate 100개
목표: 높은 Recall@100
Stage 2 · Reranking
Cross-Encoder / monoT5 / ColBERT
candidate 100개 → final 5개
목표: 높은 MRR·nDCG@5·Precision@5
첫 단계가 gold passage를 놓치면 두 번째 단계는 아무것도 할 수 없습니다.
candidate contains gold = false
→ reranker maximum success = false
따라서 reranker 평가 report에는 반드시 candidate set recall을 함께 씁니다.
Bi-Encoder가 놓치는 interaction
Bi-Encoder는 query와 passage를 별도로 한 vector에 압축합니다.
score(q, d) = E_Q(q) · E_D(d)
빠르지만 query의 E104가 document의 어느 token과 대응하고, 연결 시간 초과가 인증 만료와 어떻게 다른지 joint attention으로 직접 비교하지 않습니다.
한 vector에 다음 세부를 모두 보존해야 합니다.
- exact identifier
- 숫자와 단위
- 부정과 조건
- entity 관계
- 문장 내 위치
- query가 강조한 부분
Reranker는 후보가 작아진 뒤 더 많은 interaction을 계산합니다.
Cross-Encoder: query와 document를 함께 읽는다
입력은 대략 다음처럼 구성됩니다.
[CLS] query tokens [SEP] document tokens [SEP]
Transformer self-attention에서 query token과 document token이 모든 layer에서 상호작용합니다.
"E104" ↔ document의 "E104"
"연결" ↔ "네트워크"
"시간 초과" ↔ "timeout"
마지막 representation에서 relevance logit 또는 score를 예측합니다.
장점
- exact term과 문맥 관계를 함께 볼 수 있습니다.
- 부정, 숫자, 조건의 mismatch를 구분할 가능성이 큽니다.
- top-k ranking 품질이 Bi-Encoder보다 높은 경우가 많습니다.
비용
- query-document pair마다 Transformer forward가 필요합니다.
- document representation을 query와 독립적으로 precompute할 수 없습니다.
- candidate 수와 sequence length가 latency·GPU memory를 직접 늘립니다.
corpus 1천만 개 전체에 적용하지 않고 1차 검색 후보에만 적용하는 이유입니다.
Cross-Encoder를 Python으로 붙인다
Sentence Transformers의 CrossEncoder를 사용하는 기본 구조입니다. model ID는 언어·domain·license·학습 task에 맞춰 선택합니다.
from dataclasses import dataclass
import numpy as np
from sentence_transformers import CrossEncoder
@dataclass(frozen=True)
class Candidate:
chunk_id: str
text: str
retrieval_rank: int
retrieval_score: float
source: str
@dataclass(frozen=True)
class RerankedHit:
candidate: Candidate
rerank_score: float
rerank_rank: int
model = CrossEncoder("YOUR_MULTILINGUAL_OR_DOMAIN_RERANKER")
def rerank(
query: str,
candidates: list[Candidate],
top_k: int,
) -> list[RerankedHit]:
if not 1 <= top_k <= len(candidates):
raise ValueError("top_k is outside candidate size")
pairs = [(query, candidate.text) for candidate in candidates]
scores = np.asarray(
model.predict(pairs, batch_size=32, show_progress_bar=False),
dtype=np.float32,
).reshape(-1)
order = np.argsort(-scores, kind="stable")[:top_k]
return [
RerankedHit(
candidate=candidates[index],
rerank_score=float(scores[index]),
rerank_rank=rank,
)
for rank, index in enumerate(order, start=1)
]
동점일 때 stable sort가 원래 candidate order를 보존합니다. 더 명시적으로 (-score, retrieval_rank, chunk_id)를 tie-break로 사용할 수도 있습니다.
Raw logit을 확률로 착각하지 않는다
Cross-Encoder output이 3.2라고 320% relevance가 아닙니다. model에 따라 logit, regression score, class probability가 다릅니다.
ranking만 필요하면 score 순서를 사용하고, threshold나 “relevant 확률”로 쓰려면 별도 calibration을 평가합니다.
monoBERT에서 monoT5로
초기 BERT reranker는 query-passage pair를 BERT에 넣어 relevance score를 예측했습니다. 이를 흔히 monoBERT 계열로 부릅니다.
monoT5는 sequence-to-sequence model인 T5에 ranking을 text-to-text task로 표현했습니다.
Input:
Query: 계정 복구 방법
Document: 비밀번호 재설정과 인증 복구 절차
Relevant:
Target token:
true
true와 false token의 logit으로 relevance를 계산할 수 있습니다.
P(true | query, document)
monoT5의 의미
- pretrained seq2seq model을 ranking task로 fine-tuning
- query-document를 함께 입력해 full interaction 사용
- output vocabulary의 relevance token score로 ranking
Cross-Encoder라는 넓은 범주 안에서 encoder-only classification과 encoder-decoder text generation 방식이 다른 것입니다.
비용 주의
reranking에 자유로운 긴 답변 generation이 필요한 것은 아닙니다. relevance token logit만 계산하도록 구현할 수 있습니다. framework가 실제로 몇 token을 decode하는지 확인합니다.
ColBERT: token-level late interaction
ColBERT는 query와 document를 별도로 encoding하지만 하나의 vector로 평균내지 않고 token vector를 유지합니다.
Q = [q₁, q₂, ..., qₘ]
D = [d₁, d₂, ..., dₙ]
각 query token이 document token 중 가장 비슷한 하나를 찾고 그 값을 합칩니다.
MaxSim(qᵢ, D) = maxⱼ qᵢ · dⱼ
score(Q,D) = Σᵢ maxⱼ qᵢ · dⱼ
예를 들어 query token E104, 연결, 초과가 document 안의 서로 다른 token과 각각 match할 수 있습니다.
왜 late interaction인가
- document token vector는 query 없이 미리 계산 가능
- query가 들어온 뒤 token 간 MaxSim interaction 수행
- single-vector Bi-Encoder보다 세밀함
- full Cross-Encoder보다 interaction 시점이 늦고 제한적
비용
- document당 vector 하나가 아니라 token 수만큼 저장
- index와 retrieval 구현이 복잡
- MaxSim 계산과 candidate generation 최적화 필요
- token pruning·compression에 따라 품질과 저장량 trade-off
블로그의 ColBERT Late Interaction 해설에는 실제 token matrix와 MaxSim 수치 예제가 더 자세히 정리돼 있습니다.
세 구조를 같은 축으로 비교한다
| 방식 | document 사전 계산 | interaction | 검색 범위 | 대표 비용 |
|---|---|---|---|---|
| Bi-Encoder | single vector | dot/cosine | corpus 전체 | vector index |
| ColBERT | token vectors | query-token MaxSim | corpus 또는 후보 | 큰 token index |
| Cross-Encoder | 불가 | full joint attention | 작은 후보 | pair forward |
| monoT5 | 불가 | encoder-decoder attention | 작은 후보 | pair forward + token logits |
실무 pipeline 예시는 다음과 같습니다.
BM25 + Dense ANN top-100 each
→ RRF top-50
→ Cross-Encoder top-10
→ parent expansion / context selection top-5
또는 ColBERT를 1차·중간 retrieval로 사용하고 작은 후보만 Cross-Encoder에 보낼 수 있습니다.
Candidate depth가 비용과 상한을 정한다
rerank candidate 수를 N, pair당 평균 비용을 c라고 하면 계산량은 대략 N × c입니다. batch가 hardware 효율을 높이지만 total token work는 늘어납니다.
N ∈ {10, 20, 50, 100, 200}
각 N에서 측정합니다.
- candidate Recall@N
- reranked MRR/nDCG@k
- p50/p95 latency
- tokens 또는 padded tokens
- GPU memory와 throughput
candidate Recall이 20에서 이미 포화되고 nDCG가 개선되지 않으면 100개를 rerank할 이유가 없습니다.
Batch size는 pair 수가 아니라 token 수로 본다
다음 두 batch는 pair 수가 같아도 비용이 다릅니다.
Batch A: 32 pairs × 128 tokens
Batch B: 32 pairs × 512 tokens
padding 때문에 batch의 가장 긴 sequence가 나머지 pair 비용을 늘릴 수 있습니다.
개선 방법
- passage token length로 candidate를 bucket
- dynamic batching
- max sequence length 명시
- 긴 passage 비율 관찰
- query별 deadline과 maximum candidate 수
여러 사용자의 pair를 하나의 batch로 합칠 때 결과 mapping과 tenant isolation을 철저히 유지합니다.
Truncation은 reranker의 조용한 실패다
query와 document를 함께 넣으므로 total length가 model limit을 넘을 수 있습니다.
[query tokens] + [special tokens] + [document tokens] ≤ max_length
document 뒤에 정답이 있는데 head truncation만 쓰면 reranker가 근거를 보지 못합니다.
선택지:
- chunk 자체를 model limit보다 작게 유지
- heading·핵심 span을 앞에 배치
- 긴 parent가 아니라 retrieval child를 rerank
- sliding window로 여러 score를 만들고 max/aggregate
- query-aware passage extraction 후 rerank
truncated candidate 비율과 정답 span 손실률을 기록합니다.
Reranker는 어떤 negative로 학습할까
random negative보다 현재 retriever가 실제로 올리는 hard negative가 중요합니다.
training loop
1. production-like BM25/dense/hybrid로 top-N 생성
2. gold가 아닌 높은 후보 수집
3. false negative 제거
4. query-positive-negative로 reranker 학습
5. 새 reranker로 holdout 평가
retriever를 교체하면 hard negative 분포도 달라집니다. reranker training data에는 후보를 만든 retriever version을 남깁니다.
Pointwise, pairwise, listwise
| 목표 | 학습 단위 | 질문 |
|---|---|---|
| pointwise | (q,d) → relevance | 이 문서는 관련 있는가? |
| pairwise | (q,d⁺,d⁻) | 어느 문서가 더 관련 있는가? |
| listwise | (q,[d₁...dₙ]) | 전체 순서를 어떻게 둘까? |
serving interface가 pointwise score여도 training objective는 pairwise/listwise일 수 있습니다. model 문서를 확인합니다.
Reranking metric을 읽는 법
Reranker는 후보를 추가하지 않고 순서만 바꾸므로, candidate set이 고정되면 Recall@N은 그대로입니다. 대신 작은 cutoff의 rank-sensitive metric이 중요합니다.
MRR
첫 relevant passage가 몇 위에 있는지 봅니다.
RR = 1 / first_relevant_rank
MRR = query별 RR 평균
첫 정답이 rank 10에서 rank 2로 오르면 0.1 → 0.5입니다.
nDCG@k
여러 relevance grade와 순위 discount를 반영합니다. 매우 관련 있음, 부분 관련 있음 같은 graded label에 적합합니다.
Precision@k와 context precision
LLM에 보내는 top-5 중 실제로 유용한 근거 비율을 봅니다. candidate Recall이 같아도 context noise를 줄일 수 있습니다.
올바른 비교표
Run A: Hybrid top-50 그대로
Run B: Hybrid top-50 → Cross-Encoder
Run C: Hybrid top-50 → monoT5
Run D: ColBERT top-50 → Cross-Encoder
각 run에 다음을 기록합니다.
| 단계 | metric |
|---|---|
| 후보 | Recall@50, unique sources |
| 순위 | MRR, nDCG@5/10, Precision@5 |
| 생성 | faithfulness, answer correctness |
| 성능 | p50/p95, pairs/sec, tokens/sec |
| 비용 | GPU memory, API cost |
후보 집합이 다른 B와 D를 비교하면서 개선을 전부 reranker 덕분이라고 해석하지 않습니다.
Rerank trace
{
"question_id": "q-104",
"candidate_count": 50,
"candidate_recall": true,
"reranker": "cross-encoder-domain-v4",
"max_length": 512,
"batch_size": 32,
"truncated_candidates": 3,
"hits": [
{
"chunk_id": "chunk-e104-timeout",
"retrieval_rank": 7,
"retrieval_sources": {"bm25": 2, "dense": 18},
"rerank_rank": 1,
"rerank_score": 5.83
}
],
"latency_ms": 41
}
score만 저장하지 말고 후보가 어디서 왔고 얼마나 이동했는지 기록합니다.
외부 rerank API를 쓸 때
query와 document text가 외부 service로 전송됩니다. 다음을 확인합니다.
- 개인정보와 영업 비밀 전송 정책
- data retention과 model training 사용 여부
- region과 encryption
- tenant별 API key와 quota
- timeout·retry·partial failure
- model version 고정 가능 여부
retrieval에서 ACL을 통과했더라도 외부 전송 허용 여부는 별도 정책일 수 있습니다.
LLM을 reranker로 쓰는 경우
instruction LLM에 후보 list를 주고 순위를 요청할 수도 있습니다. pairwise 또는 listwise prompt가 가능합니다.
장점:
- 복잡한 relevance 기준을 natural language로 설명 가능
- label이 적을 때 prototype이 쉬움
주의점:
- 높은 token·latency 비용
- 후보 순서와 position bias
- 출력 parsing 실패와 비결정성
- document prompt injection
- 긴 list에서 비교 일관성 저하
먼저 학습된 reranker baseline을 만들고 LLM reranker는 별도 evaluation과 안전 경계로 다룹니다.
Production 체크리스트
- candidate Recall@N이 reranker 상한으로 기록된다.
- retrieval과 reranking metric을 분리한다.
- candidate depth sweep으로 품질·latency 지점을 찾았다.
- Cross-Encoder output의 의미와 score 방향을 확인했다.
- max length와 truncation 비율을 기록한다.
- token length bucket과 batch policy가 있다.
- reranker hard negative가 production retriever를 반영한다.
- MRR·nDCG·Precision과 p95를 함께 본다.
- retrieval rank, rerank rank, model version을 trace한다.
- 외부 API의 data policy와 timeout fallback을 검증했다.
스스로 확인하기
- Cross-Encoder가 document vector를 offline에 미리 계산할 수 없는 이유는 무엇인가?
- candidate Recall@50이 0인 query를 reranker가 고칠 수 없는 이유는 무엇인가?
- ColBERT의 MaxSim은 single-vector dot product와 어떻게 다른가?
- reranker를 붙였는데 Recall@50은 같고 nDCG@5가 오르는 것이 자연스러운 이유는 무엇인가?
- candidate 수를 늘릴 때 latency 외에 어떤 truncation과 batch 비용을 봐야 하는가?
다음 글에서는 원 질문 자체가 검색에 불리할 때 Rewrite·Expansion·HyDE·Decomposition으로 Query를 변환하는 방법을 다룹니다.
참고자료
- Passage Re-ranking with BERT
- Document Ranking with a Pretrained Sequence-to-Sequence Model
- ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT
- ColBERTv2: Effective and Efficient Retrieval via Lightweight Late Interaction
- Sentence Transformers Cross-Encoder Documentation
- Zero-Shot Listwise Document Reranking with a Large Language Model