Skip to content

GRASS-MIL 1.1.0: one default path, niche analysis as primary results, validity fixes - #23

Merged
LRabuzin merged 9 commits into
mainfrom
cleanup-1.1
Oct 16, 2026
Merged

LRabuzin merged 9 commits into
mainfrom
cleanup-1.1

Conversation

@LRabuzin

Copy link
Copy Markdown
Collaborator

What does this change?

GRASS-MIL 1.1.0 turns the 1.0.0 paper companion into a package with one default path: BGRL pretraining, then finetune with an automatically fitted probe head, then nested CV, per-cell extraction and niche analysis. See CHANGELOG.md for the full list.

Breaking: commands, configuration keys and file formats changed. Graphs and checkpoints from 1.0.0 must be rebuilt.

  • Regimes renamed, with finetune as the default: linear_ftall → finetune, linear_ftall_reg → finetune_reg, linear_probehead → attention_only, mil → mlp_head, mil_frozen → mlp_head_frozen, pretrain_a3 → pretrain. The probe head is now fitted automatically, so grass-mil-probe is gone.

  • grass-mil-niches reports the primary results:

    • niches in the attention space;
    • their composition, association and attention-weighted margins, with patient-bootstrap intervals;
    • per-niche Moran's I, neighbourhood enrichment, cross-L and contacts;
    • optional survival analysis.

    grass-mil-metaclusters and grass-mil-spatial-stats are optional.

  • Removed:

    • commands: grass-mil-report, -eval, -loocv, -scores and -probe;
    • mean pooling and the GCN/GAT/SAGE/GIN encoders;
    • the 1.0.0 checkpoint import and the unused presets.
  • Configuration:

    • one data.input block for CSV, TSV, h5ad and SCE;
    • data.unit (region, tile or polygon);
    • labels attached at load time, so one set of graphs serves every task and fold;
    • task (classification, regression or survival), each with its own selection metric.
  • Fixes:

    • Training:
      • Gradient clipping now acts on unscaled gradients under AMP.
      • CV refits no longer evaluate on the held-out fold, and they keep the inner learning-rate schedule.
      • Survival training optimises over several units per step.
      • The Cox loss handles tied times (Breslow), and the c-index handles ties correctly.
      • Outer folds must keep each patient together.
    • Statistics:
      • Margin intervals resample patients.
      • Cross-L is measured relative to random labelling.
      • Moran's I uses exact randomisation moments.
    • Data and prediction:
      • grass-mil-predict reports AUROC, works without regime=, and refuses cohorts preprocessed differently from training.
      • The PCA reducer works.
      • Numeric sample ids work.
      • Stale graphs are detected.
  • Docs:

    • a README with a step-by-step "Your first cohort";
    • four guides (data preparation, training and evaluation, niche analysis, configuration);
    • three re-executed vignettes on squidpy.datasets.mibitof().

LRabuzin and others added 9 commits October 15, 2026 08:42
- data: one input config for CSV/h5ad/SCE, labels attached at load time,
  splits drawn at setup (stratified, patient-grouped), data.unit
  (region|tile|polygon), one ego-graph sampler subclassing PyG's ShaDow
  sampler, fingerprinted processed graphs, fixed PCA serialisation
- models: GINE encoder, projected gated attention, logit-space pooling;
  one manual-optimisation training path with gradient clipping after AMP
  unscaling, probe head fitted in setup, Cox/regression objectives over a
  hyperbatch, modules rebuilt from their checkpoints alone
- configs: model/task/regime groups; finetune is the default regime;
  regimes renamed (finetune, finetune_reg, attention_only, mlp_head,
  mlp_head_frozen, pretrain)
- CLIs: train, nested-cv (refit without validation), extract, predict

Tests, niche analysis and docs follow in later commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
…rs optional

- grass_mil.niches: niche discovery in the attention-head space (cosine
  HDBSCAN on a sample, k-NN assignment), niches numbered by margin with
  Background for unassigned cells; per-niche composition, empirical
  association and attention-weighted margin with a bootstrap CI over units;
  per-niche Moran's I, neighbourhood enrichment, cross-L at r* and contact
  enrichment; niche co-localisation; optional survival (Cox per niche share,
  Kaplan-Meier of score tertiles); figures following the preprint panels
- grass_mil.niches.replication: grass-mil-metaclusters on the same discovery
- grass_mil.spatial: one implementation of every statistic, shared by the
  niche descriptors and grass-mil-spatial-stats
- remove the report/plugin stack, grass-mil-scores, the instance export and
  the evaluation package
