Add README.md
Browse files
README.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
library_name: pytorch
|
| 4 |
+
tags:
|
| 5 |
+
- biology
|
| 6 |
+
- protein
|
| 7 |
+
- immunology
|
| 8 |
+
- mhc-class-ii
|
| 9 |
+
- epitope
|
| 10 |
+
- binding-prediction
|
| 11 |
+
- esm
|
| 12 |
+
pipeline_tag: tabular-classification
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
# PREpiBind — research checkpoints (float32)
|
| 16 |
+
|
| 17 |
+
Four MHC class II peptide-binding models, one per measurement type, in the precision they were
|
| 18 |
+
trained in. Weights only: the optimizer state and the epoch counter are dropped, nothing else is
|
| 19 |
+
changed.
|
| 20 |
+
|
| 21 |
+
- Code and everything needed to run these: <https://github.com/daylight-00/PREpiBind>
|
| 22 |
+
- float16 copies of the same four models, for the notebooks and Colab:
|
| 23 |
+
[`daylight-00/prepibind-demo`](https://huggingface.co/daylight-00/prepibind-demo)
|
| 24 |
+
- The backbone they call: [`daylight-00/esmc-300m-2024-12`](https://huggingface.co/daylight-00/esmc-300m-2024-12)
|
| 25 |
+
- The full-length HLA embedding store they read:
|
| 26 |
+
[`daylight-00/prepibind-embeddings`](https://huggingface.co/datasets/daylight-00/prepibind-embeddings)
|
| 27 |
+
|
| 28 |
+
## The four files
|
| 29 |
+
|
| 30 |
+
| file | arm | seed | fold | val_loss | predict config |
|
| 31 |
+
|---|---|---|---|---:|---|
|
| 32 |
+
| `prepibind_qualitative_s100_f0.pt` | qualitative binding assays | 100 | 0 | 0.35582 | `configs/predict/config_demo.py` |
|
| 33 |
+
| `prepibind_ms_s128_f3.pt` | mass-spectrometry eluted ligands | 128 | 3 | 0.12274 | `configs/predict/config_ms.py` |
|
| 34 |
+
| `prepibind_ic50_500_s128_f2.pt` | IC50, binder threshold < 500 nM | 128 | 2 | 0.46631 | `configs/predict/config_ic50_500.py` |
|
| 35 |
+
| `prepibind_ic50_1000_s42_f1.pt` | IC50, binder threshold < 1000 nM | 42 | 1 | 0.49456 | `configs/predict/config_ic50_1000.py` |
|
| 36 |
+
|
| 37 |
+
Each is 213.0 MiB, a one-key `{"model_state_dict": ...}` dict of 64 float32 tensors, 55,820,161
|
| 38 |
+
parameters. The seed and the fold are in the filename because they are part of what the file is.
|
| 39 |
+
|
| 40 |
+
All four rows are the val_loss argmin of their arm. **Not yet published:** the files exist locally
|
| 41 |
+
and nothing has been uploaded to HuggingFace; that is a human step, and the sequence is
|
| 42 |
+
`tools/release/upload_plan.md`.
|
| 43 |
+
|
| 44 |
+
## How these four were chosen, and why they are not the paper's numbers
|
| 45 |
+
|
| 46 |
+
Each arm was trained as **15 runs**: three seeds (42, 100, 128) by five cross-validation folds.
|
| 47 |
+
**Every number reported in the paper is a mean over those 15 runs.**
|
| 48 |
+
|
| 49 |
+
A released checkpoint is one single run out of the fifteen — the one with the **lowest validation
|
| 50 |
+
loss** in its arm, the value in the table above. **Validation loss is the selection criterion; the
|
| 51 |
+
`Val ROC-AUC` in the training logs is invalid — `train.py` defines `roc_auc_score(outputs, labels)`
|
| 52 |
+
and calls it with the arguments swapped, so 21.7 % of the logged values fall outside [0, 1] — and
|
| 53 |
+
it was never used for anything.** No test-set quantity took part in the choice either.
|
| 54 |
+
|
| 55 |
+
**The ms row changed on 2026-09-10, and only the ms row.** It was seed 100, fold 1 from
|
| 56 |
+
`250527/1_ms_re`, whose true val_loss is 0.13128. The corrected parse puts the arm's argmin at seed
|
| 57 |
+
128, fold 3 from `260829/ms`, 0.12274. That run was already one of the fifteen this arm averages:
|
| 58 |
+
`analysis/scoring/2_ms/per_run_results.csv` draws 9 of them from `250527/1_ms_re` and 6 from
|
| 59 |
+
`260829/ms` (seed 100 fold 4, and seed 128 folds 0-4). The two directories carry byte-identical
|
| 60 |
+
`config_global.json` and `config_esmc_small.py`, both log `Total samples: 77954` from the one
|
| 61 |
+
`data/dataset/ms_ql/train.csv`, and `260829`'s replicate of three seed-42 folds reproduces the
|
| 62 |
+
earlier root's validation losses exactly (0.14181, 0.14277, 0.14074). So this is a re-selection
|
| 63 |
+
inside the arm, on a criterion that did not change — not a new model, and not a new comparison.
|
| 64 |
+
|
| 65 |
+
The val_loss values above were re-derived on 2026-09-10 from `analysis/val_metrics.csv`, after a
|
| 66 |
+
regex in `analysis/tools/parse_train_logs.py` was fixed: it required an unsigned number for
|
| 67 |
+
`Val ROC-AUC`, so every epoch line carrying one of those negative values was dropped and the
|
| 68 |
+
recorded minimum was a minimum over a filtered subset. It could only ever come out too high, and on
|
| 69 |
+
46.7 % of runs it did. The training itself was never affected — `train.py` checkpoints on the true
|
| 70 |
+
minimum — which is why the epoch stored inside each of these four files (20, 27, 28, 28) matches the
|
| 71 |
+
corrected argmin and not the old one.
|
| 72 |
+
|
| 73 |
+
So a released checkpoint does not reproduce a paper number and is not meant to. It scores a little
|
| 74 |
+
above the mean on most metrics, because it was picked for a quantity correlated with test
|
| 75 |
+
performance — though not on all of them: F1 comes out slightly below the mean in the qualitative and
|
| 76 |
+
ic50_1000 arms. Both are below:
|
| 77 |
+
|
| 78 |
+
| arm | metric | paper (mean ± sd over 15 runs) | this checkpoint alone |
|
| 79 |
+
|---|---|---|---|
|
| 80 |
+
| qualitative | ROC AUC | 0.9193 ± 0.0028 | 0.9211 |
|
| 81 |
+
| | PR AUC | 0.9448 ± 0.0019 | 0.9460 |
|
| 82 |
+
| | F1 | 0.8586 ± 0.0037 | 0.8582 |
|
| 83 |
+
| | accuracy | 0.8375 ± 0.0038 | 0.8391 |
|
| 84 |
+
| | MCC | 0.6683 ± 0.0084 | 0.6736 |
|
| 85 |
+
| ms | ROC AUC | 0.9897 ± 0.0009 | 0.9911 |
|
| 86 |
+
| | PR AUC | 0.9865 ± 0.0012 | 0.9884 |
|
| 87 |
+
| | F1 | 0.9439 ± 0.0042 | 0.9502 |
|
| 88 |
+
| | accuracy | 0.9563 ± 0.0030 | 0.9612 |
|
| 89 |
+
| | MCC | 0.9083 ± 0.0063 | 0.9186 |
|
| 90 |
+
| ic50_500 | ROC AUC | 0.8375 ± 0.0039 | 0.8436 |
|
| 91 |
+
| | PR AUC | 0.7429 ± 0.0055 | 0.7520 |
|
| 92 |
+
| | F1 | 0.6764 ± 0.0146 | 0.6805 |
|
| 93 |
+
| | accuracy | 0.7685 ± 0.0035 | 0.7739 |
|
| 94 |
+
| | MCC | 0.4979 ± 0.0107 | 0.5067 |
|
| 95 |
+
| ic50_1000 | ROC AUC | 0.8347 ± 0.0044 | 0.8401 |
|
| 96 |
+
| | PR AUC | 0.8024 ± 0.0057 | 0.8093 |
|
| 97 |
+
| | F1 | 0.7399 ± 0.0083 | 0.7322 |
|
| 98 |
+
| | accuracy | 0.7554 ± 0.0039 | 0.7575 |
|
| 99 |
+
| | MCC | 0.5101 ± 0.0092 | 0.5110 |
|
| 100 |
+
|
| 101 |
+
Held-out test sets, never used for selection: 48,352 rows (qualitative), 33,490 (ms), 14,150 (both
|
| 102 |
+
IC50 arms). The per-run table these come from is `analysis/scoring/*/per_run_results.csv` in the
|
| 103 |
+
repository. Ignore its `val_auc` column, and the `Val ROC-AUC` in the training logs it comes from:
|
| 104 |
+
the `roc_auc_score` arguments are swapped and the values are meaningless. **Ignore its `val_loss`
|
| 105 |
+
column too** — it was written before the parser fix and is still the filtered minimum, 0.36951 for
|
| 106 |
+
the qualitative checkpoint and 0.14216 for the ms one. The validation losses in this card come from
|
| 107 |
+
`analysis/val_metrics.csv`, which was regenerated on 2026-09-10, and that is the only file to quote
|
| 108 |
+
them from.
|
| 109 |
+
|
| 110 |
+
## Training data
|
| 111 |
+
|
| 112 |
+
From the IEDB Export v3, split into arms by measurement type. The rebuild is
|
| 113 |
+
`pipeline/preprocess/` in the repository; the resulting CSVs are `data/dataset/` there.
|
| 114 |
+
|
| 115 |
+
| arm | directory | train rows | test rows | target column | positives (train) |
|
| 116 |
+
|---|---|---:|---:|---|---:|
|
| 117 |
+
| qualitative | `data/dataset/full/` | 112,871 | 48,352 | `Target` | 58.1 % |
|
| 118 |
+
| ms | `data/dataset/ms_ql/` | 77,954 | 33,490 | `Target` | 39.4 % |
|
| 119 |
+
| ic50_500 | `data/dataset/ic50/` | 33,004 | 14,150 | `Target_500` | 36.7 % |
|
| 120 |
+
| ic50_1000 | `data/dataset/ic50/` | 33,004 | 14,150 | `Target_1000` | 46.0 % |
|
| 121 |
+
|
| 122 |
+
The qualitative arm's training config names `dataset/full_bal/`, a directory since renamed to
|
| 123 |
+
`full`. Same CSVs.
|
| 124 |
+
|
| 125 |
+
Training: AdamW, lr 1e-5, betas (0.9, 0.999), eps 1e-8, weight decay 0.01, five-fold CV, best epoch
|
| 126 |
+
by validation loss (20, 27, 28, 28 for the four files above — the epoch each file stores, and the
|
| 127 |
+
corrected argmin of its run). Both sides were fed from precomputed
|
| 128 |
+
ESM C 300M embeddings, `emb_hla_esmc_small_0430.h5` for the HLA chains and the matching epitope
|
| 129 |
+
store.
|
| 130 |
+
|
| 131 |
+
## Architecture
|
| 132 |
+
|
| 133 |
+
`prepibind.model.plm_cat_mean_inf(hla_dim=960, epi_dim=960, head_div=64)`, 55,820,161 parameters:
|
| 134 |
+
|
| 135 |
+
- 2 self-attention blocks over the HLA chain-pair embeddings, 2 over the epitope embeddings,
|
| 136 |
+
15 heads each (`960 // 64`)
|
| 137 |
+
- 1 joint self-attention block over the two concatenated token sequences
|
| 138 |
+
- masked mean pool, then `Linear(960, 480) -> GELU -> Dropout -> Linear(480, 1)`, one logit
|
| 139 |
+
|
| 140 |
+
Inputs are per-residue ESM C 300M embeddings, 960-dimensional. The head is not a language model and
|
| 141 |
+
never sees a raw sequence: the epitope is encoded at run time by ESM C 300M, the HLA alpha and beta
|
| 142 |
+
chains are read from a precomputed store.
|
| 143 |
+
|
| 144 |
+
## Input format
|
| 145 |
+
|
| 146 |
+
Two columns. The MHC field is the two chain names joined by an underscore. Both names must be keys
|
| 147 |
+
in the HLA mapping table the config points at, spelled exactly as that table spells them. The
|
| 148 |
+
datasets and the demo write **beta first, then alpha**; the order does not change the score, because
|
| 149 |
+
the head carries no positional encoding and pools over tokens, but following the convention keeps
|
| 150 |
+
inputs comparable with the published ones.
|
| 151 |
+
|
| 152 |
+
```csv
|
| 153 |
+
MHC,Epitope
|
| 154 |
+
HLA-DRB1*01:01_HLA-DRA*01:01,PKYVKQNTLKLATA
|
| 155 |
+
HLA-DPB1*03:01_HLA-DPA1*01:03,AAAARYPNVTIAAAA
|
| 156 |
+
H2-IAbB_H2-IAbA,AAAAPAAAATTAAPA
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
Output is the input with `Logits` and `Score` (sigmoid of the logit) appended.
|
| 160 |
+
|
| 161 |
+
Two limits worth knowing before feeding it anything:
|
| 162 |
+
|
| 163 |
+
- **Every training and test epitope is a 15-mer.** The arms were normalised to 15 residues. Other
|
| 164 |
+
lengths run, since the encoder pads and masks, but they are outside the training distribution.
|
| 165 |
+
- **Coverage is 116 chains**: 98 human HLA class II chains and 18 mouse H2 chains, the ones the four
|
| 166 |
+
arms actually use. The research embedding store carries 154 keys; the other 38 (20 BoLA, Mamu and
|
| 167 |
+
SLA chains, plus 18 further human alleles) were curated alongside them, and no released model was
|
| 168 |
+
trained to score them.
|
| 169 |
+
|
| 170 |
+
## Usage
|
| 171 |
+
|
| 172 |
+
```bash
|
| 173 |
+
git clone https://github.com/daylight-00/PREpiBind && cd PREpiBind
|
| 174 |
+
pip install --extra-index-url https://download.pytorch.org/whl/cu126 -e .
|
| 175 |
+
|
| 176 |
+
hf download daylight-00/prepibind prepibind_qualitative_s100_f0.pt --local-dir models
|
| 177 |
+
hf download daylight-00/esmc-300m-2024-12 esmc_300m_2024_12_v0_fp16.pth --local-dir models
|
| 178 |
+
hf download daylight-00/prepibind-embeddings emb_hla_esmc_small_0430.h5 --repo-type dataset --local-dir emb
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
The ESM C encoder is vendored into the package (`prepibind/esmc/`), so `torch` and
|
| 182 |
+
`huggingface_hub` are the only runtime dependencies for inference — no `pip install esm`.
|
| 183 |
+
|
| 184 |
+
```python
|
| 185 |
+
from prepibind.inference import load_config, main
|
| 186 |
+
|
| 187 |
+
cfg = load_config(
|
| 188 |
+
"configs/predict/config_demo.py", # the qualitative model's config
|
| 189 |
+
chkp_path="models/prepibind_qualitative_s100_f0.pt", # this float32 checkpoint
|
| 190 |
+
hla_emb_path="emb/emb_hla_esmc_small_0430.h5", # full-length store
|
| 191 |
+
hla_path="data/mhc_mapping/HLA2_IMGT_MSA_idx_edit.csv", # mapping WITH the windows
|
| 192 |
+
test_path="my_input.csv",
|
| 193 |
+
out_path="outputs",
|
| 194 |
+
)
|
| 195 |
+
cfg["Test"]["precision"] = "as-trained" # bfloat16 ESM C, float32 head. The default in that
|
| 196 |
+
# config is "fp16", which is the demo's setting.
|
| 197 |
+
df = main(cfg) # writes outputs/prediction.csv, returns the DataFrame
|
| 198 |
+
```
|
| 199 |
+
|
| 200 |
+
Three things have to agree, and the code will tell you if they do not: a **full-length** store needs
|
| 201 |
+
the mapping table that carries the window as `sequence|start|end`
|
| 202 |
+
(`data/mhc_mapping/HLA2_IMGT_MSA_idx_edit.csv`), while the bundled demo store is already cut to the
|
| 203 |
+
window and needs `demo/data/mhc_mapping_demo.csv`, which has none. Applying a window twice raises.
|
| 204 |
+
|
| 205 |
+
The CLI does the same thing for the configs' own settings:
|
| 206 |
+
|
| 207 |
+
```bash
|
| 208 |
+
python -m prepibind.inference configs/predict/config_ms.py --chkp_path models/prepibind_ms_s128_f3.pt --plot
|
| 209 |
+
```
|
| 210 |
+
|
| 211 |
+
No GPU is required. Without CUDA, or on a pre-Ampere card, the runtime turns off flash-attn, falls
|
| 212 |
+
back to float32 and says so.
|
| 213 |
+
|
| 214 |
+
## Limitations and known caveats
|
| 215 |
+
|
| 216 |
+
- **One run, not the reported mean.** See above. For a benchmark comparison, use the 15-run means in
|
| 217 |
+
the paper, not one checkpoint.
|
| 218 |
+
- **Class II only.** No class I allele is in the training data or in the embedding store.
|
| 219 |
+
- **The embedding store moved after training.** `emb_hla_esmc_small_0430.h5` as published in
|
| 220 |
+
[`daylight-00/prepibind-embeddings`](https://huggingface.co/datasets/daylight-00/prepibind-embeddings)
|
| 221 |
+
received the 2026-09-08 H2 chain-swap correction and differs from what these checkpoints were
|
| 222 |
+
trained on in 3 of its 154 keys: `H2-IAdA` and `H2-IAdB`, which held each other's arrays, and
|
| 223 |
+
`H2-IAg7A`, same shape and different values. The correction is disclosed, not retro-applied. All
|
| 224 |
+
three are mouse H2 chains and all three are in the training data — 0.48 % to 0.66 % of the rows
|
| 225 |
+
of each arm — so **predictions for H2-IAd and H2-IAg7 come from a model fit on the pre-fix
|
| 226 |
+
arrays**. Every human HLA embedding is unchanged. The dataset card has the per-arm counts.
|
| 227 |
+
- **Training is not bit-reproducible** across GPU models, so a retrained checkpoint will differ in
|
| 228 |
+
the last decimals. Inference on fixed inputs is deterministic.
|
| 229 |
+
- The models score binding, not immunogenicity. A high score is not a prediction that a peptide is
|
| 230 |
+
presented in vivo, still less that it elicits a response.
|
| 231 |
+
|
| 232 |
+
## Licence
|
| 233 |
+
|
| 234 |
+
**MIT** for these weights and for the PREpiBind code, same as the repository.
|
| 235 |
+
|
| 236 |
+
The backbone is not ours, but it is also MIT. ESM C 300M moved to Chan Zuckerberg Biohub:
|
| 237 |
+
[`biohub/esmc-300m-2024-12`](https://huggingface.co/biohub/esmc-300m-2024-12) is ungated and its card
|
| 238 |
+
is tagged `mit` + `other`, and `esm` 3.4.0 — the release the vendored `prepibind/esmc/` source is
|
| 239 |
+
copied from — ships a plain MIT licence, "Copyright 2026 Chan Zuckerberg Biohub, Inc.". These
|
| 240 |
+
checkpoints were produced by training on ESM C 300M embeddings. Checked 2026-09-10; the repository's
|
| 241 |
+
`THIRD_PARTY_NOTICES.md` records the evidence and every other dependency.
|
| 242 |
+
|
| 243 |
+
Training data derives from the IEDB Export v3 (free to use, asks to be cited) and allele sequences
|
| 244 |
+
from IPD-IMGT/HLA and UniProt.
|
| 245 |
+
|
| 246 |
+
## Citation
|
| 247 |
+
|
| 248 |
+
```bibtex
|
| 249 |
+
@article{jang2026prepibind,
|
| 250 |
+
title = {PREpiBind: Protein Representation-integrated Epitope-MHC Class II Binding Prediction},
|
| 251 |
+
author = {Jang, David Hyunyoo and Kim, Dongwoo and Park, Byungho and Hwang, Untaek and Choi, Yoonjoo and Lee, Juyong},
|
| 252 |
+
journal = {bioRxiv},
|
| 253 |
+
year = {2026}
|
| 254 |
+
}
|
| 255 |
+
```
|
| 256 |
+
|
| 257 |
+
The preprint is not posted yet; the DOI goes here when it is.
|