samwjya commited on
Commit Β·
2e38dc6
1
Parent(s): 48e6983
update ui
Browse files- __pycache__/evaluate.cpython-313.pyc +0 -0
- __pycache__/reranker.cpython-313.pyc +0 -0
- __pycache__/retriever.cpython-313.pyc +0 -0
- requirements.txt +1 -0
- reranker.py +7 -73
- ui/app.py +9 -6
__pycache__/evaluate.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/evaluate.cpython-313.pyc and b/__pycache__/evaluate.cpython-313.pyc differ
|
|
|
__pycache__/reranker.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/reranker.cpython-313.pyc and b/__pycache__/reranker.cpython-313.pyc differ
|
|
|
__pycache__/retriever.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/retriever.cpython-313.pyc and b/__pycache__/retriever.cpython-313.pyc differ
|
|
|
requirements.txt
CHANGED
|
@@ -3,3 +3,4 @@ rank_bm25
|
|
| 3 |
sentence-transformers
|
| 4 |
pandas
|
| 5 |
torch
|
|
|
|
|
|
| 3 |
sentence-transformers
|
| 4 |
pandas
|
| 5 |
torch
|
| 6 |
+
einops
|
reranker.py
CHANGED
|
@@ -1,20 +1,19 @@
|
|
| 1 |
-
from concurrent.futures import ThreadPoolExecutor
|
| 2 |
from sentence_transformers import CrossEncoder
|
| 3 |
|
| 4 |
|
| 5 |
-
DEFAULT_MODEL = "
|
| 6 |
|
| 7 |
-
|
| 8 |
-
"jinaai/jina-reranker-v2-base-multilingual",
|
| 9 |
-
"Alibaba-NLP/gte-multilingual-reranker-base",
|
| 10 |
"BAAI/bge-reranker-v2-m3",
|
|
|
|
|
|
|
| 11 |
]
|
| 12 |
|
| 13 |
|
| 14 |
def load_model(model_name: str = DEFAULT_MODEL) -> CrossEncoder:
|
| 15 |
"""Load a CrossEncoder model from HuggingFace (cached after first download)."""
|
| 16 |
print(f"Loading reranker model: {model_name} ...")
|
| 17 |
-
model = CrossEncoder(model_name, max_length=512)
|
| 18 |
print("Model loaded.\n")
|
| 19 |
return model
|
| 20 |
|
|
@@ -37,15 +36,8 @@ def build_pairs(query: str, papers: list[dict]) -> list[tuple[str, str]]:
|
|
| 37 |
def rerank(query: str, papers: list[dict], model: CrossEncoder, batch_size: int = 16) -> list[dict]:
|
| 38 |
"""Score every (query, paper) pair with the CrossEncoder and return papers sorted by score.
|
| 39 |
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
papers: List of paper dicts (already BM25-ranked; order does not matter here).
|
| 43 |
-
model: A loaded CrossEncoder instance.
|
| 44 |
-
batch_size: Number of pairs to score in one forward pass.
|
| 45 |
-
|
| 46 |
-
Returns:
|
| 47 |
-
The same paper dicts, each annotated with `ce_score` and `ce_rank`,
|
| 48 |
-
sorted by CrossEncoder score descending.
|
| 49 |
"""
|
| 50 |
pairs = build_pairs(query, papers)
|
| 51 |
scores = model.predict(pairs, batch_size=batch_size, show_progress_bar=True)
|
|
@@ -64,64 +56,6 @@ def rerank(query: str, papers: list[dict], model: CrossEncoder, batch_size: int
|
|
| 64 |
return results
|
| 65 |
|
| 66 |
|
| 67 |
-
def load_models(model_names: list[str] = ENSEMBLE_MODELS) -> list[CrossEncoder]:
|
| 68 |
-
"""Load all CrossEncoder models in parallel using ThreadPoolExecutor."""
|
| 69 |
-
with ThreadPoolExecutor(max_workers=len(model_names)) as executor:
|
| 70 |
-
futures = [executor.submit(load_model, name) for name in model_names]
|
| 71 |
-
return [f.result() for f in futures]
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
def _normalize(scores: list[float]) -> list[float]:
|
| 75 |
-
"""Min-max normalize scores to [0, 1]. Returns all 1.0 when all scores are equal."""
|
| 76 |
-
lo, hi = min(scores), max(scores)
|
| 77 |
-
if hi == lo:
|
| 78 |
-
return [1.0] * len(scores)
|
| 79 |
-
return [(s - lo) / (hi - lo) for s in scores]
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
def ensemble_rerank(
|
| 83 |
-
query: str,
|
| 84 |
-
papers: list[dict],
|
| 85 |
-
models: list[CrossEncoder],
|
| 86 |
-
batch_size: int = 16,
|
| 87 |
-
) -> list[dict]:
|
| 88 |
-
"""Score papers with multiple CrossEncoders, normalize each model's scores to
|
| 89 |
-
[0, 1] via min-max, then average across models (equal weights) to produce an
|
| 90 |
-
ensemble score. Returns papers sorted by ensemble score descending.
|
| 91 |
-
|
| 92 |
-
Each returned paper dict gains:
|
| 93 |
-
ce_score_model1 / 2 / 3 β raw score from each model
|
| 94 |
-
ce_score_ensemble β mean of the three normalized scores
|
| 95 |
-
ce_score β alias for ce_score_ensemble (pipeline compat)
|
| 96 |
-
ce_rank β 1-based position in the ensemble ranking
|
| 97 |
-
"""
|
| 98 |
-
pairs = build_pairs(query, papers)
|
| 99 |
-
|
| 100 |
-
with ThreadPoolExecutor(max_workers=len(models)) as executor:
|
| 101 |
-
futures = [executor.submit(m.predict, pairs) for m in models]
|
| 102 |
-
raw_scores = [list(f.result()) for f in futures]
|
| 103 |
-
norm_scores = [_normalize(s) for s in raw_scores]
|
| 104 |
-
|
| 105 |
-
n_models = len(models)
|
| 106 |
-
ensemble = [
|
| 107 |
-
sum(norm_scores[m][i] for m in range(n_models)) / n_models
|
| 108 |
-
for i in range(len(papers))
|
| 109 |
-
]
|
| 110 |
-
|
| 111 |
-
order = sorted(range(len(papers)), key=lambda i: ensemble[i], reverse=True)
|
| 112 |
-
|
| 113 |
-
results = []
|
| 114 |
-
for rank, i in enumerate(order, start=1):
|
| 115 |
-
paper = dict(papers[i])
|
| 116 |
-
for m_idx, raw in enumerate(raw_scores, start=1):
|
| 117 |
-
paper[f"ce_score_model{m_idx}"] = round(float(raw[i]), 4)
|
| 118 |
-
paper["ce_score_ensemble"] = round(float(ensemble[i]), 4)
|
| 119 |
-
paper["ce_score"] = paper["ce_score_ensemble"]
|
| 120 |
-
paper["ce_rank"] = rank
|
| 121 |
-
results.append(paper)
|
| 122 |
-
return results
|
| 123 |
-
|
| 124 |
-
|
| 125 |
if __name__ == "__main__":
|
| 126 |
import argparse
|
| 127 |
from retriever import fetch_papers
|
|
|
|
|
|
|
| 1 |
from sentence_transformers import CrossEncoder
|
| 2 |
|
| 3 |
|
| 4 |
+
DEFAULT_MODEL = "BAAI/bge-reranker-v2-m3"
|
| 5 |
|
| 6 |
+
AVAILABLE_MODELS = [
|
|
|
|
|
|
|
| 7 |
"BAAI/bge-reranker-v2-m3",
|
| 8 |
+
"cross-encoder/ms-marco-MiniLM-L-6-v2",
|
| 9 |
+
"cross-encoder/ms-marco-electra-base",
|
| 10 |
]
|
| 11 |
|
| 12 |
|
| 13 |
def load_model(model_name: str = DEFAULT_MODEL) -> CrossEncoder:
|
| 14 |
"""Load a CrossEncoder model from HuggingFace (cached after first download)."""
|
| 15 |
print(f"Loading reranker model: {model_name} ...")
|
| 16 |
+
model = CrossEncoder(model_name, max_length=512, trust_remote_code=True)
|
| 17 |
print("Model loaded.\n")
|
| 18 |
return model
|
| 19 |
|
|
|
|
| 36 |
def rerank(query: str, papers: list[dict], model: CrossEncoder, batch_size: int = 16) -> list[dict]:
|
| 37 |
"""Score every (query, paper) pair with the CrossEncoder and return papers sorted by score.
|
| 38 |
|
| 39 |
+
Returns the same paper dicts, each annotated with `ce_score` and `ce_rank`,
|
| 40 |
+
sorted by CrossEncoder score descending.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
"""
|
| 42 |
pairs = build_pairs(query, papers)
|
| 43 |
scores = model.predict(pairs, batch_size=batch_size, show_progress_bar=True)
|
|
|
|
| 56 |
return results
|
| 57 |
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
if __name__ == "__main__":
|
| 60 |
import argparse
|
| 61 |
from retriever import fetch_papers
|
ui/app.py
CHANGED
|
@@ -39,8 +39,10 @@ def tier_badge(score: float) -> str:
|
|
| 39 |
|
| 40 |
# ββ Sidebar ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 41 |
with st.sidebar:
|
| 42 |
-
st.title("PaperRank")
|
| 43 |
-
st.caption("CrossEncoder reranking on live academic papers.")
|
|
|
|
|
|
|
| 44 |
st.divider()
|
| 45 |
|
| 46 |
query = st.text_input("Search query", placeholder="e.g. transformer models for information retrieval")
|
|
@@ -115,9 +117,10 @@ if (
|
|
| 115 |
|
| 116 |
|
| 117 |
# ββ Main area ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 118 |
-
st.title("PaperRank")
|
| 119 |
-
st.caption("CrossEncoder reranking on live academic papers.")
|
| 120 |
-
|
|
|
|
| 121 |
if "reranked_papers" not in st.session_state:
|
| 122 |
st.info("Enter a query in the sidebar and click **Run pipeline** to get started.")
|
| 123 |
st.stop()
|
|
@@ -177,7 +180,7 @@ def render_ce_card(paper: dict) -> None:
|
|
| 177 |
score = paper.get("ce_score", 0.0)
|
| 178 |
|
| 179 |
oa_r = oa_rank_by_id.get(pid)
|
| 180 |
-
note =
|
| 181 |
|
| 182 |
st.markdown(
|
| 183 |
f"**#{rank}**{note} {tier_badge(score)} \n{title_md} \n"
|
|
|
|
| 39 |
|
| 40 |
# ββ Sidebar ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 41 |
with st.sidebar:
|
| 42 |
+
# st.title("PaperRank")
|
| 43 |
+
# st.caption("CrossEncoder reranking on live academic papers.")
|
| 44 |
+
st.title("Task1")
|
| 45 |
+
st.caption("Retrieval and Reranking")
|
| 46 |
st.divider()
|
| 47 |
|
| 48 |
query = st.text_input("Search query", placeholder="e.g. transformer models for information retrieval")
|
|
|
|
| 117 |
|
| 118 |
|
| 119 |
# ββ Main area ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 120 |
+
# st.title("PaperRank")
|
| 121 |
+
# st.caption("CrossEncoder reranking on live academic papers.")
|
| 122 |
+
st.title("Task1")
|
| 123 |
+
st.caption("Retrieval and Reranking")
|
| 124 |
if "reranked_papers" not in st.session_state:
|
| 125 |
st.info("Enter a query in the sidebar and click **Run pipeline** to get started.")
|
| 126 |
st.stop()
|
|
|
|
| 180 |
score = paper.get("ce_score", 0.0)
|
| 181 |
|
| 182 |
oa_r = oa_rank_by_id.get(pid)
|
| 183 |
+
note = title
|
| 184 |
|
| 185 |
st.markdown(
|
| 186 |
f"**#{rank}**{note} {tier_badge(score)} \n{title_md} \n"
|