Configuration¶
An experiment is one soma.ExperimentConfig. Its seven sections each
hold the keyword arguments of one public callable, so a YAML file and a Python
config spell every field the same way: a knob that cannot be passed in Python
cannot appear in YAML.
Section |
Callable |
Holds |
|---|---|---|
|
|
|
|
|
|
|
the feature source |
the |
|
|
|
|
|
the learner’s name and params: the model’s |
|
|
|
|
|
|
Only cohort is required. A field left out keeps its default;
examples/reference.yaml spells every section at its defaults (see the
CLI reference).
Components¶
Every swappable component is a {name, params} leaf: the encoder, the
aggregator, decoder and head, the optimizer and each scheduler, the sampler, the
augmentation, each trainer callback and the learner itself. name is a
registry key or a dotted import path to a class; params are its constructor
keywords, validated against the params the component declares (ADR 0018). A bare
name stands for {name, params: {}}. soma list KIND prints every
registered component of a kind with its params and their defaults.
learner:
name: mil
params:
aggregator: abmil
head:
name: multiclass_classification
params: {dropout: 0.1}
optimizer: {name: adamw, params: {lr: 3.0e-4}}
The learner decides the training path. mil trains the set shape (a bag or a
vector per sample) and dense the grid shape (a feature grid per sample,
segmentation and detection). The head decides the task family. A set run’s
sample is a patient with cohort.collapse: patient_id, a tile when a tile
encoder runs without an aggregator, and otherwise a slide.
Editing a config¶
A config has no override mechanism: you edit it. In Python, assign its fields:
from soma import presets
config = presets.mil_classification("uni2")
config.run.seed = 1
config.trainer.max_steps = 2000
config.learner.params["head"].params["dropout"] = 0.2
A field write runs no check of its own; soma.plan.check() builds every
section again, so a value its constructor would refuse is refused before
anything runs. A step budget written over an epoch budget replaces it, as at
construction. On the command line, edit the YAML file: soma init PRESET
prints a preset (see Use cases) with every default filled in, and
soma run config.yaml runs a file. ExperimentConfig.from_yaml(path) reads
a YAML file and to_yaml(path) writes one.
Checking and resolving¶
soma.plan.check() validates a config across its sections (for example that
a decoder belongs to the dense learner, or that a survival head is not given
tile samples) without reading any file. soma.resolve() then expands the
config against its cohort into a soma.Plan: every component’s params
with their defaults, plus what the cohort decides, such as a classification
head’s label map. A value the config states wins; a contradiction raises. A
null value counts as unset, so the cohort decides it.
Pipeline runs both at its start. A plan is frozen: its sections
refuse field writes.
The run directory keeps config.yaml (the config as given) and plan.yaml
(the plan). plan.yaml loads as a config, so soma run plan.yaml replays a
run with every value pinned, while soma run config.yaml resolves again
against the current code and cohort. See The plan for how a resume compares plans and
what the experiment identity digests.
- class soma.ExperimentConfig(*, run=<factory>, cohort, features=<factory>, loaders=<factory>, learner=None, trainer=<factory>, evaluation=<factory>)¶
Bases:
EditableA whole experiment: the seven sections. Only
cohortis required; a run that trains needs alearnerwith ahead.
- soma.resolve(config, cohort)¶
Expand
configinto itsPlanagainstcohort.Every
{name, params}leaf takes its registered params dataclass’s defaults; the head’sinferhook adds what the cohort decides, under the explicit params (a differing explicit value raises); the head’stargetsare spelled out. The plan is normalized through its YAML form, so a Python config and the YAML it saves resolve to one plan.- Return type:
- class soma.Plan(config)¶
Bases:
MappingA resolved config, frozen: section name -> section.
plan.learner(orplan["learner"]) is the resolved section; its sections refuse field writes.to_yaml()/from_yaml()round-trip it, and two plans are equal when their YAML forms are.
- class soma.config.Component(name, params=<factory>)¶
Bases:
objectA
{name, params}leaf: a registry key or a dotted import path, and its params.
Presets¶
A preset returns a complete config for a common task (see Use cases);
soma.components("presets") lists them with their arguments.
- soma.presets.mil_classification(encoder, aggregator='abmil')¶
Classify whole slides: a frozen tile encoder, a MIL aggregator and a classifier.
Each slide is tiled at the encoder’s recommended size and spacing (tiles with at least 10% tissue), its tiles are embedded once and cached, and
aggregatorpools the bag for amulticlass_classificationhead (two classes or more; the classes come from the cohort’slabelcolumn). AdamW (lr 1e-4) with a cosine schedule, 50 epochs, the best tune-loss checkpoint and early stopping after 10 epochs without improvement.- Return type:
- soma.presets.tile_classification(encoder)¶
Classify tile images: a linear probe on a frozen tile encoder’s embeddings.
Each row of the cohort is one image, embedded whole by
encoder(no tiling, no aggregator), and amulticlass_classificationhead learns the cohort’slabelcolumn. Batches of 256 embeddings, AdamW (lr 3e-4) without a schedule, 50 epochs, the best tune-loss checkpoint and early stopping after 10 epochs without improvement.- Return type:
- soma.presets.survival(encoder, aggregator)¶
Predict survival from whole slides: a discrete-time hazard head over a MIL bag.
Slides are tiled and embedded as in
mil_classification();aggregatorpools the bag for thesurvivalhead, which reads the cohort’stime,eventandbincolumns (the number of bins comes from the cohort). AdamW (lr 1e-4) with a cosine schedule, 50 epochs, selection and early stopping on the tune loss.- Return type:
- soma.presets.segmentation(encoder, decoder, num_classes=2)¶
Segment tissue: a decoder over a frozen encoder’s dense token grid.
Each tile (at the encoder’s recommended size and spacing) is encoded once into a token grid and cached;
decoderupsamples it to per-pixelsegmentationlogits fornum_classesclasses, whose indices the label masks hold (background 0 and one foreground class by default). For masks holding raw values, replace the head’s params with aclassesmapping (class name -> raw mask values) andignore. Batches of 8, AdamW (lr 1e-4) with a cosine schedule, 50 epochs, selection and early stopping on the tune loss.- Return type:
- soma.presets.detection(encoder, decoder, num_classes=1, match_distance=3.0)¶
Detect cells: a decoder over a frozen encoder’s dense token grid, one heatmap per class.
Each image is encoded once into a token grid and cached;
decoderpredicts a peak heatmap for each ofnum_classesclasses, whose indices the point annotations hold (one cell class by default), scored by F1 atmatch_distanceµm (3 µm, about a nucleus radius, by default). For points holding raw classes, replace the head’s params with aclassesmapping (class name -> raw point classes) anddrop. One image per batch, AdamW (lr 1e-4) with a cosine schedule, 50 epochs, selection and early stopping on the tune loss.- Return type: