Architecture¶
PGVL-Gym separates experiment orchestration from paper-specific model code. The unified trainer knows only the method interface and loader contract; the adapter owns the method's model construction and optimization recipe.
Protocol YAML
│
├── dataset labels, prompts, folds, shots
├── feature sources and resolutions
└── experiment registry
│
▼
Generated run config ──► MethodBackboneContract validation
│
├──► feature loader ──► method-specific batch
└──► BaseMethod adapter ──► paper model
├── build_model
├── train_step
├── eval_step
└── interpret_step (optional)
Stable layers¶
Protocol and configuration layer¶
scripts/tcga_benchmark.py compiles a protocol into manifests, deterministic
patient-disjoint folds, method-native prompt assets, validated YAML configs,
and a run matrix. Dataset names are not hardcoded in model adapters.
Method layer¶
Every registered adapter subclasses methods.base.BaseMethod. The common
trainer calls the same lifecycle for every method:
Training-free adapters may declare training_free = True. They build all
fold-local immutable state from the support loader in prepare_fold, expose no
trainable parameters or optimizer, receive one validation pass, and then enter
the same logits/metrics/test reporting path as trained methods. MI-VisionShot
uses this boundary for class prototypes; validation and test data are never
available while those prototypes are constructed.
- validate the encoder contract;
- build the model;
- construct the optimizer and scheduler;
- pass loader batches to
train_steporeval_step; - collect the normalized result mapping.
interpret_step is a deliberately opt-in evaluation hook. Its base
implementation raises NotImplementedError; adapters override it only when
the model exposes an audited patch-aligned quantity with known score semantics
and ordering. The shared common.interpretability layer validates that typed
result, resolves level-0 feature coordinates, and serializes the raw score,
rendering, and provenance manifest. It never imports or infers behavior from a
concrete adapter.
See Patch-level interpretability for the provider and artifact contracts.
Vendored model files remain under methods/<method>/. Cross-method utilities
belong under common/ only when their semantics are genuinely shared.
Encoder layer¶
EncoderBundle wraps the native model and tokenizer while exposing narrow
capabilities such as text encoding, tile encoding, soft prompting, or native
slide projection. MethodBackboneContract then restricts how an adapter may
consume those capabilities.
See Backbone interfaces for the compatibility matrix and registration API.
Data layer¶
Loader selection is based on tensor level, not on a paper name:
| Tensor level | Typical shape | Shared loader responsibility |
|---|---|---|
| Patch bag | [patches, dim] |
exact feature key and width |
| Dual-scale patch bag | two variable-length bags | independent low/high sources |
| Slide embedding | [dim] |
source type, key, width, slide ID |
| Patch sequence + report | [frames, dim] plus text |
sequence/report pairing |
| Raw tile directory | image tiles | tile sampling and transforms |
Dependency direction¶
train.py and eval.py may depend on methods and common. Adapters may
depend on common and their own vendored implementation. common must never
import a concrete method adapter. This keeps registry imports lazy and makes
the generated API documentation safe to build without loading model weights.