- packaging: 1.1.0, seven entry points, dependencies trimmed to imports

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
Data (inputs, graphs, units, labels, splits, PCA, stale-graph guard),
sampling (exact k-hop ego-graphs, every cell scored once, fixed roots,
interior roots, radius cap), models (pooling identity, Cox loss, probe
folding, augmentations, clipping hook, freezing), training (every regime,
regression, survival, rebuild from checkpoint, no-validation runs), nested
CV (selection fallbacks, refit without validation), prediction, extraction,
niches, spatial statistics, replication, configs and entry points, and the
whole workflow through the installed commands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
…ross-L titles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
- README: the method, install, quick start, a workflow table, changing the
  defaults, documentation and citation; no preprint-reproduction section
- docs/: data preparation, training and evaluation, niche analysis and a
  configuration reference replace the sixteen previous pages (including the
  method-correspondence page and the generated repository map)
- vignettes: rewritten and executed on squidpy's MIBI-TOF data (tiles as
  units, a CD8-infiltration phenotype): pretraining, the default regime,
  niches, nested cross-validation, replication and model-free statistics;
  outputs carry no local paths
- CI: one test workflow on Python 3.11/3.12 and macOS with torch-sparse,
  one pre-commit workflow; no release drafter; real coverage thresholds;
  weekly dependabot; a short PR template
- environment.yaml installs the package; Makefile targets test/test-all;
  CHANGELOG 1.1.0
- spatial-stats compares patients or units (compare_by); niches are numbered
  by the margin statistic the table reports; random splits stratify only when
  every patient has one class

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
Training and evaluation
- Cox loss: tied times share the Breslow risk set; survival heads start from
  a ridge-Cox probe; Harrell's C counts censoring at an event time as at risk
- the last, smaller hyperbatch of an epoch is stepped before validation
- nested CV refuses folds that split a unit or patient, and refits keep the
  inner runs' learning-rate schedule (finetune.schedule_epochs)
- class labels must be integers 0..C-1; patient metrics need one target per
  patient; explicit splits may not overlap
- PCA fit_on=train uses the split setup() draws; per-sample PCA removed;
  PCA from the feature covariance
- InputSpec carries feature names, edge features and a preprocessing
  fingerprint; predict and extract refuse cohorts preprocessed differently;
  predict scores the roots the test split scores
- CSV features=all keeps numeric columns; non-finite inputs are refused
- checkpoints load with weights_only=True and once per command

Niche statistics
- margins are relative to the model's mean instance logit (recorded at
  extraction) instead of the head bias, whose offset set every margin's sign
- margin intervals resample patients (NaN with one patient)
- cross-L relative to random labelling (cancels window and edge effects)
- Moran's I tested with exact randomisation moments; enrichment adds a
  size-free log2 ratio, which spatial-stats compares
- Cox fit with step halving and convergence check; outcomes with missing
  values dropped
- cell caches resume only over files of the same checkpoint
- transfer consensus shares are descriptive (no in-sample tests)

Simplifications: one strict-config helper and one plain() helper, one
group-test and patient-label helper, vectorised attention and contact
counts, per-unit slices in Cells, sample-weight path and freeze_head removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
Restores the bias-omitted margin of the method; the single-patient NaN
interval stays. Vignettes re-executed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
data.pyg.org has no page per bare PyTorch version (torch-2.4.1.html is a
404); point at the CPU wheels (torch-<version>+cpu.html), which serve CUDA
builds of PyTorch too since ego-graph sampling runs on the CPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
From a new-user walkthrough of the docs:
- README: "Your first cohort" with a project layout, the cohort file in
  configs/experiment with --config-dir, hydra.run.dir per step and what to
  look at in every output folder
- data preparation: sample / region / unit / patient defined; scripts for
  the manifest and for patient-grouped, class-stratified folds
- training: plain explanations of BGRL, the probe and the hyperbatch; which
  nested-CV number to report; a small-cohorts section (frozen encoder, probe
  penalty, selection on the loss)
- niche analysis: how to read the margins, small-cohort clustering settings,
  +clustering keys for metaclusters; configuration: command-line basics
- the template reads ./data by default and shows --config-dir
- commands print their checkpoint, summary and output folders; warnings for
  a large probe head, warmup >= pretraining steps, no niches found and
  single-class outer folds; the nested-CV summary reports the task's metric
  whatever metric selects the epochs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NqExFvaq8RhDYBj6RShCMk
@LRabuzin
LRabuzin merged commit 281df20 into main Oct 16, 2026
4 checks passed
@LRabuzin
LRabuzin deleted the cleanup-1.1 branch October 16, 2026 08:01
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