Spaces:
Running
Running
File size: 7,611 Bytes
57efc81 13d0b34 d38e9d6 57efc81 3e6f48b 72d7def 57efc81 d2a6015 57efc81 75e6a8b 13d0b34 75b3042 d38e9d6 75b3042 7bfbc7e 75e6a8b 7bfbc7e c9d0ebc d2058b2 c9d0ebc 88b3ddf 75e6a8b 7bfbc7e 75b3042 7bfbc7e c9d0ebc 7bfbc7e 75b3042 d2058b2 7bfbc7e 75e6a8b d38e9d6 1d983ea d38e9d6 1d983ea d38e9d6 75e6a8b 1d983ea 75e6a8b d38e9d6 1d983ea 6286da1 d38e9d6 1d983ea d38e9d6 6286da1 1d983ea 75e6a8b 7d3e988 d38e9d6 cb0c5af 75e6a8b 7bfbc7e 75e6a8b 7bfbc7e cb0c5af 75e6a8b d38e9d6 48fcbed 75e6a8b 48fcbed 75e6a8b 48fcbed d38e9d6 d2058b2 d38e9d6 13d0b34 1d983ea d38e9d6 d2058b2 75e6a8b d2058b2 d38e9d6 d2a6015 1d983ea c9d0ebc bce9e42 6286da1 cb0c5af 75e6a8b d2a6015 75e6a8b d2058b2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | ---
title: PRIMO Benchmark
emoji: 🧬
colorFrom: indigo
colorTo: blue
sdk: gradio
sdk_version: 5.50.0
python_version: "3.10"
app_file: app.py
pinned: false
hf_oauth: true
---
# PRIMO: Patient Representations in Multi-Omics
**A blind benchmark for omics foundation models.**
PRIMO grades how well a model turns a patient's omics data into a useful
**patient embedding**. You embed every dataset and upload **one** file; a fixed
linear probe scores each hidden task, and the results roll up into a blind,
per-category leaderboard. The datasets are opaque (`d001`, `d002`…) and you never
see the disease, tissue, or target, which leaves you grading the *embedding*
itself with no room for per-task tuning.
A left **rail** navigates six pages: **Boards** (a grid of every board),
**Board** (one leaderboard at a time), **Tasks**, a **Submit** form (sign in with
Hugging Face), **Contribute**, and **Method**. Every board has its own URL,
`?board=rheumatology-bulk-rna`, and every page too, as `?tab=contribute`, so the
rail links and the open cards are shareable deep links.
Boards also shows **open boards**: greyed-out cards for the omics layers and the
therapeutic areas PRIMO does not cover yet, each linking to Contribute. They are
declared in `boards.py` (`OPEN_BOARDS`) and drop out on their own once the
registry covers that slice. Task categories deliberately get none, because a
category is pinned to a single metric, so an open one would advertise a probe
that does not exist.
🌐 Website: http://primomics.org/ ·
📄 Paper: https://openreview.net/forum?id=v2SA8gHwqo ·
📦 Data: https://huggingface.co/datasets/PRIMOmics/primo
## What's in the data
PRIMO benchmarks any omics modality. Today's datasets are all **bulk RNA-seq**,
covering **immune-mediated inflammatory diseases (IMIDs)** with real clinical
labels from published cohorts:
- **Gastroenterology**: Crohn's disease, ulcerative colitis (anti-TNF response, severity scores)
- **Dermatology**: atopic dermatitis, psoriasis (severity scores)
- **Rheumatology**: rheumatoid arthritis (joint counts, molecular endotype)
## Submission format
One file, one row per (`dataset_id`, `sample_id`), spanning all datasets:
- **CSV / TSV / Parquet**: a `dataset_id` column, a `sample_id` column, and one
numeric column per embedding dimension. Embedding dim may differ per dataset
(pad short datasets with blank columns; blank/NaN padding columns are dropped
per dataset).
- **NPZ**: `dataset_ids`, `sample_ids`, and a 2-D `embeddings` array.
The valid `dataset_id`s and how to download each dataset's `expression.h5ad` are
listed in the public `datasets.yaml` manifest. Alignment is by join, so row
order does not matter; every labelled sample of a task must be present with no
NaN/inf, or that task is skipped.
## How it works
Each dataset is embedded once and scored on every hidden task defined for it. Per
task: standardise on the training folds, fit `LogisticRegressionCV`
(classification) or `RidgeCV` (regression) with the regularisation chosen by
inner cross-validation, predict the held-out fold, and pool the out-of-fold
predictions into one score: **AUROC** (classification) or **Pearson r**
(regression). A few tasks instead use a fixed train/test split: the probe is fit
once on the training portion and scored on the held-out portion.
Tasks are grouped into **three families**, each reported in its own native
metric. Two families are never merged into one column:
- **Treatment outcome**: response to anti-TNF therapy (**AUROC**)
- **Clinical scores**: disease-severity regression (**Pearson r**)
- **Endotype**: molecular-subtype classification (**AUROC**)
A leaderboard shows one column per family. A board holding more than one family
also shows **Mean**, the average of those columns. It is what orders the rows,
but it does mix AUROC with Pearson, so treat it as a tie-break and compare models
on the family columns.
## Boards
Results are shown as **boards**, self-contained leaderboards over a slice of the
registry: the whole modality, one therapeutic area, one task family. A board
ranks only the models that covered **all of its tasks**, so a submission that
skipped Dermatology is still ranked on Rheumatology. Modality is where we draw
the line: an area or category board never spans two modalities, because an AUROC
on bulk RNA and an AUROC on single-cell are not measuring the same thing.
Partial and failed submissions still get feedback, and their scores always appear
in each board's **per-task** table even when they are not ranked.
## Make a submission
`quickstart.py` is the shortest path: it downloads every dataset, embeds each one
(log2(CPM+1) → PCA) and writes the file the Submit tab wants. Swap its `embed`
function for your encoder and nothing else changes. `example_submission.csv`
shows the expected shape in four lines.
```bash
pip install anndata scikit-learn pandas pyyaml huggingface_hub
python quickstart.py --out submission.parquet
```
## Run the scorer locally
```bash
pip install -r requirements.txt
export HF_TOKEN=... # read access to the PRIMO datasets
python evaluator.py --submission my_embeddings.parquet
```
## Baselines
`task_results.csv` carries an `is_baseline` flag. Reference submissions we
produce ourselves (a random embedding, PCA / HVG recipes over log-CPM) are
published with it set, rendered as `name (baseline)`, and **ranked in place**.
A foundation model losing to a PCA is exactly the result worth publishing, so we
keep it in the table rather than tucked underneath. They are generated and
pushed by `benchmark/public_benchmark/baselines.py --score --publish`.
## Space configuration
- **`hf_oauth: true`** (set above) turns on the Submit tab's *Sign in with
Hugging Face* button; submitting requires a logged-in HF account.
- Set an **`HF_TOKEN`** Space secret (fine-grained) with: **read** on
`PRIMOmics/primo` (the public `datasets.yaml` manifest) and
`PRIMOmics/primo-labels` (the private `tasks.yaml` registry +
`<task_id>/labels.csv`), and **read + write** on
`ScientaLab/primo-results` (the persisted leaderboard).
- Results persist as one normalized `task_results.csv` (`model_name, task_id,
score, submitted_at, is_baseline, hf_username`) in the results dataset; the
leaderboard is recomputed from it by joining the registry, so it survives
Space restarts.
- A board keeps each name's **latest** submission, so a model name is owned by
the account that first submitted it: `hf_username` locks it, and Submit
refuses a name somebody else holds. It is never rendered anywhere: it works as
a lock rather than as a credit. Names colliding with a per-task column (`Task`,
`Family`, `Area`, `Metric`, `Best`) are refused too, since model names become
column headers.
- Submitter contact metadata (HF username, email, paper / model links, notes)
persists to a separate `submissions.csv` in the same **private** results
dataset, and never reaches the public leaderboard.
## Moving to another Hugging Face org
The three dataset repos are derived from one constant, `ORG` in `evaluator.py`.
The rest of the org name is spelled out and has to be changed by hand:
- `SPACE_REPO` in `benchmark/public_benchmark/deploy_space.py`
- `PUBLIC_REPO` in `quickstart.py`
- the links in this file and in `pages/*.md`
The theme follows the PRIMO charter: Funnel Display for headings, Funnel Sans for
everything else, Scienta Navy `#080F5F` / PRIMO Cyan `#16B3C0` on Paper
`#F3F8F8`. `colorFrom`/`colorTo` above stay `indigo`/`blue` because Hugging Face
only accepts eight named colours and none of them is cyan.
|