(case, prediction) one source image at a time, instead of all of them at once.
best_model
Engine.best_model(key='dice', split='validation')
Rank the models on one column.
Refuses to rank on loss when the models were not trained on the same one. Two losses are two different scales: a Focal-Tversky of 0.05 is not better than a CCE-Dice of 0.14, it is not even the same question. Metrics are comparable because they measure the mask, not the objective.
Parameters
Name
Type
Description
Default
key
str
Which column to rank on.
'dice'
split
str
Which data to rank on.
'validation'
Returns
Name
Type
Description
str
The winning model’s name.
Raises
Name
Type
Description
EngineError
If there is no such column; the message lists the ones there are.
only_images defaults to what the split actually has rather than to False.
It used to default to False, so asking for a test split of images alone built a dataset that would read masks and fail on the first item. The engine knew: it had just discovered the split without them.
Parameters
Name
Type
Description
Default
model
SegmentationModel
Which model’s settings to read.
required
split
str
Which data.
required
augmented
bool
Apply the model’s augmentation. Training only.
False
only_images
bool | None
Read images alone. Defaults to what the split actually has.
None
Returns
Name
Type
Description
SegmentationDataset
A SegmentationDataset. inspect_masks() on it is what fit calls to refuse
SegmentationDataset
a colormap matching none of the labelled tissue.
evaluate
Engine.evaluate(split='validation')
One row per model: the comparison table the whole multi-model idea is for.
Parameters
Name
Type
Description
Default
split
str
Which data to score on.
'validation'
Returns
Name
Type
Description
list[dict[str, Any]]
One row per model: what it is, how long it trained, its loss and its metrics.
list[dict[str, Any]]
The loss column is comparable only between models trained on the same
list[dict[str, Any]]
objective, which is why loss_function sits beside it.
Raises
Name
Type
Description
EngineError
If the split carries no masks - and the message says predict works on it, because that is the question asked next.
The comparison table answers “which model”; this answers “on whom does it fail”, which is the question a clinician asks first and the one a single mean cannot answer. With group_by, each row also carries the group - the patient, usually - so the rows can be summarised per patient rather than per slice.
Parameters
Name
Type
Description
Default
model_name
str | None
Which model, by name. Defaults to the first.
None
split
str
Which data to score on.
'validation'
group_by
str | None
A pattern picking a group out of each case’s name. With it the rows are patients rather than slices: a patient’s slices are pooled into one score, the way a volume would be.
None
Returns
Name
Type
Description
list[dict[str, Any]]
One row per case, or per group. summarise_cases turns them into the
list[dict[str, Any]]
distribution - and its minimum is the number worth reading.
export_weights
Engine.export_weights(model_name, path, **extra)
Write one trained model’s weights, ready to publish or to load again later.
safetensors plus a sidecar recording what the weights are for. Anything passed as extra joins the sidecar, which is where the data they were trained on and its licence belong - a weights file whose provenance is only in somebody’s memory cannot be used by anybody else.
Parameters
Name
Type
Description
Default
model_name
str
Which model’s weights to write.
required
path
str | Path
Where to write. The sidecar goes beside it.
required
**extra
Anything else to record in the sidecar.
{}
Returns
Name
Type
Description
Path
The path written.
fit
Engine.fit(verbose=False)
Train every model the spec asks for, in order.
Parameters
Name
Type
Description
Default
verbose
bool
Print each model’s name and its per-epoch numbers as they arrive.
False
Returns
Name
Type
Description
dict[str, History]
One History per model, by name. The same objects stay on the engine, so
dict[str, History]
evaluate, predict and export_weights work afterwards.
Raises
Name
Type
Description
EngineError
If a split a model needs is missing, or carries no masks while something is about to score against it. check_masks is what refuses a declared class appearing in no mask: a target channel that is zero everywhere has no gradient towards it, so the model is never shown what to find.
A torch DataLoader over one split, wrapping dataset.
Parameters
Name
Type
Description
Default
model
SegmentationModel
Which model’s settings to read - input_shape, batch_size and the augmentation all come from here.
required
split
str
"train", "validation" or "test".
required
augmented
bool
Apply the model’s augmentation. Training only: validation is read the same way every epoch, or two epochs measure different things.
False
shuffle
bool
Shuffle between epochs. Training only, for the same reason.
False
only_images
bool
Read images alone, with no targets.
False
for_loss
bool
Whether these batches are going to the loss. When they are and the loss asks for one, each batch carries a third item: the signed distance map for boundary, or the foreground mask for a regression loss scoring the foreground. Both are computed in the loader’s workers rather than in the loss - the distance transform costs more than an epoch of a small 3D model - and the paths that only score or predict should not pay for either.
True
Returns
Name
Type
Description
A torch.utils.data.DataLoader. Batches hold two items, or three when the
Class probabilities, channels-last, one array per source image.
Tiles are reassembled, so an image that went in at 2048x1536 comes back at 2048x1536 rather than as 24 unrelated pieces.
space decides which grid the answer is on, and the two are different enough that the return type differs with it:
"model" - one stacked array, every prediction on the model’s grid. What training saw, and the only form that can be a single array, since a stack requires one shape.
"source" - a list, one array per sample, each on the grid of the file it came from. What a person asked for when they asked to segment their scan: it can be laid over that scan, written beside it, or measured in millilitres of its voxels. Sources differ in size, so this cannot be stacked, and pretending otherwise by silently resizing is how a mask ends up describing the wrong anatomy.
Parameters
Name
Type
Description
Default
split
str
Which data to predict on.
'test'
model_name
str | None
Which model, by name. Defaults to the first.
None
space
str
"model" returns one stacked array on the network’s own grid. "source" maps each prediction back onto the grid of the scan it came from, undoing the resampling and the crop, and returns a list - scans differ in size, a stacked array needs one shape, and quietly resizing them to match is how a mask ends up describing anatomy it was not computed from.
'model'
Returns
Name
Type
Description
An array for space="model", a list for space="source".
Two losses in source space: interpolation softens a boundary, and where the forward crop cut anatomy away the inverse pads with background - which means not examined rather than nothing there.
(case, prediction) one source image at a time, instead of all of them at once.
predict returns a stacked array, so the whole split has to fit in memory. For a tiled split that is the whole thing at full resolution - 200 FIVES retinas at 2048 is 6.7 GB for the result alone - and tiling exists precisely for images too large to resize, so the stacked form is least usable exactly where splits is most needed. This yields one image, lets the caller keep what it wants, and drops it.
The case name comes with the prediction rather than being left to the caller to line up against samples: predictions arrive in the dataset’s order, and anything that pairs two sequences by position is one drop_last away from scoring the wrong image. group_of turns a case into a group the same way evaluate_cases does.
Parameters
Name
Type
Description
Default
model_name
str | None
Which model, by name. Defaults to the first.
None
split
str
Which data to predict on.
'test'
space
str
As for predict - "model" for the network’s grid, "source" for the grid of the file each prediction came from.
'model'
Returns
Name
Type
Description
Iterator[tuple[str, np.ndarray]]
An iterator of (case, prediction) in the dataset’s order. An iterator rather
Iterator[tuple[str, np.ndarray]]
than a list on purpose: a list would hold the whole split again.