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

run

Pipeline

output_dir, seed, run_id, resume, folds, mirror_root

cohort

Cohort

records and splits (the two CSV files), collapse, tune_is_test, allow_missing_tune

features

the feature source

the encoder leaf, slide2vec’s extraction, execution and dense options, the cache, kind and spacing_policy (see Preprocessing)

loaders

soma.learn.loaders()

batch_size, worker settings, the sampler and augment leaves

learner

Learner

the learner’s name and params: the model’s aggregator or decoder and head leaves, optimizer, scheduler, normalization, projection and the learner’s own keywords

trainer

Trainer

max_epochs or max_steps, accumulate_grad_batches, val_every_n_epochs, device, precision, callbacks

evaluation

soma.evaluate()

metrics, subgroups, holdout_test, overwrite_test, the saved artifacts, attention_heatmaps and representation

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: Editable

A whole experiment: the seven sections. Only cohort is required; a run that trains needs a learner with a head.

soma.resolve(config, cohort)

Expand config into its Plan against cohort.

Every {name, params} leaf takes its registered params dataclass’s defaults; the head’s infer hook adds what the cohort decides, under the explicit params (a differing explicit value raises); the head’s targets are 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:

Plan

class soma.Plan(config)

Bases: Mapping

A resolved config, frozen: section name -> section.

plan.learner (or plan["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: object

A {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 aggregator pools the bag for a multiclass_classification head (two classes or more; the classes come from the cohort’s label column). 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:

ExperimentConfig

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 a multiclass_classification head learns the cohort’s label column. 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:

ExperimentConfig

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(); aggregator pools the bag for the survival head, which reads the cohort’s time, event and bin columns (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:

ExperimentConfig

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; decoder upsamples it to per-pixel segmentation logits for num_classes classes, 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 a classes mapping (class name -> raw mask values) and ignore. Batches of 8, AdamW (lr 1e-4) with a cosine schedule, 50 epochs, selection and early stopping on the tune loss.

Return type:

ExperimentConfig

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; decoder predicts a peak heatmap for each of num_classes classes, whose indices the point annotations hold (one cell class by default), scored by F1 at match_distance µm (3 µm, about a nucleus radius, by default). For points holding raw classes, replace the head’s params with a classes mapping (class name -> raw point classes) and drop. One image per batch, AdamW (lr 1e-4) with a cosine schedule, 50 epochs, selection and early stopping on the tune loss.

Return type:

ExperimentConfig