Skip to content

Add NetCleave predictor for MHC-I/II C-terminal cleavage (#213) - #217

Merged
iskandr merged 3 commits into
masterfrom
netcleave-predictor
Jul 9, 2026
Merged

Add NetCleave predictor for MHC-I/II C-terminal cleavage (#213)#217
iskandr merged 3 commits into
masterfrom
netcleave-predictor

Conversation

@iskandr

@iskandr iskandr commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Closes #213.

Adds NetCleave, filling the MHC-II antigen-processing gap: NetChop and pepsickle only cover MHC-I proteasomal cleavage, and no class-II (endolysosomal) option existed in the predictor set.

What's new

Ontology

  • Kind.endolysosomal_cleavage (MHC-II, cathepsin pathway) alongside the existing proteasome_cleavage (MHC-I, cytosolic). Naming follows the decision recorded on #213: name the machine when one dominates, the compartment when the machinery is a diffuse family.
  • PeptideResult.endolysosomal_cleavage accessor.

Predictor

  • NetCleave + NetCleave_I / NetCleave_II subclasses. Class I → proteasome_cleavage, class II → endolysosomal_cleavage.
from mhctools import NetCleave_II

p = NetCleave_II()                                  # resolves NETCLEAVE_DIR / ~/NetCleave
p.predict(["SIINFEKL"], c_flanks=["DGH"])[0].endolysosomal_cleavage.score
p.predict_proteins({"TP53": "MEEPQ..."}, peptide_lengths=[15])   # real protein context

Design: why not a ProcessingPredictor subclass

NetChop/pepsickle produce a per-position cleavage profile that the base class collapses to a per-peptide score. NetCleave instead emits one C-terminal score per peptide (the 4+3 cleavage site: last 4 residues of the peptide + 3 downstream). So it doesn't fit the cleavage_probs contract — it's a standalone subprocess predictor (NetChop-style: write a temp CSV, shell out to NetCleave.py --predict --pred_input 3, parse the output). Because the C-terminal site needs downstream residues, predict() requires c_flanks (≥3); predict_proteins() supplies real context automatically.

Distribution — no gated download, no R for prediction

  • Weights ship in the git repo as small Keras .h5 files (~621 KB each), including the class-II models (II_mass-spectrometry_HLA-DR/DP/DQ). Just git clone.
  • The R dependency in NetCleave's README is a red herring for predictioniedb_processing.R is only used in training-data generation and is not referenced by any Python in the --predict path (verified). --predict needs only Python (tensorflow/keras, scikit-learn, biopython, pandas). The wrapper shells out to a user-provided interpreter (python_executable= to override), vendoring nothing (NetCleave is GPL-v2).
  • The .h5 files are loaded as weights into an architecture NetCleave rebuilds in code, so loading is robust across Keras versions (verified on TF 2.21 / Keras 3.14).

Faithfulness — verified against upstream CLI

Tests reproduce NetCleave's own NetCleave.py --predict output exactly (abs=1e-3) on both the class-I and class-II pan models — reference values generated from the upstream CLI, not this wrapper (non-circular). A class-I > class-II signal check reflects the paper's AUC 0.91 vs 0.66.

⚠️ Class-II C-terminal cleavage is a genuinely weak signal (paper AUC ~0.66 vs ~0.91 for class I — the class-II C-terminus sits outside the binding groove and cathepsin specificity is diffuse). Documented in the class docstring, the README, and asserted-relatively in tests.

Scope

  • In: --predict (pred_input 3, protein-sequence context), both classes, protein scanning.
  • Out: NetCleave's --generate/--train modes and the UniProt-fetching pred_input 2 (avoids network in predict()); no vendoring.
  • Not wired into the CLI (its allele-centric interface doesn't fit a cleavage predictor with flanks) — Python API only, like the other processing predictors' advanced use.

Tests

  • Model-free (run in CI without an install): constructor validation (bad class, missing path).
  • Model-gated on NETCLEAVE_DIR (skip when absent, mirroring test_nettcr.py): CLI-reference reproduction for both classes, kind mapping + kind_support metadata, predict_proteins with correct offset/context, c_flanks required, short-flank → empty result with 1:1 alignment, dataframe schema, determinism, subclasses.

Full suite: 238 passed, 25 skipped without the DTU/NetCleave installs; 15/15 NetCleave model tests pass with the install. Ruff clean. Version 3.15.0 → 3.16.0.

https://claude.ai/code/session_01LZahFhBSCiehXTESCYQ7wG

iskandr added 3 commits July 8, 2026 20:32
Adds NetCleave, filling the MHC-II antigen-processing gap: NetChop and
pepsickle only cover MHC-I proteasomal cleavage, and no class-II
(endolysosomal) option existed.

- New Kind.endolysosomal_cleavage (MHC-II, cathepsin) alongside the existing
  proteasome_cleavage (MHC-I, cytosolic); PeptideResult.endolysosomal_cleavage
  accessor. Naming per the decision recorded on #213.
- NetCleave predictor (+ NetCleave_I / NetCleave_II subclasses). Unlike
  NetChop/pepsickle it emits one C-terminal score per peptide, so it shells
  out to NetCleave.py (NetChop-style subprocess) rather than subclassing
  ProcessingPredictor. Class I -> proteasome_cleavage, class II ->
  endolysosomal_cleavage.
- predict(peptides, c_flanks=...) scores peptides given their downstream
  residues; predict_proteins() scans proteins so peptides are scored in real
  context. DataFrame variants included.

NetCleave ships its Keras .h5 weights in-repo (git clone, no download); the R
dependency in its README is only for training-data generation, not
prediction (confirmed: not referenced by any Python in the predict path).

Tests reproduce NetCleave's own CLI output exactly (abs 1e-3) on both the
class-I and class-II pan models, from upstream `NetCleave.py --predict`
(non-circular). Model-free constructor/validation tests run in CI without an
install; model tests skip-gate on NETCLEAVE_DIR. A class-I > class-II signal
check reflects the paper's AUC 0.91 vs 0.66.

Version 3.15.0 -> 3.16.0.

Claude-Session: https://claude.ai/code/session_01LZahFhBSCiehXTESCYQ7wG
Addresses two bugs found in review (self-review + ultrareview):

1. predict_proteins crashed ("NetCleave returned N rows for M epitopes") on
   any protein containing a repeated peptide, and predict() failed when a
   peptide recurred within peptide+flank. NetCleave's pred_input 3 emits one
   row per regex occurrence of the epitope, so output rows are not 1:1 with
   input rows. Fix: score each peptide in its own surrogate protein
   (peptide + downstream flank) and de-multiplex output back to inputs by
   row id + expected cleavage site, instead of asserting positional 1:1. The
   surrogate yields identical scores (the site depends only on the C-terminal
   4 + 3 downstream residues) and shrinks the predict_proteins CSV.

2. Concurrent NetCleave instances collided on a shared output/<basename>.csv
   (basename was pid + per-instance counter). Fix: use uuid4 in the basename.

Also: document that an instance isn't thread-safe, and return [] from
predict([]) instead of raising.

Adds regression tests: repeated peptide in predict_proteins (both occurrences
scored in their own downstream context, cross-checked against predict()),
peptide recurring in flank, two-instance independence, and empty input.

Claude-Session: https://claude.ai/code/session_01LZahFhBSCiehXTESCYQ7wG
Belt-and-suspenders for the occurrence-mapping fix: when resolving each
input row's score, verify NetCleave's output rows for that row id actually
carry the requested epitope, so a future NetCleave output-contract change
raises loudly instead of silently misaligning scores.

Claude-Session: https://claude.ai/code/session_01LZahFhBSCiehXTESCYQ7wG
@iskandr
iskandr merged commit 173ecf4 into master Jul 9, 2026
4 checks passed
@iskandr
iskandr deleted the netcleave-predictor branch July 9, 2026 03:02
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.

Add NetCleave integration for MHC-I/II C-terminal cleavage prediction

1 participant