genAI-Project / src /retrieval /reranker.py
OGB2000's picture
Initial clean deployment
bf77be6
Raw
History Blame Contribute Delete
1.91 kB
"""
src/retrieval/reranker.py
Cross-encoder reranking (BAAI/bge-reranker-v2-m3).
Pipeline:
Stage 1 β†’ Chroma dense retrieval β†’ top-20 candidates
Stage 2 β†’ CrossEncoder scores each (query, chunk) pair β†’ returns top-5
Latency: ~0.5–2s CPU, ~0.1–0.3s GPU per query.
Quality: +5–15% MRR@10 on scientific QA vs dense-only.
"""
import time
from dataclasses import dataclass
@dataclass
class RankedChunk:
text: str
metadata: dict
distance: float
rerank_score: float
class CrossEncoderReranker:
DEFAULT_MODEL = "BAAI/bge-reranker-v2-m3"
def __init__(self, model_name: str = None):
from sentence_transformers import CrossEncoder
self.model_name = model_name or self.DEFAULT_MODEL
self.model = CrossEncoder(self.model_name, max_length=512)
print(f"βœ… Reranker loaded: {self.model_name}")
def rerank(self, query: str, retrieval_result: dict, top_k: int = 5) -> list:
docs = retrieval_result["documents"]
metas = retrieval_result["metadatas"]
dists = retrieval_result["distances"]
if not docs:
return []
scores = self.model.predict([(query, d) for d in docs], show_progress_bar=False)
ranked = sorted(
[RankedChunk(text=d, metadata=m, distance=dist, rerank_score=float(s))
for d, m, dist, s in zip(docs, metas, dists, scores)],
key=lambda x: x.rerank_score, reverse=True,
)
return ranked[:top_k]
def rerank_with_timing(self, query, retrieval_result, top_k=5):
t0 = time.time()
return self.rerank(query, retrieval_result, top_k), time.time() - t0
def ranked_to_result(ranked):
return {
"documents": [r.text for r in ranked],
"metadatas": [r.metadata for r in ranked],
"distances": [r.distance for r in ranked],
"rerank_scores": [r.rerank_score for r in ranked],
}