Download src/retrieve/reranker.py from hembad/reranker: direct link, hf CLI and curl.
- Browser
- Download file 3.53 kB
-
https://huggingface.co/spaces/hembad/reranker/resolve/main/src/retrieve/reranker.py
- Command line
-
hf download hf://spaces/hembad/reranker/src/retrieve/reranker.py
-
curl -L -o reranker.py https://huggingface.co/spaces/hembad/reranker/resolve/main/src/retrieve/reranker.py
3.53 kB
| """Step 09 β cross-encoder rerank (granite-embedding-reranker-r2 ONNX): the most precise relevance signal. | |
| A cross-encoder reads the JD query and each candidate's text TOGETHER (one transformer pass per pair) and | |
| emits a single relevance logit β strictly more precise than the bi-encoder cosine of Step 08, and the most | |
| expensive per item, so it runs only on the ~300 granite survivors. ModernBERT sequence-classification head; | |
| onnxruntime + tokenizers (NO torch). Re-ranks by ``reranker_score`` (raw logit; monotonic for ordering). | |
| """ | |
| from __future__ import annotations | |
| from functools import lru_cache | |
| from typing import Any | |
| import numpy as np | |
| import polars as pl | |
| from common.logging import step | |
| from config import Settings, load_settings | |
| from .onnx import load_onnx_session | |
| def _load_reranker(repo: str, onnx_file: str, max_length: int, cpu_threads: int = 0) -> tuple[Any, Any, set[str]]: | |
| """Load (downloading on first use) the cross-encoder ONNX session + tokenizer; cached for the process.""" | |
| return load_onnx_session(repo, onnx_file, max_length, "reranker.load", cpu_threads) | |
| def _score_pairs( | |
| session: Any, tokenizer: Any, input_names: set[str], query: str, docs: list[str], batch_size: int | |
| ) -> np.ndarray: | |
| """Cross-encode (query, doc) pairs β 1-D array of relevance logits, in doc order.""" | |
| logits: list[np.ndarray] = [] | |
| for start in range(0, len(docs), batch_size): | |
| batch_pairs = [(query, doc) for doc in docs[start : start + batch_size]] | |
| tokenizer.enable_padding() | |
| encodings = tokenizer.encode_batch(batch_pairs) # pair encoding: [CLS] query [SEP] doc [SEP] | |
| token_ids = np.array([enc.ids for enc in encodings], dtype=np.int64) | |
| feed: dict[str, np.ndarray] = { | |
| "input_ids": token_ids, | |
| "attention_mask": np.array([enc.attention_mask for enc in encodings], dtype=np.int64), | |
| } | |
| if "token_type_ids" in input_names: | |
| feed["token_type_ids"] = np.zeros_like(token_ids) | |
| batch_logits = session.run(None, {k: v for k, v in feed.items() if k in input_names})[0] | |
| logits.append(np.asarray(batch_logits, dtype=np.float64).reshape(-1)) # (batch, 1) β (batch,) | |
| return np.concatenate(logits) if logits else np.zeros(0) | |
| def cross_encoder_rerank( | |
| candidates: pl.DataFrame, query_text: str, settings: Settings | None = None | |
| ) -> pl.DataFrame: | |
| """Add ``reranker_score`` (cross-encoder relevance) and re-sort by it. Empty/blank β passthrough at 0.""" | |
| active = settings or load_settings() | |
| rerank = active.rerank | |
| with step("rank.cross_encoder", rows_in=candidates.height) as metrics: | |
| if candidates.height == 0 or not query_text.strip(): | |
| metrics["rows_out"] = candidates.height | |
| metrics["skipped"] = True | |
| return candidates.with_columns(pl.lit(0.0).alias("reranker_score")) | |
| session, tokenizer, input_names = _load_reranker( | |
| active.models.reranker, rerank.onnx_file, rerank.max_length, active.compute.cpu_threads | |
| ) | |
| scores = _score_pairs( | |
| session, tokenizer, input_names, query_text, candidates.get_column("text").to_list(), rerank.batch_size | |
| ) | |
| reranked = candidates.with_columns(pl.Series("reranker_score", scores)).sort( | |
| ["reranker_score", "candidate_id"], descending=[True, False] | |
| ) | |
| metrics["rows_out"] = reranked.height | |
| metrics["score_max"] = round(float(scores.max()), 4) | |
| return reranked | |