Skip to content

Fix evaluation leakage & methodology; de-duplicate model; honest reporting - #2

Open
deepanshumody wants to merge 1 commit into
mainfrom
methodology-fixes
Open

deepanshumody wants to merge 1 commit into
mainfrom
methodology-fixes

Conversation

@deepanshumody

Copy link
Copy Markdown
Owner

Why

The headline ROC-AUC ≈ 0.95 on 1FUF was in-sample: 1FUF is in the training list (data/nonredundantRNA.txt) and the splits shuffled candidate sites, so near-identical grid points from one structure leaked across train/val/test. This PR corrects the methodology, makes the claims honest, removes ~5× model duplication, and adds tests.

Core fixes

  • Structure-level split (rna_splits.py) — group by PDB id, never by site; hold whole structures out of the corpus (HELD_OUT_PDBS). Wired into train_gnn{,1,2}.py and the GNN-DTI trainer.
  • Imbalance-aware — pos_weight in BCEWithLogitsLoss; report PR-AUC + enrichment + precision/recall@Youden alongside ROC-AUC (rna_metrics.py).
  • Reproducibility — single seed (rna_seed.py).
  • Focal loss — apply (1 − p_t)^γ per example (was reduced to a scalar first, collapsing it to a scaled BCE).
  • Atom features — Mg is now an explicit symbol with an X catch-all (it was silently encoded as hydrogen). One shared dataset_creation/atom_features.py replaces 4 copies; dead stale copy removed from GNN-DTI/utils.py.
  • De-duplicate the GCN (was copied across 5 files) into gcn_model.py; models read feature width from the data.

Honesty

  • README + Streamlit demo relabel the 0.95 as an in-sample sanity check and correct the "edges = spatial proximity" claim — the deployed GCN uses the covalent adjacency (A1); geometry (A2) is used only by the GNN-DTI variant.

Test plan

  • ruff check . ✅ · python -m compileall -q . ✅ · pytest -q ✅ 24 passed (was 3)
  • New tests: structure-level split (no PDB across folds), imbalance metrics, focal loss, atom-feature Mg fix, GCN forward + shipped best_model.pth still loads (guards the demo), width-agnostic GNN-DTI.
  • Ran predfrommodel.py on real 1FUF → reproduces ROC-AUC=0.951 PR-AUC=0.014 top 4.9%, now labelled in-sample.

⚠️ Still needs a retrain (can't be done in-repo)

Only 1fuf.pkl ships, not the ~397-structure corpus, so the honest held-out number can't be produced here. To finish: regenerate pickles (dataset_creation/gnn_rna.py) on the full PDB set and run train_gnn.py, which now prints a HELD-OUT: line. Expect it to be lower than 0.95 — that's the point.

The headline "ROC-AUC ~0.95 on 1FUF" was in-sample: 1FUF is in the
training list (data/nonredundantRNA.txt) and the splits shuffled candidate
*sites*, so near-identical grid points from one structure leaked across
train/val/test. This corrects the methodology and the claims, and adds tests.

Core fixes
- Structure-level split (rna_splits.py): group by PDB id, never by site;
  hold whole structures out of the corpus. Wired into all trainers + GNN-DTI.
- Imbalance-aware: pos_weight in BCEWithLogitsLoss; report PR-AUC +
  enrichment + precision/recall@Youden, not ROC-AUC alone (rna_metrics.py).
- Reproducibility: single seed via rna_seed.py.
- Focal loss: apply the (1-p_t)^gamma modulation per example (was reduced to
  a scalar first, collapsing it to a scaled BCE).
- Atom features: 'Mg' is now an explicit symbol with an 'X' catch-all; it was
  silently encoded as hydrogen. Single shared dataset_creation/atom_features.py
  replaces 4 copies; dead stale copy removed from GNN-DTI/utils.py.
- De-duplicate the GCN (was copied across 5 files) into gcn_model.py; models
  read feature width from the data so an extended vocabulary just works.

Honesty
- README + Streamlit demo relabel the 0.95 as an in-sample sanity check and
  correct the "edges = spatial proximity" claim (the deployed GCN uses the
  covalent adjacency; geometry is used only by the GNN-DTI variant).

Tests
- New: structure-level split, imbalance metrics, focal loss, atom-feature Mg
  fix, GCN forward + shipped-checkpoint load (guards the demo), width-agnostic
  GNN-DTI. 24 passing; ruff + compileall clean.

Note: the honest held-out number still requires regenerating the full corpus
(only 1fuf.pkl ships) and running train_gnn.py, which now prints a HELD-OUT line.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c010e7c9b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread GNN_models/train_gnn1.py

out = None
groups = [g.pdb for g in data_list]
train_idx, valid_idx, test_idx = group_train_val_test_split(groups, (0.8, 0.1, 0.1), seed=SEED)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove 4RUM before tensor splits

When the saved-tensor corpus is built from the bundled nonredundant RNA list, it includes 4RUM, but this split is computed over the full data_list and only later loads pd4rum_graphs.pt as HELD-OUT. That lets 4RUM graphs land in train/valid/test while the same structure is reported as held out, so the advertised leakage-free HELD-OUT metric is contaminated; filter the 4RUM group out before calling group_train_val_test_split.

Useful? React with 👍 / 👎.

Comment thread GNN_models/train_gnn2.py

out = None
groups = [g.pdb for g in data_list]
train_idx, valid_idx, test_idx = group_train_val_test_split(groups, (0.8, 0.1, 0.1), seed=SEED)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove 4RUM before tensor splits

This variant has the same leakage path: if saved_tensors is generated from the nonredundant corpus containing 4RUM, the full data_list is grouped and split here, then pd4rum_graphs.pt is evaluated as HELD-OUT afterward. Any 4RUM tensors selected into train/valid/test make the held-out result in-sample, so exclude the held-out PDB before this split.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant