Spaces:
Running
Running
Rename Lodestar -> PRIMO
Browse files- README.md +8 -6
- app.py +4 -4
- evaluator.py +8 -9
- scoring.py +1 -1
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
emoji: 🧬
|
| 4 |
colorFrom: indigo
|
| 5 |
colorTo: blue
|
|
@@ -10,7 +10,9 @@ app_file: app.py
|
|
| 10 |
pinned: false
|
| 11 |
---
|
| 12 |
|
| 13 |
-
#
|
|
|
|
|
|
|
| 14 |
|
| 15 |
Embed **every** dataset with your model and upload **one** file. A fixed linear
|
| 16 |
probe grades each hidden task (a dataset may be scored on several targets) over
|
|
@@ -56,16 +58,16 @@ feedback, they just aren't ranked.
|
|
| 56 |
|
| 57 |
```bash
|
| 58 |
pip install -r requirements.txt
|
| 59 |
-
export HF_TOKEN=... # read access to the
|
| 60 |
python evaluator.py --submission my_embeddings.parquet
|
| 61 |
```
|
| 62 |
|
| 63 |
## Space configuration
|
| 64 |
|
| 65 |
- Set an **`HF_TOKEN`** Space secret (fine-grained) with: **read** on
|
| 66 |
-
`ScientaLab/
|
| 67 |
-
`ScientaLab/
|
| 68 |
-
`<task_id>/labels.csv`), and **write** on `ScientaLab/
|
| 69 |
persisted leaderboard).
|
| 70 |
- Results persist as one normalized `task_results.csv` (`model_name, task_id,
|
| 71 |
score, submitted_at`) in the results dataset; the leaderboard is recomputed
|
|
|
|
| 1 |
---
|
| 2 |
+
title: PRIMO Benchmark
|
| 3 |
emoji: 🧬
|
| 4 |
colorFrom: indigo
|
| 5 |
colorTo: blue
|
|
|
|
| 10 |
pinned: false
|
| 11 |
---
|
| 12 |
|
| 13 |
+
# PRIMO — Patient Representations in Multi-Omics
|
| 14 |
+
|
| 15 |
+
**A blind benchmark for transcriptomic foundation models.**
|
| 16 |
|
| 17 |
Embed **every** dataset with your model and upload **one** file. A fixed linear
|
| 18 |
probe grades each hidden task (a dataset may be scored on several targets) over
|
|
|
|
| 58 |
|
| 59 |
```bash
|
| 60 |
pip install -r requirements.txt
|
| 61 |
+
export HF_TOKEN=... # read access to the PRIMO datasets
|
| 62 |
python evaluator.py --submission my_embeddings.parquet
|
| 63 |
```
|
| 64 |
|
| 65 |
## Space configuration
|
| 66 |
|
| 67 |
- Set an **`HF_TOKEN`** Space secret (fine-grained) with: **read** on
|
| 68 |
+
`ScientaLab/primo` (the public `datasets.yaml` manifest) and
|
| 69 |
+
`ScientaLab/primo-labels` (the private `tasks.yaml` registry +
|
| 70 |
+
`<task_id>/labels.csv`), and **write** on `ScientaLab/primo-results` (the
|
| 71 |
persisted leaderboard).
|
| 72 |
- Results persist as one normalized `task_results.csv` (`model_name, task_id,
|
| 73 |
score, submitted_at`) in the results dataset; the leaderboard is recomputed
|
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""Gradio front-end for the
|
| 2 |
|
| 3 |
Upload one embedding file spanning every dataset (rows keyed by ``dataset_id``
|
| 4 |
+ ``sample_id``); a fixed linear probe scores each task (a dataset may carry
|
|
@@ -259,15 +259,15 @@ def evaluate(submission_path: str, model_name: str):
|
|
| 259 |
|
| 260 |
|
| 261 |
def build_demo() -> gr.Blocks:
|
| 262 |
-
with gr.Blocks(title="
|
| 263 |
gr.Markdown(
|
| 264 |
-
"# 🧬
|
| 265 |
"A **blind benchmark for transcriptomic foundation models** — grade "
|
| 266 |
"your model's patient-level embeddings against real clinical signal, "
|
| 267 |
"without ever seeing the labels.\n\n"
|
| 268 |
"**Submit in 3 steps:**\n"
|
| 269 |
"1. **Get the data** → download the opaque datasets from "
|
| 270 |
-
"[ScientaLab/
|
| 271 |
" (start with its `datasets.yaml`).\n"
|
| 272 |
"2. **Embed every dataset** → build **one** file: `dataset_id`, "
|
| 273 |
"`sample_id`, then one column per embedding dim (`e0`, `e1`, …). "
|
|
|
|
| 1 |
+
"""Gradio front-end for the PRIMO public benchmark.
|
| 2 |
|
| 3 |
Upload one embedding file spanning every dataset (rows keyed by ``dataset_id``
|
| 4 |
+ ``sample_id``); a fixed linear probe scores each task (a dataset may carry
|
|
|
|
| 259 |
|
| 260 |
|
| 261 |
def build_demo() -> gr.Blocks:
|
| 262 |
+
with gr.Blocks(title="PRIMO Benchmark") as demo:
|
| 263 |
gr.Markdown(
|
| 264 |
+
"# 🧬 PRIMO\n\n"
|
| 265 |
"A **blind benchmark for transcriptomic foundation models** — grade "
|
| 266 |
"your model's patient-level embeddings against real clinical signal, "
|
| 267 |
"without ever seeing the labels.\n\n"
|
| 268 |
"**Submit in 3 steps:**\n"
|
| 269 |
"1. **Get the data** → download the opaque datasets from "
|
| 270 |
+
"[ScientaLab/primo](https://huggingface.co/datasets/ScientaLab/primo)"
|
| 271 |
" (start with its `datasets.yaml`).\n"
|
| 272 |
"2. **Embed every dataset** → build **one** file: `dataset_id`, "
|
| 273 |
"`sample_id`, then one column per embedding dim (`e0`, `e1`, …). "
|
evaluator.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""Standalone probe for the
|
| 2 |
|
| 3 |
Loads one embedding submission that spans every dataset (rows keyed by
|
| 4 |
``dataset_id`` + ``sample_id``), then scores each TASK = (dataset, target): a
|
|
@@ -22,6 +22,7 @@ the fetch helpers / CLI, imported lazily.
|
|
| 22 |
|
| 23 |
import argparse
|
| 24 |
import logging
|
|
|
|
| 25 |
import time
|
| 26 |
from collections import defaultdict
|
| 27 |
from dataclasses import dataclass
|
|
@@ -41,9 +42,9 @@ TASK_ID = "task_id"
|
|
| 41 |
LABEL = "label"
|
| 42 |
FOLD = "fold"
|
| 43 |
|
| 44 |
-
PUBLIC_REPO = "ScientaLab/
|
| 45 |
-
LABELS_REPO = "ScientaLab/
|
| 46 |
-
RESULTS_REPO = "ScientaLab/
|
| 47 |
MANIFEST_FILENAME = "datasets.yaml"
|
| 48 |
TASKS_FILENAME = "tasks.yaml"
|
| 49 |
LABELS_FILENAME = "labels.csv"
|
|
@@ -98,7 +99,7 @@ def load_tasks_registry(path: str | Path) -> list[dict]:
|
|
| 98 |
with open(path) as handle:
|
| 99 |
data = yaml.safe_load(handle)
|
| 100 |
tasks = data.get("tasks", []) if isinstance(data, dict) else (data or [])
|
| 101 |
-
ids = [_norm_id(t[
|
| 102 |
if len(set(ids)) != len(ids):
|
| 103 |
raise EvaluatorError(f"duplicate task_id in the registry: {ids}")
|
| 104 |
return tasks
|
|
@@ -109,7 +110,7 @@ def load_labels(path: str | Path) -> pd.DataFrame:
|
|
| 109 |
df = pd.read_csv(path)
|
| 110 |
missing = {SAMPLE_ID, LABEL, FOLD} - set(df.columns)
|
| 111 |
if missing:
|
| 112 |
-
raise
|
| 113 |
return df
|
| 114 |
|
| 115 |
|
|
@@ -497,13 +498,11 @@ def _rollup_datasets(outcomes: list[TaskOutcome]) -> list[dict]:
|
|
| 497 |
|
| 498 |
|
| 499 |
def _cli() -> None:
|
| 500 |
-
parser = argparse.ArgumentParser(description="Score a
|
| 501 |
parser.add_argument("--submission", required=True, help="CSV/TSV/Parquet/NPZ file")
|
| 502 |
parser.add_argument("--token", default=None, help="HF token (else env HF_TOKEN)")
|
| 503 |
args = parser.parse_args()
|
| 504 |
|
| 505 |
-
import os
|
| 506 |
-
|
| 507 |
token = args.token or os.environ.get("HF_TOKEN")
|
| 508 |
result = score_all(args.submission, token)
|
| 509 |
print(
|
|
|
|
| 1 |
+
"""Standalone probe for the PRIMO public benchmark.
|
| 2 |
|
| 3 |
Loads one embedding submission that spans every dataset (rows keyed by
|
| 4 |
``dataset_id`` + ``sample_id``), then scores each TASK = (dataset, target): a
|
|
|
|
| 22 |
|
| 23 |
import argparse
|
| 24 |
import logging
|
| 25 |
+
import os
|
| 26 |
import time
|
| 27 |
from collections import defaultdict
|
| 28 |
from dataclasses import dataclass
|
|
|
|
| 42 |
LABEL = "label"
|
| 43 |
FOLD = "fold"
|
| 44 |
|
| 45 |
+
PUBLIC_REPO = "ScientaLab/primo"
|
| 46 |
+
LABELS_REPO = "ScientaLab/primo-labels"
|
| 47 |
+
RESULTS_REPO = "ScientaLab/primo-results"
|
| 48 |
MANIFEST_FILENAME = "datasets.yaml"
|
| 49 |
TASKS_FILENAME = "tasks.yaml"
|
| 50 |
LABELS_FILENAME = "labels.csv"
|
|
|
|
| 99 |
with open(path) as handle:
|
| 100 |
data = yaml.safe_load(handle)
|
| 101 |
tasks = data.get("tasks", []) if isinstance(data, dict) else (data or [])
|
| 102 |
+
ids = [_norm_id(t[TASK_ID]) for t in tasks]
|
| 103 |
if len(set(ids)) != len(ids):
|
| 104 |
raise EvaluatorError(f"duplicate task_id in the registry: {ids}")
|
| 105 |
return tasks
|
|
|
|
| 110 |
df = pd.read_csv(path)
|
| 111 |
missing = {SAMPLE_ID, LABEL, FOLD} - set(df.columns)
|
| 112 |
if missing:
|
| 113 |
+
raise EvaluatorError(f"labels.csv missing columns: {sorted(missing)}")
|
| 114 |
return df
|
| 115 |
|
| 116 |
|
|
|
|
| 498 |
|
| 499 |
|
| 500 |
def _cli() -> None:
|
| 501 |
+
parser = argparse.ArgumentParser(description="Score a PRIMO submission locally.")
|
| 502 |
parser.add_argument("--submission", required=True, help="CSV/TSV/Parquet/NPZ file")
|
| 503 |
parser.add_argument("--token", default=None, help="HF token (else env HF_TOKEN)")
|
| 504 |
args = parser.parse_args()
|
| 505 |
|
|
|
|
|
|
|
| 506 |
token = args.token or os.environ.get("HF_TOKEN")
|
| 507 |
result = score_all(args.submission, token)
|
| 508 |
print(
|
scoring.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""Scoring policy for the
|
| 2 |
|
| 3 |
Everything that turns a model's out-of-fold predictions into leaderboard
|
| 4 |
numbers lives here, kept apart from the probe and from any I/O so it stays easy
|
|
|
|
| 1 |
+
"""Scoring policy for the PRIMO benchmark: predictions -> final scores.
|
| 2 |
|
| 3 |
Everything that turns a model's out-of-fold predictions into leaderboard
|
| 4 |
numbers lives here, kept apart from the probe and from any I/O so it stays easy
|