The configuration file
A configuration is one YAML file describing a whole run: where the data is, and one or more models to train on it. It is the same file in both packages - R reads it with platypus_spec(yaml = ...) and Python with from_yaml(), and both hand it to the same pydantic models, which is where every description on this page comes from.
# 2018 Data Science Bowl: find the nuclei.
#
# Two models, one file, one run. This is the whole point of the YAML path - and the
# identical spec can be built from arguments in R and behave the same way.
task: semantic_segmentation
data:
train_path: examples/data/data_science_bowl/stage1_train
validation_path: examples/data/data_science_bowl/stage1_validation
test_path: examples/data/data_science_bowl/stage1_test
mode: nested_dirs
colormap:
- [0, 0, 0] # background
- [255, 255, 255] # nucleus
shuffle: true
output_dir: examples/output
seed: 0
models:
- name: unet
architecture: u_net
input_shape: [160, 160]
channels: 3
blocks: 4
filters: 16
dropout: 0.1
loss:
name: cce_dice
cce_weight: 0.5
metrics:
- name: dice
include_background: false # nuclei are a small part of the picture
- name: iou
include_background: false
optimizer:
name: adam
learning_rate: 0.001
callbacks:
- name: early_stopping
monitor: val_dice
patience: 4
- name: model_checkpoint
path: examples/output/unet.pt
monitor: val_dice
- name: csv_logger
path: examples/output/unet_history.csv
- name: terminate_on_nan
augmentation:
- name: HorizontalFlip
params: {p: 0.5}
- name: VerticalFlip
params: {p: 0.5}
- name: RandomRotate90
params: {p: 0.5}
epochs: 6
batch_size: 8
- name: linknet
architecture: linknet
input_shape: [160, 160]
channels: 3
blocks: 4
filters: 16
dropout: 0.1
loss:
name: focal_tversky
alpha: 0.7 # weight missed nuclei more than false alarms
gamma: 1.3
metrics:
- name: dice
include_background: false
- name: iou
include_background: false
optimizer:
name: adamw
learning_rate: 0.001
callbacks:
- name: early_stopping
monitor: val_dice
patience: 4
- name: model_checkpoint
path: examples/output/linknet.pt
monitor: val_dice
- name: csv_logger
path: examples/output/linknet_history.csv
epochs: 6
batch_size: 8Validation happens before anything is read from disk and before torch is imported, so a mistake here costs a second rather than an epoch. Every refusal names the field.
Schema for your editor
The same definitions are published as JSON Schema, so an editor can complete and check a configuration as you type it. In VS Code with the YAML extension, or any editor using yaml-language-server, put this on the first line:
# yaml-language-server: $schema=https://maju116.github.io/pyplatypus/schema/spec.schema.jsontask, which decides the rest
task is required, and it is the only field that is: it chooses which shape the rest of the file has. Give it wrong and the refusal names the tag rather than reporting every field of the other task as unexpected.
task |
what it describes |
|---|---|
semantic_segmentation |
SegmentationSpec |
instance_segmentation |
InstanceSpec |
object_detection |
DetectionSpec |
task: semantic_segmentation
The SegmentationSpec shape. Its top level:
task-
text —
semantic_segmentationFixed for this specification:semantic_segmentation. seed-
a whole number — unset
Makes a run repeatable as far as the arithmetic is deterministic, which goes further on some hardware than others - so it is worth knowing where it stops.
Repeatable: the split (
split_datasetgives the same partition on any machine), a detector’s fitted anchors, the order the data arrives in and its augmentation - including inside the loader’s worker processes - and, on the processor, every number to twelve decimal places. Not repeatable: a run on a CUDA device. Measured on a GTX 1070: two runs at one seed agreed to twelve decimals on the processor and differed in the fourth after a single epoch on the card, because cuDNN chooses algorithms this does not constrain. Nor across versions of this package - anything that changes the order data is drawn in changes what one seed draws, and0.8.0a3did exactly that. So it is the field for comparing two of your own runs, and not for publishing a number somebody else can land on. deterministic-
trueorfalse—falseAsk torch for deterministic algorithms as well, which is the only thing that makes a run on a card repeatable -seedalone does not, and the field above says so. It is an opt-in because the cost is real and arrives late. Some convolutions become much slower, and an operation that has no deterministic implementation raises when it is reached - so a run can stop ten minutes in rather than when the specification was checked. Every other refusal here happens before anything is read; this one cannot, because whether an operation has a deterministic algorithm is a fact about the installed torch and the device, not about the configuration. Needsseed: constraining the algorithms while the draw still varies produces a run that is no more repeatable and only slower, so asking for it without a seed is refused rather than quietly accepted. output_dir-
text —
platypus_outputWhere a run record is written, and wherefit()puts anything it is asked to save. Nothing is written unless something asks to write: naming this does not by itself create a directory. data-
a
SegmentationDatablock — required Where the images and their masks are, and how to read them. models-
a list of
SegmentationModelblocks — required One or more models, trained in turn against the same data. Names must be unique: a run is recorded under its model’s name, so two models called the same thing would overwrite one record and report it as two. Several entries of the same architecture with different settings is the ordinary case - that is how a comparison is written.
data: for semantic segmentation
train_path-
text — required
Where the training data is. For
nested_dirs, a directory of sample directories; forconfig_file, a CSV naming the files. With asplit, this is the only path given and the divisions are cut from it. validation_path-
text — unset
Optional only in the sense that
splitis the other way to get one, andvalidation: falsethe third. Exactly one of them, because two sources for the validation set are two places to change it. test_path-
text — unset
A third set, scored only when asked. Whether it can be scored at all depends on whether it carries labels: a folder of images alone is accepted and
predictworks on it, whileevaluaterefuses it by name rather than failing two layers down on the first missing mask. validation-
trueorfalse—trueWhether this run validates at all.falseis the third and last way of answering the questionvalidation_pathandsplitanswer, and it has to be written down. A final fit on every case you have, once the hyperparameters are settled, is a legitimate thing to want - and until now it meant inventing a split and ignoring the number it produced. What is not legitimate is arriving here by omission: a specification with neither a path nor a split is still an error, because ‘I have no validation set’ and ‘I forgot’ look identical and only one of them is a decision. Withfalse, givingvalidation_pathorsplitas well is refused: they say where the validation set comes from and this says there is not one. split-
a
SplitSpecblock — unset Dividetrain_pathinstead of naming a second folder. Nothing is written: the samples that were already discovered are partitioned, so a specification stays a description and leaves no files behind.split_dataset()is the tool when the CSVs are the point - to keep, to hand to a colleague, or to cite. mode-
nested_dirsorconfig_file—nested_dirsHow the files are arranged:nested_dirsfor one directory per sample,config_filefor a CSV whose columns name the files. The CSV form is what to use when the data cannot be moved or copied into a layout. subdirs-
a list —
["images", "masks"]Fornested_dirs, the two subdirectories of each sample directory: the images and their masks. A sample may hold several mask files - one per object, as Data Science Bowl does - and they are united into one mask when read. column_sep-
text —
;Forconfig_file, what separates several paths inside one cell - a sample with one mask file per object, as Data Science Bowl has, or one file per channel. shuffle-
trueorfalse—trueShuffle the training data between epochs. Only the training data: validation is read in order so that two epochs measure the same thing in the same way. window-
text or a list —
autoHow values in real units are mapped to 0-1: a named window such as ‘lung’ or ‘soft_tissue’, an explicit (centre, width) pair, ‘auto’ for the window the file recorded - DICOM only, since NIfTI stores none - or ‘full’ for the whole range present. A fixed window is what makes two scans comparable: scaling each one by its own extremes lets a single bright voxel rescale everything else. Ignored for ordinary pictures, which are already 0-255. Accepted asdicom_windowtoo, the name it had while DICOM was the only format that needed it. colormap- a list of lists — unset One colour per class; index in this list is the class index.
labels-
a list of whole numbers — unset
For masks stored as label maps rather than pictures: the voxel value of each class, in class order. This is how volumes label anything - NIfTI holds integers, not colours - and how a single-channel PNG mask can be read too. Exactly one of
colormapandlabelsis given. Include 0 unless the masks really do fill every voxel: a value matching no entry falls back to the first class, so a list without 0 trains the background as whatever that first class is, andfitrefuses the combination rather than let it look plausible. regions-
a list of
Regionblocks — unset Overlapping classes, named as unions of a label map’s values - the third way of saying what a mask’s classes are, and the only one that lets a voxel belong to more than one. Each entry gives anameand the values it is madeof. Withregionsthe model’s channels are independent binary questions: a sigmoid per channel rather than one softmax across them, and no background entry, for the same reason detection’sclasseshas none. Exactly one ofcolormap,labelsandregionsis given. Two regions are the minimum - one region is ordinary binary segmentation, whichlabels: [0, 1]already says. classes_from-
a list of text values — unset
For masks kept as one binary file per class - the fourth way of saying what a mask’s classes are, and the only one that can express classes which overlap independently: a vessel crossing an organ, or two annotators’ masks of the same structure. One pattern per class, in class order, each a regular expression matched against a sample’s mask file names, and every pattern must match exactly one file - the rules
channels_fromuses, through the same matcher. Stated rather than inferred for the reasonchannels_fromgives, with worse consequences: sorting the names is reproducible and anatomically meaningless, and a model trained with ‘tumour’ in channel 1 and used where channel 1 is ‘oedema’ answers plausibly, in range, and nothing downstream can tell. Likeregionsthis makes every channel an independent binary question - a sigmoid per channel rather than one softmax across them - and has no background entry. The difference between the two is the file on disk and nothing else:regionsderives overlap from one label map’s values, which is what BraTS and the liver-and-tumour family need, and this reads overlap that was drawn separately in the first place. Exactly one ofcolormap,labels,regionsandclasses_fromis given. channels_from- a list of text values — unset For datasets that keep one channel per file - BraTS ships T1, T1ce, T2 and FLAIR per patient; 38-Cloud keeps its bands apart - one pattern per channel, in channel order, each a regular expression matched against the file names. Every pattern must match exactly one of a sample’s files. Stated rather than inferred because sorting gives flair, t1, t1ce, t2: reproducible, and anatomically meaningless. A model trained with FLAIR in channel one and used on data where channel one is T1 returns a plausible answer and no error.
target_spacing- a list — unset Resample every volume to this many millimetres per voxel before training, then centre-crop or pad to the model’s input_shape. Without it a volume is simply resized to input_shape, which gives two scans of the same anatomy different physical scale when they were acquired at different slice thicknesses - and the network has no way to know. Volumes only; ignored for 2D.
split: inside semantic segmentation data:
fractions- a list of numbers — required Two or three: train, validation, and optionally test. The length rule is enforced by the splitting module rather than restated here - one rule, one place. A third fraction is scoreable, because it comes from annotated data.
group_by-
text — required
Required, and may be null. A regular expression read against the sample key; everything sharing a group lands in exactly one split. Usually a patient, sometimes a study, a scanner or a site - whatever must not straddle the division.
Required rather than optional because the mistake it prevents is invisible. Slices of one patient in training and validation at once make validation measure memory instead of generalisation, and Dice comes out several points too high with nothing in the output to say so. An omitted field would be that choice made silently;
nullis the same choice made on purpose, which is all this asks for. seed-
a whole number —
0The split is deterministic given the samples, the fractions, the pattern and this. A split that moves between runs makes two results incomparable and the reason for a difference impossible to find.
regions: inside semantic segmentation data:
name-
text — required
What this region is called. It names a channel of the output, so it is what a per-class score is reported under and what
save_class_masksputs in a filename. Distinct from every other region’s name. of-
a list of whole numbers — required
The label-map values belonging to this region, in any order. A voxel is in the region when its value is one of these, so regions may overlap - that is the point - and a single value is normal: BraTS’s enhancing tumour is
of: [4]. Disjoint regions are allowed and mean something useful, grouping several labels into one class and dropping the background channel; they are not a mistake and are not refused.
models: for semantic segmentation
name- text — required Unique within a spec; names the outputs.
input_shape- a list of whole numbers — required Spatial size: (height, width) in 2D, (depth, height, width) in 3D.
channels-
a whole number —
3How many channels the network takes: 3 for colour, 1 for greyscale, and one per sequence for multi-modal data such as the four MRI sequences in BraTS. Derived rather than declared whenchannels_fromnames the files, because then the data decides. Not adopted from a weights file - the data pipeline needs it before a network exists, and a specification has to be checkable offline. optimizer-
one of
adadelta,adagrad,adam,adamax,adamw,nadam,rmsprop,sgd— optional How the weights are updated.nameselects which one and decides which other keys this block accepts. callbacks-
a list, each one of
cosine_annealing,csv_logger,early_stopping,model_checkpoint,reduce_lr_on_plateau,swa,terminate_on_nan— optional Things that happen between epochs: stopping early, writing checkpoints, moving the learning rate, averaging the weights. A callback watching a number no metric will produce is refused when the specification is built rather than waited on forever, and so is one watchingval_*in a run with no validation set. augmentation-
a list of
AugmentationStepblocks — unset albumentations transforms, applied to the training data only - never to validation, where the point is to measure the same thing every epoch. Each step is probed at the model’s own input size while the pipeline is built, because support for volumes is uneven and a transform that cannot take one raises from inside the library. epochs-
a whole number —
10How many passes over the training data. The default is deliberately small: it is enough to see that a run works and not enough to mistake for a result. Withcosine_annealingthis is also the length the schedule is spread over. batch_size-
a whole number —
8How many samples go through the network at once. Limited by memory rather than by the problem, and it is the first thing to lower when a 3D run will not fit. It interacts withbatch_normalization: statistics taken over very few samples are noisy. weights- text — unset Registry name (e.g. ‘dsbowl-unet’) or a path to a local checkpoint.
fit-
trueorfalse—trueSet false to load weights and skip training. architecture-
u_netoru_net_plus_plusorres_u_netorlinknet—u_netWhich U-shaped network. Measured on Data Science Bowl, one split, 60 epochs, the four span 0.9187 to 0.9217 of Dice - a spread of 0.0030, against 0.054 between images within any one of them, and 0.0012 from re-running one of them with another seed. On that problem the architecture is not where the result comes from. Adopted from the weights file whenweightsnames one. blocks-
a whole number —
4How many times the resolution is halved, so how much context the deepest layer sees.input_shapemust divide by 2^blocks. Adopted from the weights file whenweightsis given and this is not; with anencoder, the backbone’s own depth is the ceiling. filters-
a whole number —
16Filters in the first block, doubled at every level, so this sets the model’s size more than anything else does. Adopted from the weights file whenweightsis given and this is not. block_width-
a whole number —
2Convolutions per block, at every level. 2 is what every U-Net paper uses and what the published weights were trained with; raising it adds depth without adding levels, which is the lever to reach for when the objects are small enough that halving the resolution again would lose them. dropout-
a number —
0.0Drop this fraction during training. 0 switches it off, which is the default because these models are small enough that batch normalisation usually carries the regularisation on its own. batch_normalization-
trueorfalse—trueNormalise between convolutions. On by default, and worth knowing about when weights are averaged:swahas to recompute these statistics over the training data, because an averaged weight tensor inherits them from whichever epoch was last rather than averaging them. separable_conv-
trueorfalse—falseDepthwise-separable convolutions: far fewer parameters for the same shape, and a little slower to converge. spatial_dropout-
trueorfalse—trueDrop whole feature maps rather than individual activations. The right kind for images, because neighbouring pixels in one map are correlated and dropping them one at a time removes less than it appears to. Has no effect unlessdropoutis above 0. upsample-
trueorfalse—falseUpsample+conv instead of transposed convolution. deep_supervision-
trueorfalse—falseTrain every decoder depth rather than only the last, each at input resolution.u_net_plus_plusonly, since it is the architecture with intermediate outputs to supervise. activation-
reluorleaky_reluoreluorseluorgeluorsiluortanh—reluThe non-linearity between convolutions, the same one throughout. initialiser-
he_normalorhe_uniformorglorot_normalorglorot_uniform—he_normalHow the weights start. Thehe_*family is scaled for ReLU-like activations andglorot_*for symmetric ones, so this andactivationare a pair. loss-
one of
boundary,cce,cce_dice,combo,dice,focal,focal_tversky,generalized_dice,iou,l1,lovasz,mse,smooth_l1,tversky— optional The objective trained against. Thelosscolumn of a score table is comparable only between models trained on the same one - a Focal-Tversky of 0.05 is not better than a cross-entropy of 0.14, it is not the same question - which is why the table carries the loss’s name beside it. metrics-
a list, each one of
cldice,dice,iou,surface_dice,tversky— optional What to report beside the loss, computed on the mask rather than on the objective, so these stay comparable across models. A callback watchingval_<metric>is refused when nothing here will produce it. splits- a list of whole numbers — unset Cut each image into a grid of tiles instead of shrinking it. One entry per spatial dimension, e.g. [2, 3] cuts into 2 rows by 3 columns. The source is read at splits * input_shape and divided; every tile is input_shape. Leave unset to resize the whole image instead. Training then shuffles in windows rather than over every tile, so one decode serves all of a sample’s tiles: measured on 2048-pixel retinas cut sixteen ways, the loader costs 254 ms a tile shuffled wholesale against 49 ms this way, and a batch still draws from several sources.
encoder- text — unset A timm backbone to use as the contracting path, e.g. ‘resnet34’. Leave unset for the built-in encoder, which is the only one that works in 3D.
pretrained-
trueorfalse—falseLoad the encoder’s ImageNet weights. Separate fromencoderon purpose: naming a backbone builds that architecture, and this is the flag that reaches the network, which matters where there is no network. encoder_learning_rate- a number — unset A separate, usually smaller learning rate for the layers that arrived pretrained. The full-resolution stage added in front of them is ours and starts random, so it trains at the optimizer’s own rate. Unset means one rate for the whole network.
freeze_encoder-
a whole number —
0Keep the pretrained layers fixed for this many epochs, then train them. Lets the random decoder settle before its gradients reach weights worth keeping. A number at or aboveepochsfreezes them for the whole run.
augmentation: inside semantic segmentation models:
name-
text — required
An albumentations transform, by its own class name -
HorizontalFlip,RandomBrightnessContrast. Checked against what the installed albumentations offers, andavailable_transforms()lists them; at rank 3 the list is shorter, because support for volumes is uneven. params-
a mapping — optional
Arguments passed to that transform, exactly as albumentations names them - so
pis the probability of applying it, defaulting to the transform’s own. Not validated here: they are albumentations’ to accept, and each step is probed at the model’s input size when the pipeline is built, so a bad argument is named there rather than guessed at here.
task: instance_segmentation
The InstanceSpec shape. Its top level:
task-
text —
instance_segmentationFixed for this specification:instance_segmentation. seed-
a whole number — unset
Makes a run repeatable as far as the arithmetic is deterministic, which goes further on some hardware than others - so it is worth knowing where it stops.
Repeatable: the split (
split_datasetgives the same partition on any machine), a detector’s fitted anchors, the order the data arrives in and its augmentation - including inside the loader’s worker processes - and, on the processor, every number to twelve decimal places. Not repeatable: a run on a CUDA device. Measured on a GTX 1070: two runs at one seed agreed to twelve decimals on the processor and differed in the fourth after a single epoch on the card, because cuDNN chooses algorithms this does not constrain. Nor across versions of this package - anything that changes the order data is drawn in changes what one seed draws, and0.8.0a3did exactly that. So it is the field for comparing two of your own runs, and not for publishing a number somebody else can land on. deterministic-
trueorfalse—falseAsk torch for deterministic algorithms as well, which is the only thing that makes a run on a card repeatable -seedalone does not, and the field above says so. It is an opt-in because the cost is real and arrives late. Some convolutions become much slower, and an operation that has no deterministic implementation raises when it is reached - so a run can stop ten minutes in rather than when the specification was checked. Every other refusal here happens before anything is read; this one cannot, because whether an operation has a deterministic algorithm is a fact about the installed torch and the device, not about the configuration. Needsseed: constraining the algorithms while the draw still varies produces a run that is no more repeatable and only slower, so asking for it without a seed is refused rather than quietly accepted. output_dir-
text —
platypus_outputWhere a run record is written, and wherefit()puts anything it is asked to save. Nothing is written unless something asks to write: naming this does not by itself create a directory. data-
a
InstanceDatablock — required Where the images and their per-object masks are, and how to read them. models-
a list of
InstanceModelblocks — required One or more models, trained in turn against the same data. Names must be unique, for the same reason as in a segmentation specification. Several entries differing only inseparationis the comparison this task exists to make easy - anddecodewidens the table again without a second run.
data: for instance segmentation
train_path-
text — required
Where the training data is. For
nested_dirs, a directory of sample directories; forconfig_file, a CSV naming the files. With asplit, this is the only path given and the divisions are cut from it. validation_path-
text — unset
Optional only in the sense that
splitis the other way to get one, andvalidation: falsethe third. Exactly one of them, because two sources for the validation set are two places to change it. test_path-
text — unset
A third set, scored only when asked. Whether it can be scored at all depends on whether it carries labels: a folder of images alone is accepted and
predictworks on it, whileevaluaterefuses it by name rather than failing two layers down on the first missing mask. validation-
trueorfalse—trueWhether this run validates at all.falseis the third and last way of answering the questionvalidation_pathandsplitanswer, and it has to be written down. A final fit on every case you have, once the hyperparameters are settled, is a legitimate thing to want - and until now it meant inventing a split and ignoring the number it produced. What is not legitimate is arriving here by omission: a specification with neither a path nor a split is still an error, because ‘I have no validation set’ and ‘I forgot’ look identical and only one of them is a decision. Withfalse, givingvalidation_pathorsplitas well is refused: they say where the validation set comes from and this says there is not one. split-
a
SplitSpecblock — unset Dividetrain_pathinstead of naming a second folder. Nothing is written: the samples that were already discovered are partitioned, so a specification stays a description and leaves no files behind.split_dataset()is the tool when the CSVs are the point - to keep, to hand to a colleague, or to cite. mode-
nested_dirsorconfig_file—nested_dirsHow the files are arranged:nested_dirsfor one directory per sample,config_filefor a CSV whose columns name the files. The CSV form is what to use when the data cannot be moved or copied into a layout. subdirs-
a list —
["images", "masks"]Fornested_dirs, the two subdirectories of each sample directory: the images, and one mask file per object. A case with 375 nuclei has 375 files here, which is the Data Science Bowl’s own layout; they are read as a label map with one value per object rather than united into one mask, which is the whole difference between this task andsemantic_segmentation. column_sep-
text —
;Forconfig_file, what separates several paths inside one cell - a sample with one mask file per object, as Data Science Bowl has, or one file per channel. shuffle-
trueorfalse—trueShuffle the training data between epochs. Only the training data: validation is read in order so that two epochs measure the same thing in the same way. window-
text or a list —
autoHow values in real units are mapped to 0-1: a named window such as ‘lung’ or ‘soft_tissue’, an explicit (centre, width) pair, ‘auto’ for the window the file recorded - DICOM only, since NIfTI stores none - or ‘full’ for the whole range present. A fixed window is what makes two scans comparable: scaling each one by its own extremes lets a single bright voxel rescale everything else. Ignored for ordinary pictures, which are already 0-255. Accepted asdicom_windowtoo, the name it had while DICOM was the only format that needed it.
split: inside instance segmentation data:
fractions- a list of numbers — required Two or three: train, validation, and optionally test. The length rule is enforced by the splitting module rather than restated here - one rule, one place. A third fraction is scoreable, because it comes from annotated data.
group_by-
text — required
Required, and may be null. A regular expression read against the sample key; everything sharing a group lands in exactly one split. Usually a patient, sometimes a study, a scanner or a site - whatever must not straddle the division.
Required rather than optional because the mistake it prevents is invisible. Slices of one patient in training and validation at once make validation measure memory instead of generalisation, and Dice comes out several points too high with nothing in the output to say so. An omitted field would be that choice made silently;
nullis the same choice made on purpose, which is all this asks for. seed-
a whole number —
0The split is deterministic given the samples, the fractions, the pattern and this. A split that moves between runs makes two results incomparable and the reason for a difference impossible to find.
models: for instance segmentation
name- text — required Unique within a spec; names the outputs.
input_shape- a list of whole numbers — required Spatial size: (height, width) in 2D, (depth, height, width) in 3D.
channels-
a whole number —
3How many channels the network takes: 3 for colour, 1 for greyscale, and one per sequence for multi-modal data such as the four MRI sequences in BraTS. Derived rather than declared whenchannels_fromnames the files, because then the data decides. Not adopted from a weights file - the data pipeline needs it before a network exists, and a specification has to be checkable offline. optimizer-
one of
adadelta,adagrad,adam,adamax,adamw,nadam,rmsprop,sgd— optional How the weights are updated.nameselects which one and decides which other keys this block accepts. callbacks-
a list, each one of
cosine_annealing,csv_logger,early_stopping,model_checkpoint,reduce_lr_on_plateau,swa,terminate_on_nan— optional Things that happen between epochs: stopping early, writing checkpoints, moving the learning rate, averaging the weights. A callback watching a number no metric will produce is refused when the specification is built rather than waited on forever, and so is one watchingval_*in a run with no validation set. augmentation-
a list of
AugmentationStepblocks — unset albumentations transforms, applied to the training data only - never to validation, where the point is to measure the same thing every epoch. Each step is probed at the model’s own input size while the pipeline is built, because support for volumes is uneven and a transform that cannot take one raises from inside the library. epochs-
a whole number —
10How many passes over the training data. The default is deliberately small: it is enough to see that a run works and not enough to mistake for a result. Withcosine_annealingthis is also the length the schedule is spread over. batch_size-
a whole number —
8How many samples go through the network at once. Limited by memory rather than by the problem, and it is the first thing to lower when a 3D run will not fit. It interacts withbatch_normalization: statistics taken over very few samples are noisy. weights- text — unset Registry name (e.g. ‘dsbowl-unet’) or a path to a local checkpoint.
fit-
trueorfalse—trueSet false to load weights and skip training. architecture-
u_netoru_net_plus_plusorres_u_netorlinknet—u_netWhich U-shaped network. Measured on Data Science Bowl, one split, 60 epochs, the four span 0.9187 to 0.9217 of Dice - a spread of 0.0030, against 0.054 between images within any one of them, and 0.0012 from re-running one of them with another seed. On that problem the architecture is not where the result comes from. Adopted from the weights file whenweightsnames one. blocks-
a whole number —
4How many times the resolution is halved, so how much context the deepest layer sees.input_shapemust divide by 2^blocks. Adopted from the weights file whenweightsis given and this is not; with anencoder, the backbone’s own depth is the ceiling. filters-
a whole number —
16Filters in the first block, doubled at every level, so this sets the model’s size more than anything else does. Adopted from the weights file whenweightsis given and this is not. block_width-
a whole number —
2Convolutions per block, at every level. 2 is what every U-Net paper uses and what the published weights were trained with; raising it adds depth without adding levels, which is the lever to reach for when the objects are small enough that halving the resolution again would lose them. dropout-
a number —
0.0Drop this fraction during training. 0 switches it off, which is the default because these models are small enough that batch normalisation usually carries the regularisation on its own. batch_normalization-
trueorfalse—trueNormalise between convolutions. On by default, and worth knowing about when weights are averaged:swahas to recompute these statistics over the training data, because an averaged weight tensor inherits them from whichever epoch was last rather than averaging them. separable_conv-
trueorfalse—falseDepthwise-separable convolutions: far fewer parameters for the same shape, and a little slower to converge. spatial_dropout-
trueorfalse—trueDrop whole feature maps rather than individual activations. The right kind for images, because neighbouring pixels in one map are correlated and dropping them one at a time removes less than it appears to. Has no effect unlessdropoutis above 0. upsample-
trueorfalse—falseUpsample+conv instead of transposed convolution. deep_supervision-
trueorfalse—falseTrain every decoder depth rather than only the last, each at input resolution.u_net_plus_plusonly, since it is the architecture with intermediate outputs to supervise. activation-
reluorleaky_reluoreluorseluorgeluorsiluortanh—reluThe non-linearity between convolutions, the same one throughout. initialiser-
he_normalorhe_uniformorglorot_normalorglorot_uniform—he_normalHow the weights start. Thehe_*family is scaled for ReLU-like activations andglorot_*for symmetric ones, so this andactivationare a pair. loss-
one of
boundary,cce,cce_dice,combo,dice,focal,focal_tversky,generalized_dice,iou,l1,lovasz,mse,smooth_l1,tversky— optional The objective trained against. Thelosscolumn of a score table is comparable only between models trained on the same one - a Focal-Tversky of 0.05 is not better than a cross-entropy of 0.14, it is not the same question - which is why the table carries the loss’s name beside it. metrics-
a list, each one of
cldice,dice,iou,surface_dice,tversky— optional What to report beside the loss, computed on the mask rather than on the objective, so these stay comparable across models. A callback watchingval_<metric>is refused when nothing here will produce it. splits- a list of whole numbers — unset Cut each image into a grid of tiles instead of shrinking it. One entry per spatial dimension, e.g. [2, 3] cuts into 2 rows by 3 columns. The source is read at splits * input_shape and divided; every tile is input_shape. Leave unset to resize the whole image instead. Training then shuffles in windows rather than over every tile, so one decode serves all of a sample’s tiles: measured on 2048-pixel retinas cut sixteen ways, the loader costs 254 ms a tile shuffled wholesale against 49 ms this way, and a batch still draws from several sources.
encoder- text — unset A timm backbone to use as the contracting path, e.g. ‘resnet34’. Leave unset for the built-in encoder, which is the only one that works in 3D.
pretrained-
trueorfalse—falseLoad the encoder’s ImageNet weights. Separate fromencoderon purpose: naming a backbone builds that architecture, and this is the flag that reaches the network, which matters where there is no network. encoder_learning_rate- a number — unset A separate, usually smaller learning rate for the layers that arrived pretrained. The full-resolution stage added in front of them is ours and starts random, so it trains at the optimizer’s own rate. Unset means one rate for the whole network.
freeze_encoder-
a whole number —
0Keep the pretrained layers fixed for this many epochs, then train them. Lets the random decoder settle before its gradients reach weights worth keeping. A number at or aboveepochsfreezes them for the whole run. separation-
one of
flows,rim— unset What the network predicts so that one mask can become many objects: a block namedrimorflows, carrying that mechanism’s own settings. Required unlessweightsnames a file that records what it was trained to predict, because it decides the width of the head and which losses are admissible - the two things a model cannot be built without - and because neither value is a safe guess at what somebody meant. A published file is the one place it does not have to be guessed.dsbowl-instancesrecordsrimat width 2, so leaving this out besideweights: dsbowl-instancesadopts a training decision rather than inventing one, and the engine prints what it took. It was required outright until0.8.0a28, which left four of this model’s fields as the file’s own fingerprint with no way to learn them but a model card - andseparationis the one nobody can guess, since a rim’s width is not a shape.pyplatypus#236. A block rather than a bare name because the settings differ between the two and belong inside the one they describe:widthundername: flowsis an unknown key rather than something a validator has to refuse. It is also the field whose documentation carries a result: on the Data Science Bowl the two are worth +0.0640 and +0.0680 of instance average precision over the same mask throughconnected_components, a difference below the noise between two runs of one configuration, while a perfect mask is worth +0.2398. Read the blocks’ own descriptions for what that means before planning a comparison around this axis. decode-
a list, each one of
connected_components,follow_flows,nearest_seed,watershed— unset How the predicted channels become objects. A list, because several decodings share one forward pass: a second decoding of 134 images costs 1.41 s against 16 minutes for a second training run, so this ismetrics:rather thancallbacks:-evaluate()returns one row per (model, decoding). Each entry is a block with anameand that decoding’s own parameters. A decoding theseparationcannot feed -follow_flowswithrim, say - is refused at load time with both halves named, rather than returning a label map of something else. Left unset it is the signal’s own decoding plusconnected_components, which is the pair that makes a row readable: the same model and the same mask with and without the separation signal. Write the list to choose; leave it out and the comparison every one of these runs needs is already there. Withsplits, decoding happens after the tiles are stitched, never per tile - an object crossing a seam would otherwise become two objects, and that is a constraint of the task rather than a setting.
augmentation: inside instance segmentation models:
name-
text — required
An albumentations transform, by its own class name -
HorizontalFlip,RandomBrightnessContrast. Checked against what the installed albumentations offers, andavailable_transforms()lists them; at rank 3 the list is shorter, because support for volumes is uneven. params-
a mapping — optional
Arguments passed to that transform, exactly as albumentations names them - so
pis the probability of applying it, defaulting to the transform’s own. Not validated here: they are albumentations’ to accept, and each step is probed at the model’s input size when the pipeline is built, so a bad argument is named there rather than guessed at here.
task: object_detection
The DetectionSpec shape. Its top level:
task-
text — required
Fixed for this specification:
object_detection. seed-
a whole number — unset
Makes a run repeatable as far as the arithmetic is deterministic, which goes further on some hardware than others - so it is worth knowing where it stops.
Repeatable: the split (
split_datasetgives the same partition on any machine), a detector’s fitted anchors, the order the data arrives in and its augmentation - including inside the loader’s worker processes - and, on the processor, every number to twelve decimal places. Not repeatable: a run on a CUDA device. Measured on a GTX 1070: two runs at one seed agreed to twelve decimals on the processor and differed in the fourth after a single epoch on the card, because cuDNN chooses algorithms this does not constrain. Nor across versions of this package - anything that changes the order data is drawn in changes what one seed draws, and0.8.0a3did exactly that. So it is the field for comparing two of your own runs, and not for publishing a number somebody else can land on. deterministic-
trueorfalse—falseAsk torch for deterministic algorithms as well, which is the only thing that makes a run on a card repeatable -seedalone does not, and the field above says so. It is an opt-in because the cost is real and arrives late. Some convolutions become much slower, and an operation that has no deterministic implementation raises when it is reached - so a run can stop ten minutes in rather than when the specification was checked. Every other refusal here happens before anything is read; this one cannot, because whether an operation has a deterministic algorithm is a fact about the installed torch and the device, not about the configuration. Needsseed: constraining the algorithms while the draw still varies produces a run that is no more repeatable and only slower, so asking for it without a seed is refused rather than quietly accepted. output_dir-
text —
platypus_outputWhere a run record is written, and wherefit()puts anything it is asked to save. Nothing is written unless something asks to write: naming this does not by itself create a directory. data-
a
DetectionDatablock — required Where the images and their annotations are, and how to read them. models-
a list of
DetectionModelblocks — required One or more detectors, trained in turn against the same data. Names must be unique, for the same reason as in a segmentation specification.
data: for object detection
train_path-
text — required
Where the training data is. For
nested_dirs, a directory of sample directories; forconfig_file, a CSV naming the files. With asplit, this is the only path given and the divisions are cut from it. validation_path-
text — unset
Optional only in the sense that
splitis the other way to get one, andvalidation: falsethe third. Exactly one of them, because two sources for the validation set are two places to change it. test_path-
text — unset
A third set, scored only when asked. Whether it can be scored at all depends on whether it carries labels: a folder of images alone is accepted and
predictworks on it, whileevaluaterefuses it by name rather than failing two layers down on the first missing mask. validation-
trueorfalse—trueWhether this run validates at all.falseis the third and last way of answering the questionvalidation_pathandsplitanswer, and it has to be written down. A final fit on every case you have, once the hyperparameters are settled, is a legitimate thing to want - and until now it meant inventing a split and ignoring the number it produced. What is not legitimate is arriving here by omission: a specification with neither a path nor a split is still an error, because ‘I have no validation set’ and ‘I forgot’ look identical and only one of them is a decision. Withfalse, givingvalidation_pathorsplitas well is refused: they say where the validation set comes from and this says there is not one. split-
a
SplitSpecblock — unset Dividetrain_pathinstead of naming a second folder. Nothing is written: the samples that were already discovered are partitioned, so a specification stays a description and leaves no files behind.split_dataset()is the tool when the CSVs are the point - to keep, to hand to a colleague, or to cite. mode-
nested_dirsorconfig_file—nested_dirsHow the files are arranged:nested_dirsfor one directory per sample,config_filefor a CSV whose columns name the files. The CSV form is what to use when the data cannot be moved or copied into a layout. subdirs-
a list —
["images", "annotations"]Fornested_dirs, the two subdirectories of each sample directory: the images and the files holding their boxes. Which format those files are in is said byannotation_format, never guessed from the extension. column_sep-
text —
;Forconfig_file, what separates several paths inside one cell - a sample with one mask file per object, as Data Science Bowl has, or one file per channel. shuffle-
trueorfalse—trueShuffle the training data between epochs. Only the training data: validation is read in order so that two epochs measure the same thing in the same way. window-
text or a list —
autoHow values in real units are mapped to 0-1: a named window such as ‘lung’ or ‘soft_tissue’, an explicit (centre, width) pair, ‘auto’ for the window the file recorded - DICOM only, since NIfTI stores none - or ‘full’ for the whole range present. A fixed window is what makes two scans comparable: scaling each one by its own extremes lets a single bright voxel rescale everything else. Ignored for ordinary pictures, which are already 0-255. Accepted asdicom_windowtoo, the name it had while DICOM was the only format that needed it. classes- a list of text values — required Class names in class order. Position is the class index. No background entry - an image region with no object is not a box.
annotation_format-
pascal_vocorlabelme—pascal_vocPascal VOC XML, the format BCCD and most detection datasets ship, or LabelMe JSON. Both readers are inpyplatypus.detection.annotations. coordinates-
vocorzero_based— unset How to read a Pascal VOC box. The format’s own convention is 1-based and inclusive of both endpoints, so a 10-pixel-wide box runs xmin=1 xmax=10 and the width isxmax - xmin + 1. Plenty of tools write the same files 0-based and exclusive, where that box is xmin=0 xmax=10. Reading one as the other shifts every box by a pixel and shrinks it by one, which is invisible at a glance and costs real IoU on small objects. Unset means ‘voc’.describe_annotationsreports the evidence: a minimum coordinate of 0 proves the file is 0-based, since a 1-based index cannot be zero. strict_labels-
trueorfalse—trueRefuse an object whose class is not inclasses. False skips it instead, which is how you train on two of a dataset’s three classes. True by default: an unexpected label is more often a typo inclassesthan an intention.
split: inside object detection data:
fractions- a list of numbers — required Two or three: train, validation, and optionally test. The length rule is enforced by the splitting module rather than restated here - one rule, one place. A third fraction is scoreable, because it comes from annotated data.
group_by-
text — required
Required, and may be null. A regular expression read against the sample key; everything sharing a group lands in exactly one split. Usually a patient, sometimes a study, a scanner or a site - whatever must not straddle the division.
Required rather than optional because the mistake it prevents is invisible. Slices of one patient in training and validation at once make validation measure memory instead of generalisation, and Dice comes out several points too high with nothing in the output to say so. An omitted field would be that choice made silently;
nullis the same choice made on purpose, which is all this asks for. seed-
a whole number —
0The split is deterministic given the samples, the fractions, the pattern and this. A split that moves between runs makes two results incomparable and the reason for a difference impossible to find.
models: for object detection
name- text — required Unique within a spec; names the outputs.
input_shape- a list of whole numbers — required Spatial size: (height, width) in 2D, (depth, height, width) in 3D.
channels-
a whole number —
3How many channels the network takes: 3 for colour, 1 for greyscale, and one per sequence for multi-modal data such as the four MRI sequences in BraTS. Derived rather than declared whenchannels_fromnames the files, because then the data decides. Not adopted from a weights file - the data pipeline needs it before a network exists, and a specification has to be checkable offline. optimizer-
one of
adadelta,adagrad,adam,adamax,adamw,nadam,rmsprop,sgd— optional How the weights are updated.nameselects which one and decides which other keys this block accepts. callbacks-
a list, each one of
cosine_annealing,csv_logger,early_stopping,model_checkpoint,reduce_lr_on_plateau,swa,terminate_on_nan— optional Things that happen between epochs: stopping early, writing checkpoints, moving the learning rate, averaging the weights. A callback watching a number no metric will produce is refused when the specification is built rather than waited on forever, and so is one watchingval_*in a run with no validation set. augmentation-
a list of
AugmentationStepblocks — unset albumentations transforms, applied to the training data only - never to validation, where the point is to measure the same thing every epoch. Each step is probed at the model’s own input size while the pipeline is built, because support for volumes is uneven and a transform that cannot take one raises from inside the library. epochs-
a whole number —
10How many passes over the training data. The default is deliberately small: it is enough to see that a run works and not enough to mistake for a result. Withcosine_annealingthis is also the length the schedule is spread over. batch_size-
a whole number —
8How many samples go through the network at once. Limited by memory rather than by the problem, and it is the first thing to lower when a 3D run will not fit. It interacts withbatch_normalization: statistics taken over very few samples are noisy. weights- text — unset Registry name (e.g. ‘dsbowl-unet’) or a path to a local checkpoint.
fit-
trueorfalse—trueSet false to load weights and skip training. architecture-
yolo3—yolo3Which detector. YOLOv3 is the only one, and it is 2D: boxes in a volume are a different problem with different annotations, so a detection specification at rank 3 is refused while the specification is read rather than failing later. Adopted from the weights file whenweightsnames one, together with the anchors - weights mean nothing without the anchors they were trained with, so a specification naming both is refused. anchors- a list of a list of lists — unset The box shapes the model predicts offsets from, as fractions of the input size, one group per grid from coarsest to finest. Unset fits them to the training annotations with k-means under an IoU distance, which is what you want: measured on BCCD’s 2,805 training boxes, COCO’s anchors cover them at a mean IoU of 0.65 against 0.88 for anchors fitted to them. Fitted anchors are recorded with the run, since a detector cannot be reloaded without them.
anchors_per_grid-
a whole number —
3How many anchors to fit per grid, whenanchorsis not given. It also sets the head’s width, which is anchors_per_grid * (n_class + 5). ignore_threshold-
a number —
0.5A cell that is not responsible for an object but whose box overlaps one this much is left unsupervised rather than trained towards ‘no object’. Without it a nearly-correct guess beside the responsible cell is punished, which is the one thing a detector should not learn. box_loss-
offsetsorgiou—offsetsHow the box coordinates are scored.offsetsis YOLOv3’s own: cross-entropy on the two cell offsets and squared error on the two log sizes, each weighted by2 - w*hso a small object is not worth less than a large one.giouscores the decoded box against the true box directly, as1 - GIoU. The reason to prefergiouis that it can reach zero. Cross-entropy against a soft target bottoms out at that target’s entropy, so theoffsetsterm is still 4.21 on a prediction that is exactly right - measured on three boxes - and a converged run cannot be told from a stalled one by reading it. The same prediction scores 0.000000 undergiou. It is not the better objective here, and that is measured. BCCD, three seeds each, 150 epochs, its own test split:
Average precision cannot separate them in either direction - both gaps are inside the noise. How well the matched boxes fit can, andmAP@0.5 mAP@COCO matched IoU giou 0.8666 +-0.0147 0.5078 +-0.0083 0.7929 +-0.0069 offsets 0.8583 +-0.0167 0.5172 +-0.0195 0.8038 +-0.0001giouis worse by 0.0109, which is 2.7 standard errors of the difference. It is also far less repeatable on exactly the quantity it targets:offsetsgave 0.803853, 0.803656 and 0.803861 across its seeds, sixty times tighter thangiou. The reason is in the same run’s own numbers. GIoU exists because IoU is a flat zero for boxes that do not touch, so it has no gradient where a detector is most wrong - but anchors fitted by k-means already cover BCCD’s truths at a mean IoU of 0.877, so predictions never start disjoint and the advantage never arrives while the cost does. Where fitted anchors cover poorly it should be a different story, which is untested here and therefore not claimed. So choosegiouto make the objective readable, not to raise the score: at 150 epochs its coordinate term sits at 0.05 on the training split against 0.47 on validation, which is a localisation gap you can see, whileoffsetsreads 5.92 and 6.85 against a floor of 5.891 and tells you nothing. On BCCD that legibility costs about 0.011 of matched IoU. score_threshold-
a number —
0.01Discard a predicted box below this confidence. Low on purpose: average precision is computed over the whole ranking, so a cut here is a ceiling on recall that no threshold chosen later can lift. nms_threshold-
a number —
0.45Two boxes of the same class overlapping this much are one object, and the lower-scoring one is dropped. Per class, so a platelet sitting on a red cell survives. operating_point-
a number —
0.5The confidence at which precision and recall are reported. Separate fromscore_thresholdbecause they answer different questions: that one decides what is computed at all, this one decides where on the curve to stand. min_visibility-
a number —
0.25How much of a box must survive an augmentation that removes part of the frame, as a fraction of its original area, for the box to be kept. A convention rather than a measurement; what is defensible is the direction. A box keeping two pixels of a cell teaches the model that a two-pixel fragment is a whole cell, which produces false positives everywhere, while dropping a heavily truncated object only fails to teach it about that object. Pascal VOC marks such objectstruncatedfor the same reason. Irrelevant unless a transform can lose part of the frame: flips and rotations never do.
augmentation: inside object detection models:
name-
text — required
An albumentations transform, by its own class name -
HorizontalFlip,RandomBrightnessContrast. Checked against what the installed albumentations offers, andavailable_transforms()lists them; at rank 3 the list is shorter, because support for volumes is uneven. params-
a mapping — optional
Arguments passed to that transform, exactly as albumentations names them - so
pis the probability of applying it, defaulting to the transform’s own. Not validated here: they are albumentations’ to accept, and each step is probed at the model’s input size when the pipeline is built, so a bad argument is named there rather than guessed at here.
Components
Each of these is named by its name: and carries its own fields.
Losses
boundary
A region loss plus a term that knows how far a wrong voxel is from the truth.
name-
text —
boundaryWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. region-
one of
cce,cce_dice,combo,dice,focal,focal_tversky,generalized_dice,iou,lovasz,tversky— optional The overlap term this is added to. Dice unless you say otherwise. alpha-
a number —
0.9How much of the objective is the region term,1 - alphathe surface term. 0.9 because 0.5 was measured and is unusable. Three seeds on lesions with ambiguous edges, 60 epochs:
At 0.5 the surface term has half the objective while the prediction is still random, which is where it is known to be unstable - the seed spread is six times the baseline’s and every number is worse. Kervadec schedules this downwards from near 1 rather than fixing it; 0.9 is the closest a single number comes, and the spec deliberately has no schedule to hide behind. Excludes both ends: at 1 the surface term is absent and the name is a lie, and at 0 nothing measures how much of the object was found.Dice volume bias volume |error| dice alone 0.9525 ±0.0027 -5.18% ±3.11% 16.90% ±0.90% boundary a=0.5 0.9164 ±0.0240 +32.22% ±28.28% 42.95% ±19.12% boundary a=0.9 0.9411 ±0.0093 -0.06% ±4.15% 22.17% ±2.88%
cce
Per-pixel classification, with no notion of the object.
name-
text —
cceWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. label_smoothing-
a number —
0.0Train towards 1 - this value instead of towards 1, so the model is not pushed to be certain. Worth having where the labels themselves are uncertain, which on a hand-drawn boundary they are.
cce_dice
The blend to start a segmentation run with.
name-
text —
cce_diceWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. cce_weight-
a number —
0.5How much of the objective is cross-entropy; the rest is Dice. Cross-entropy cares about every pixel and Dice about the overlap, so raising this helps a class that covers very little of the image and lowering it favours the shape. smooth-
a number —
1.0Added to both numerator and denominator so a class absent from an image gives a finite number instead of 0/0. Leave it alone unless you know why you are moving it: raising it makes an empty class look better than it is.
combo
A lopsided cross-entropy blended with Dice.
name-
text —
comboWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. alpha-
a number —
0.5How much of the objective is the weighted cross-entropy term; the rest is Dice. ce_ratio-
a number —
0.5Within the cross-entropy term, how much weight goes on the positive class. This is the key that makescomboworth choosing overcce_dice: it lets the two halves of the objective disagree about which class matters.
dice
Overlap, and nothing about where the error sits.
name-
text —
diceWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. smooth-
a number —
1.0Added to both numerator and denominator so a class absent from an image gives a finite number instead of 0/0. Leave it alone unless you know why you are moving it: raising it makes an empty class look better than it is.
focal
Cross-entropy that loses interest in the voxels it already gets right.
name-
text —
focalWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. gamma-
a number —
2.0How hard to discount the pixels already classified well, so the gradient comes from the ones that are not. 0 is plain cross-entropy; 2 is the usual value. alpha- a number — unset Weight on the positive class, between 0 and 1. Unset means no weighting, which is not the same as 0.5: at 0.5 both classes are halved and the gradients are smaller, which looks like a learning-rate change.
focal_tversky
Tversky, with the same loss of interest in what is already right.
name-
text —
focal_tverskyWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. alpha-
a number —
0.5Weight on false negatives, as intversky, with false positives taking 1 - alpha. gamma-
a number —
1.0Exponent on the Tversky index, which is where this differs fromtversky: above 1 it concentrates the gradient on the cases scoring badly. Note 1 is the default and means plain Tversky, not ‘off’. smooth-
a number —
1.0Added to both numerator and denominator so a class absent from an image gives a finite number instead of 0/0. Leave it alone unless you know why you are moving it: raising it makes an empty class look better than it is.
generalized_dice
Dice with every class weighted by the inverse square of how much of the picture it occupies, so a rare class counts for more than an equal share rather than for less.
name-
text —
generalized_diceWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. smooth-
a number —
1.0Added to both numerator and denominator so a class absent from an image gives a finite number instead of 0/0. Leave it alone unless you know why you are moving it: raising it makes an empty class look better than it is.
iou
Dice’s harsher twin: the same ordering, a lower number.
name-
text —
iouWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. smooth-
a number —
1.0Added to both numerator and denominator so a class absent from an image gives a finite number instead of 0/0. Leave it alone unless you know why you are moving it: raising it makes an empty class look better than it is.
l1
Absolute error on a continuous target.
where-
foregroundoreverywhere—foregroundWhere the target means anything, and so where the error is averaged. A continuous instance target is defined on the objects and undefined off them - there is no direction towards the centre for a pixel in no object - so the builder writes zero on the background. Measured on the 536 Data Science Bowl cases: the foreground is 13.97% of the pixels at 256x256 (13.48% at native size, 0.03% to 59.2% per case, 167 cases under 5%), soeverywherespends 86% of the objective on a value a single output bias satisfies exactly.foregrounddivides by the mask’s own total, which makes the number mean average error per object voxel.everywhereis the other defensible answer and is what CellPose and HoVer-Net’s plain regression terms do - their background zero is a real value rather than a missing one. Both are offered and neither is guessed;foregroundis the default because it is the reading whose number means something.foregroundasks the loader for the mask, which is the same mechanismboundaryuses for its distance map. It is never read off the target: a flow field is exactly zero at every object’s centre, so a mask taken from the non-zero voxels would drop precisely the seeds - and the seeds are what limits this approach (pyplatypus#211). name-
text —
l1Which loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored.
lovasz
Optimise the overlap you are going to report, rather than a proxy for it.
name-
text —
lovaszWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. per_image-
trueorfalse—falseCompute the loss for each image and average, rather than over the whole batch at once. Per image is the honest thing when images differ in how much of them is foreground, because otherwise a large object in one image dominates the batch; it is also noisier.
mse
Squared error on a continuous target.
where-
foregroundoreverywhere—foregroundWhere the target means anything, and so where the error is averaged. A continuous instance target is defined on the objects and undefined off them - there is no direction towards the centre for a pixel in no object - so the builder writes zero on the background. Measured on the 536 Data Science Bowl cases: the foreground is 13.97% of the pixels at 256x256 (13.48% at native size, 0.03% to 59.2% per case, 167 cases under 5%), soeverywherespends 86% of the objective on a value a single output bias satisfies exactly.foregrounddivides by the mask’s own total, which makes the number mean average error per object voxel.everywhereis the other defensible answer and is what CellPose and HoVer-Net’s plain regression terms do - their background zero is a real value rather than a missing one. Both are offered and neither is guessed;foregroundis the default because it is the reading whose number means something.foregroundasks the loader for the mask, which is the same mechanismboundaryuses for its distance map. It is never read off the target: a flow field is exactly zero at every object’s centre, so a mask taken from the non-zero voxels would drop precisely the seeds - and the seeds are what limits this approach (pyplatypus#211). name-
text —
mseWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored.
smooth_l1
mse close to the truth and l1 far from it, switching at beta.
where-
foregroundoreverywhere—foregroundWhere the target means anything, and so where the error is averaged. A continuous instance target is defined on the objects and undefined off them - there is no direction towards the centre for a pixel in no object - so the builder writes zero on the background. Measured on the 536 Data Science Bowl cases: the foreground is 13.97% of the pixels at 256x256 (13.48% at native size, 0.03% to 59.2% per case, 167 cases under 5%), soeverywherespends 86% of the objective on a value a single output bias satisfies exactly.foregrounddivides by the mask’s own total, which makes the number mean average error per object voxel.everywhereis the other defensible answer and is what CellPose and HoVer-Net’s plain regression terms do - their background zero is a real value rather than a missing one. Both are offered and neither is guessed;foregroundis the default because it is the reading whose number means something.foregroundasks the loader for the mask, which is the same mechanismboundaryuses for its distance map. It is never read off the target: a flow field is exactly zero at every object’s centre, so a mask taken from the non-zero voxels would drop precisely the seeds - and the seeds are what limits this approach (pyplatypus#211). name-
text —
smooth_l1Which loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. beta-
a number —
1.0Where the quadratic part ends and the linear one begins, in the target’s own units. For a flow field scaled to roughly [-1, 1] the default makes the whole range quadratic, so lower it to get any of the robustness this loss is for.
tversky
Dice with a thumb on the scale: miss less, or over-segment less.
name-
text —
tverskyWhich loss, and it decides which other keys this block accepts:tverskytakesalpha,focaltakesgamma, and giving a key the chosen loss does not have is refused by name rather than ignored. alpha-
a number —
0.5Weight on false negatives; raising it buys recall at the cost of precision, which is the trade a screening task usually wants. The weight on false positives is 1 - alpha and is not a separate key, so the two cannot disagree. At alpha 0.5 this is Dice exactly, but only whensmoothis 0. smooth-
a number —
1.0Added to both numerator and denominator so a class absent from an image gives a finite number instead of 0/0. Leave it alone unless you know why you are moving it: raising it makes an empty class look better than it is.
Metrics
cldice
Whether the structure is connected, rather than whether the voxels coincide.
smooth-
a number —
1.0Zero is allowed here, unlike in the losses. Smoothing inflates a reported score: a class absent from an image scores a perfect 1.0 with smooth>0. Use 0 for an honest number, at the cost of NaN on absent classes. include_background-
trueorfalse—falseFalse here where the other metrics default to True, and not for the usual reason. The background’s skeleton lies inside the background by construction, so that class scores 1.0 whatever the model did: measured on a 3-pixel vessel, averaging it in moved 0.7865 to 0.8933. It is not a flattering average, it is a number with no meaning in it. name-
text —
cldiceCentreline Dice, which measures whether a structure is connected rather than whether its pixels coincide. A skeleton is a property of a whole mask, so on a tiled run (splits) it is reported per case byevaluate_cases, which reassembles a case’s tiles before measuring it. It is absent from the epoch and from the history there, because a per-batch average of a per-tile skeleton would be a number about nothing - which also means a callback cannot watchval_cldiceon a tiled run, and the specification says so when it is read. iterations-
a whole number —
5How many times the skeleton is peeled. It has to reach the half-width of the thickest structure or its core is never reduced to a centreline, and five covers a ten-pixel object. A retinal vessel is about nine pixels wide in the data - measured on FIVES as mask area over skeleton length, not the one to five often quoted, which is what a naive resize leaves behind - so five is enough for vessels and not much more than enough. Raise it for something thicker, at a cost linear in the number; too few is reported rather than scored, because an empty skeleton otherwise reads as a perfect 1.0.
dice
The number to report unless there is a reason not to.
smooth-
a number —
1.0Zero is allowed here, unlike in the losses. Smoothing inflates a reported score: a class absent from an image scores a perfect 1.0 with smooth>0. Use 0 for an honest number, at the cost of NaN on absent classes. include_background-
trueorfalse—trueAverage over every class, background included. In medical images the background is often 95% of the pixels, so leaving it in flatters the score; papers usually report foreground only. Set false for that. name-
text —
diceWhich metric. These are computed on the mask rather than on the objective, so they stay comparable between models trained on different losses.
iou
The same ordering as Dice, on the scale papers outside medicine use.
smooth-
a number —
1.0Zero is allowed here, unlike in the losses. Smoothing inflates a reported score: a class absent from an image scores a perfect 1.0 with smooth>0. Use 0 for an honest number, at the cost of NaN on absent classes. include_background-
trueorfalse—trueAverage over every class, background included. In medical images the background is often 95% of the pixels, so leaving it in flatters the score; papers usually report foreground only. Set false for that. name-
text —
iouWhich metric. These are computed on the mask rather than on the objective, so they stay comparable between models trained on different losses.
surface_dice
How much of each boundary lies within a tolerance of the other, rather than how much of the volume coincides.
smooth-
a number —
1.0Zero is allowed here, unlike in the losses. Smoothing inflates a reported score: a class absent from an image scores a perfect 1.0 with smooth>0. Use 0 for an honest number, at the cost of NaN on absent classes. include_background-
trueorfalse—falseFalse here wherediceandioudefault to True, and for a measured reason rather than the usual one. The background’s boundary is the foreground’s boundary plus the edge of the picture, and that edge agrees trivially between any two masks - so the background class reads high whatever the model did. Measured on four random pairs attau2: background 0.6110 to 0.7030 against foreground 0.5583 to 0.6266, higher every time, by 0.05 to 0.12. That is agreement on the field of view rather than on the object. name-
text —
surface_diceNormalised surface Dice: the fraction of each boundary lying withintauof the other. A surface is a property of a whole mask, so on a tiled run (splits) it is reported per case byevaluate_cases, which reassembles a case’s tiles first, and it is absent from the epoch history - a per-batch average of per-tile boundaries would be a number about nothing. tau-
a number — required
The tolerance, and it is required because no default is honest: a tolerance belongs to the structure and the dataset, and is conventionally the inter-observer variability for that structure - the distance two people drawing the same boundary differ by. Published values exist per organ and per challenge; take one from the protocol you are comparing against rather than from here. In millimetres when
target_spacingputs the data on a common grid, and in voxels when it does not.
tversky
A score that can be asked to care more about what was missed.
smooth-
a number —
1.0Zero is allowed here, unlike in the losses. Smoothing inflates a reported score: a class absent from an image scores a perfect 1.0 with smooth>0. Use 0 for an honest number, at the cost of NaN on absent classes. include_background-
trueorfalse—trueAverage over every class, background included. In medical images the background is often 95% of the pixels, so leaving it in flatters the score; papers usually report foreground only. Set false for that. name-
text —
tverskyWhich metric. These are computed on the mask rather than on the objective, so they stay comparable between models trained on different losses. alpha-
a number —
0.5Weight on false negatives, with 1 - alpha on false positives - the same meaning as in the Tversky loss. At 0.5 this is Dice, so it is worth moving or not reporting at all: a Tversky metric at 0.5 beside a Dice metric is one number twice.
Optimisers
adadelta
Adagrad’s idea, bounded by a window instead of a sum.
learning_rate-
a number —
0.001How big a step to take. The one setting worth trying first when a run will not learn: too high and the loss moves without improving, too low and it improves too slowly to tell from not improving at all. Each parameter group decays from its own rate, so anencoder_learning_rateis not flattened by a schedule. weight_decay-
a number —
0.0Pull weights towards zero at every step, which is L2 regularisation. Off by default because it interacts with the loss and is not free: on a small dataset it can cost more than the overfitting it prevents. name-
text —
adadeltaWhich optimiser, and it decides which other keys this block accepts -sgdtakesmomentum, the Adam family takesbeta_1andbeta_2.learning_rateandweight_decayare accepted by all of them. rho-
a number —
0.9How long a window of squared gradients to remember. Adadelta exists to avoid Adagrad’s ever-shrinking step, and this is the parameter that does it. eps-
a number —
1e-06Added to the denominator so a parameter whose gradient has been zero for a while does not take an enormous step. It is a guard, not a knob.
adagrad
Accumulates every squared gradient it has ever seen, and so slows to a stop.
learning_rate-
a number —
0.001How big a step to take. The one setting worth trying first when a run will not learn: too high and the loss moves without improving, too low and it improves too slowly to tell from not improving at all. Each parameter group decays from its own rate, so anencoder_learning_rateis not flattened by a schedule. weight_decay-
a number —
0.0Pull weights towards zero at every step, which is L2 regularisation. Off by default because it interacts with the loss and is not free: on a small dataset it can cost more than the overfitting it prevents. name-
text —
adagradWhich optimiser, and it decides which other keys this block accepts -sgdtakesmomentum, the Adam family takesbeta_1andbeta_2.learning_rateandweight_decayare accepted by all of them. lr_decay-
a number —
0.0Shrink the rate further as training goes on, on top of the shrinking Adagrad already does by accumulating every squared gradient it has seen. Adagrad’s step only ever gets smaller, which is why long runs with it can stop learning. eps-
a number —
1e-10Added to the denominator so a parameter whose gradient has been zero for a while does not take an enormous step. It is a guard, not a knob.
adam
The one to use unless there is a reason not to.
learning_rate-
a number —
0.001How big a step to take. The one setting worth trying first when a run will not learn: too high and the loss moves without improving, too low and it improves too slowly to tell from not improving at all. Each parameter group decays from its own rate, so anencoder_learning_rateis not flattened by a schedule. weight_decay-
a number —
0.0Pull weights towards zero at every step, which is L2 regularisation. Off by default because it interacts with the loss and is not free: on a small dataset it can cost more than the overfitting it prevents. name-
text —
adamWhich optimiser, and it decides which other keys this block accepts -sgdtakesmomentum, the Adam family takesbeta_1andbeta_2.learning_rateandweight_decayare accepted by all of them. beta_1-
a number —
0.9Decay for the running average of the gradient. Lower reacts to the last few batches, higher smooths over more of them; 0.9 is the usual value and rarely worth moving. beta_2-
a number —
0.999Decay for the running average of the squared gradient, which is what scales each parameter’s step. Lowering it below about 0.99 makes training noticeably noisier. eps-
a number —
1e-08Added to the denominator so a parameter whose gradient has been zero for a while does not take an enormous step. It is a guard, not a knob. amsgrad-
trueorfalse—falseKeep the largest squared-gradient average seen so far rather than the current one, so the step size never grows back. It fixes a convergence case in Adam’s proof and usually changes nothing in practice; worth trying if a run plateaus and then gets worse.
adamax
Adam under the infinity norm, which a few huge batches cannot upset.
learning_rate-
a number —
0.001How big a step to take. The one setting worth trying first when a run will not learn: too high and the loss moves without improving, too low and it improves too slowly to tell from not improving at all. Each parameter group decays from its own rate, so anencoder_learning_rateis not flattened by a schedule. weight_decay-
a number —
0.0Pull weights towards zero at every step, which is L2 regularisation. Off by default because it interacts with the loss and is not free: on a small dataset it can cost more than the overfitting it prevents. name-
text —
adamaxWhich optimiser, and it decides which other keys this block accepts -sgdtakesmomentum, the Adam family takesbeta_1andbeta_2.learning_rateandweight_decayare accepted by all of them. beta_1-
a number —
0.9Decay for the running average of the gradient. Lower reacts to the last few batches, higher smooths over more of them; 0.9 is the usual value and rarely worth moving. beta_2-
a number —
0.999As for Adam, except Adamax tracks the largest gradient seen rather than the average of their squares, which makes it the steadier of the two when a few batches carry very large gradients. eps-
a number —
1e-08Added to the denominator so a parameter whose gradient has been zero for a while does not take an enormous step. It is a guard, not a knob.
adamw
Adam, with weight decay that actually decays the weights.
learning_rate-
a number —
0.001How big a step to take. The one setting worth trying first when a run will not learn: too high and the loss moves without improving, too low and it improves too slowly to tell from not improving at all. Each parameter group decays from its own rate, so anencoder_learning_rateis not flattened by a schedule. weight_decay-
a number —
0.01As for the other optimisers, but applied to the weights directly rather than through the gradient - which is the whole difference between AdamW and Adam, and why the default here is 0.01 rather than 0. name-
text —
adamwWhich optimiser, and it decides which other keys this block accepts -sgdtakesmomentum, the Adam family takesbeta_1andbeta_2.learning_rateandweight_decayare accepted by all of them. beta_1-
a number —
0.9Decay for the running average of the gradient. Lower reacts to the last few batches, higher smooths over more of them; 0.9 is the usual value and rarely worth moving. beta_2-
a number —
0.999Decay for the running average of the squared gradient, which is what scales each parameter’s step. Lowering it below about 0.99 makes training noticeably noisier. eps-
a number —
1e-08Added to the denominator so a parameter whose gradient has been zero for a while does not take an enormous step. It is a guard, not a knob.
nadam
Adam with the look-ahead that sgd calls nesterov.
learning_rate-
a number —
0.001How big a step to take. The one setting worth trying first when a run will not learn: too high and the loss moves without improving, too low and it improves too slowly to tell from not improving at all. Each parameter group decays from its own rate, so anencoder_learning_rateis not flattened by a schedule. weight_decay-
a number —
0.0Pull weights towards zero at every step, which is L2 regularisation. Off by default because it interacts with the loss and is not free: on a small dataset it can cost more than the overfitting it prevents. name-
text —
nadamWhich optimiser, and it decides which other keys this block accepts -sgdtakesmomentum, the Adam family takesbeta_1andbeta_2.learning_rateandweight_decayare accepted by all of them. beta_1-
a number —
0.9Decay for the running average of the gradient. Lower reacts to the last few batches, higher smooths over more of them; 0.9 is the usual value and rarely worth moving. beta_2-
a number —
0.999Decay for the running average of the squared gradient, which is what scales each parameter’s step. Lowering it below about 0.99 makes training noticeably noisier. eps-
a number —
1e-08Added to the denominator so a parameter whose gradient has been zero for a while does not take an enormous step. It is a guard, not a knob.
rmsprop
Adam’s denominator without Adam’s momentum.
learning_rate-
a number —
0.001How big a step to take. The one setting worth trying first when a run will not learn: too high and the loss moves without improving, too low and it improves too slowly to tell from not improving at all. Each parameter group decays from its own rate, so anencoder_learning_rateis not flattened by a schedule. weight_decay-
a number —
0.0Pull weights towards zero at every step, which is L2 regularisation. Off by default because it interacts with the loss and is not free: on a small dataset it can cost more than the overfitting it prevents. name-
text —
rmspropWhich optimiser, and it decides which other keys this block accepts -sgdtakesmomentum, the Adam family takesbeta_1andbeta_2.learning_rateandweight_decayare accepted by all of them. alpha-
a number —
0.99Decay for the running average of the squared gradient - RmsProp’s name for what Adam callsbeta_2, and it does the same work. momentum-
a number —
0.0Carry a fraction of the previous step into this one. 0 is plain gradient descent; 0.9 is the usual choice and is what makes SGD competitive with the adaptive methods. eps-
a number —
1e-08Added to the denominator so a parameter whose gradient has been zero for a while does not take an enormous step. It is a guard, not a knob.
sgd
The plain step, and the one that rewards being tuned.
learning_rate-
a number —
0.001How big a step to take. The one setting worth trying first when a run will not learn: too high and the loss moves without improving, too low and it improves too slowly to tell from not improving at all. Each parameter group decays from its own rate, so anencoder_learning_rateis not flattened by a schedule. weight_decay-
a number —
0.0Pull weights towards zero at every step, which is L2 regularisation. Off by default because it interacts with the loss and is not free: on a small dataset it can cost more than the overfitting it prevents. name-
text —
sgdWhich optimiser, and it decides which other keys this block accepts -sgdtakesmomentum, the Adam family takesbeta_1andbeta_2.learning_rateandweight_decayare accepted by all of them. momentum-
a number —
0.0Carry a fraction of the previous step into this one. 0 is plain gradient descent; 0.9 is the usual choice and is what makes SGD competitive with the adaptive methods. nesterov-
trueorfalse—falseLook ahead to where momentum is about to carry the weights before measuring the gradient. Needsmomentumabove 0, and giving it without momentum is refused rather than ignored.
Callbacks
cosine_annealing
Decay the learning rate along a cosine, from its initial value to min_lr.
name-
text —
cosine_annealingWhich callback, and it decides which other keys this block accepts. Giving a key the chosen callback does not have is refused by name rather than ignored. min_lr-
a number —
0.0The rate at the last epoch, which the cosine decays to from the optimiser’s own learning_rate. Each parameter group decays from its own initial rate, so an encoder_learning_rate is not flattened at the first epoch - that would have been invisible, since the history records one learning_rate, the first group’s. epochs-
a whole number — unset
How many epochs to spread the decay over. Unset means the model’s
epochs, which is what you want: a cosine that ends where the run ends. Setting it shorter parks the rate atmin_lrfor the remaining epochs, and longer stops the run part way down the curve.
csv_logger
The history, written as it arrives.
name-
text —
csv_loggerWhich callback, and it decides which other keys this block accepts. Giving a key the chosen callback does not have is refused by name rather than ignored. path-
text — required
Where to append one row per epoch. The same numbers
fit()returns, written as they arrive - so a run that is interrupted still leaves its history behind.
early_stopping
Stop when the watched number stops improving, and keep the weights that did.
name-
text —
early_stoppingWhich callback, and it decides which other keys this block accepts. Giving a key the chosen callback does not have is refused by name rather than ignored. monitor-
text —
val_lossWhat to watch:val_loss,train_loss, orval_followed by a metric the model asks for, such asval_dice. Whether the number should rise or fall is worked out from its name - anything ending inlossis minimised. Watching something no metric will produce is refused when the specification is built, and so is watchingval_*in a run withvalidation: false, rather than waiting for a number that never arrives. patience-
a whole number —
10Epochs to wait, with no improvement, before acting. min_delta-
a number —
0.0Improvement smaller than this does not count as improvement. Useful where the watched number wanders: without it, noise alone keeps resetting the patience. restore_best-
trueorfalse—truePut the best weights back when training stops, rather than keeping the last - which are by definition the ones that were not improving. Note thatswareplaces nothing if a run stops before averaging began.
model_checkpoint
The run’s safety net - one file, overwritten.
name-
text —
model_checkpointWhich callback, and it decides which other keys this block accepts. Giving a key the chosen callback does not have is refused by name rather than ignored. path-
text — required
Where to write the checkpoint. One file, overwritten - this is the run’s safety net, not a history;
export_weights()is what produces a file to publish, with a sidecar recording what it is. monitor-
text —
val_lossWhat to watch:val_loss,train_loss, orval_followed by a metric the model asks for, such asval_dice. Whether the number should rise or fall is worked out from its name - anything ending inlossis minimised. Watching something no metric will produce is refused when the specification is built, and so is watchingval_*in a run withvalidation: false, rather than waiting for a number that never arrives. save_best_only-
trueorfalse—trueWrite only when the watched number improves. Withfalseevery epoch is written, which is what to use when a run may be interrupted rather than when the best epoch is wanted.
reduce_lr_on_plateau
Cut the learning rate when the evidence says the run has stalled.
name-
text —
reduce_lr_on_plateauWhich callback, and it decides which other keys this block accepts. Giving a key the chosen callback does not have is refused by name rather than ignored. monitor-
text —
val_lossWhat to watch:val_loss,train_loss, orval_followed by a metric the model asks for, such asval_dice. Whether the number should rise or fall is worked out from its name - anything ending inlossis minimised. Watching something no metric will produce is refused when the specification is built, and so is watchingval_*in a run withvalidation: false, rather than waiting for a number that never arrives. factor-
a number —
0.1Multiply the learning rate by this when the watched number stops moving. patience-
a whole number —
5Epochs to wait, with no improvement, before acting. min_lr-
a number —
0.0The floor the rate will not go below. Unlikecosine_annealing, this one acts on evidence rather than on a schedule, so it may never fire at all - which is why it is not refused alongsideswaand a cosine is.
swa
Average the weights over the last part of the run instead of taking the final ones.
name-
text —
swaWhich callback, and it decides which other keys this block accepts. Giving a key the chosen callback does not have is refused by name rather than ignored. start-
a number —
0.75The fraction of the run after which averaging begins - 0.75 folds the last quarter. A fraction rather than an epoch so that it survives a change toepochs, and the final epoch is always folded, so 1.0 averages one set of weights rather than none. learning_rate- a number — unset Hold the rate at this value once averaging begins. Unset leaves whatever the run was doing. Worth setting, and the reason is the whole mechanism: averaging is only worth something while the weights are still moving. A rate that has decayed towards zero produces a set of nearly identical snapshots, and their average is the last one.
terminate_on_nan
End the run the moment the loss stops being a number.
name-
text —
terminate_on_nanWhich callback, and it decides which other keys this block accepts. Giving a key the chosen callback does not have is refused by name rather than ignored.
Separations
flows
A unit vector field pointing at each object’s sink, plus the semantic channels.
name-
text —
flowsWhich separation signal, and it decides which other keys this block accepts:rimtakeswidth,flowstakesmode,lossandweight. A setting that belongs to the other mechanism is not refused here, it is unwritable -widthundername: flowsis an unknown key, which is the answer every typo in a specification gets. That is this block’s reason to be a block, and it was built flat first:separation: rimbesiderim_width,flow_mode,flow_lossandflow_weight. The flat form parses, so it needs a validator and a table of which field belongs to which mechanism to refuse half of what it accepts, and it shows every reader four fields of which two can apply to them. This form shows a reader ofname: rimone field, and the refusal is gone rather than written -extra="forbid"was already doing that job for every other key in the file. mode-
centroidordiffusion—diffusionHow the vector field is built: towards each object’s centroid in a straight line, or down the gradient of heat diffused inside it.diffusionis the default because the two were measured on the ground truth before anything was trained: decoded back into objects they reach 0.9924 and 0.9478. The obvious explanation for the gap is not the cause - only 19 of 6000 objects have their centroid outside themselves, 0.32% - and the real one is one level down: a straight line between two pixels of a discrete set can leave the set even when the object looks convex, which splits an object whose own centroid is inside it.centroidis 23 times cheaper to build (4.9 ms a case against 113.6 ms), which is the reason to reach for it and is small beside an epoch. loss-
one of
boundary,cce,cce_dice,combo,dice,focal,focal_tversky,generalized_dice,iou,l1,lovasz,mse,smooth_l1,tversky— unset The objective on the two vector channels, which is a different question from the model’s ownloss- that one scores the mask. A continuous target has nothing in the overlap-and-cross-entropy losses to be trained against, so this is one ofmse,l1orsmooth_l1, and anything else is refused by name:loss: diceagainst a vector field would return a number and a gradient, which is the worst available outcome. Left unset it ismsewithwhere: foreground, which is what the measured run used.whereis the field to read before changing it, because the foreground is 13.97% of a Data Science Bowl image andeverywherespends 86% of the objective on a background zero. weight-
a number —
1.0How much the vector term counts against the mask term, which are summed. The default is 1.0 and it is measured rather than conventional: on the 60-epoch run the two terms are the same order of magnitude throughout - 0.5937 against 0.6345 after the first epoch and 0.0734 against 0.0659 at the last - so neither half of the objective is drowning the other at this weight.
rim
Background, object interior, object rim - three classes, one extra channel.
name-
text —
rimWhich separation signal, and it decides which other keys this block accepts:rimtakeswidth,flowstakesmode,lossandweight. A setting that belongs to the other mechanism is not refused here, it is unwritable -widthundername: flowsis an unknown key, which is the answer every typo in a specification gets. That is this block’s reason to be a block, and it was built flat first:separation: rimbesiderim_width,flow_mode,flow_lossandflow_weight. The flat form parses, so it needs a validator and a table of which field belongs to which mechanism to refuse half of what it accepts, and it shows every reader four fields of which two can apply to them. This form shows a reader ofname: rimone field, and the refusal is gone rather than written -extra="forbid"was already doing that job for every other key in the file. width-
a whole number —
2How many pixels wide the rim class is, in pixels of the model’s own input - so it means the same thing whether the image was resized or tiled, because the target is built after the resize and the augmentation rather than before (pyplatypus#210). There is a measured sweep of this quantity and it is not a sweep of this field, which is worth knowing before quoting either. Four runs differing in nothing else scored 0.5169, 0.5486, 0.5587 and 0.5306 of instance average precision at widths 1 to 4, against a 0.0074 spread between two runs of one configuration - but that pipeline derived the rim at the source resolution and resized the three-class map afterwards. A Data Science Bowl case is around 520 pixels and the model reads 256, so the peak of that sweep is roughly 1.5 pixels of model input, not 3. The default is the nearest width above it. A sweep in these units has not been run. What that sweep does establish is the shape of the trade, which is the part that transfers: a rim wide enough to learn is wide enough to hollow the object out, so rim Dice climbed 0.48 to 0.76 across those widths while interior Dice fell 0.90 to 0.81, and past the peak the model found more regions and matched fewer of them. Both halves are reported per run, because a single instance number cannot say which of them moved.
Decodings
connected_components
Label the thresholded foreground and ignore the separation signal entirely.
name-
text —
connected_componentsWhich decoding, and it decides which other keys this block accepts -follow_flowstakesstepsandstep_size, the others do not. A decoding the chosenseparationcannot feed is refused by name with both halves stated, rather than returning a label map of something else. minimum_area-
a whole number —
0Drop an object smaller than this many pixels. 0 keeps everything, which is the default because the right floor is a property of the data rather than of the method - a nucleus at 256x256 in the Data Science Bowl runs from 4 pixels up, so a floor chosen here would silently delete the smallest real objects in one dataset while letting speckle through in another. It is applied after the objects are formed, so it removes answers rather than changing how they were reached.
follow_flows
Follow the field from every foreground pixel and label the basins they arrive in.
name-
text —
follow_flowsWhich decoding, and it decides which other keys this block accepts -follow_flowstakesstepsandstep_size, the others do not. A decoding the chosenseparationcannot feed is refused by name with both halves stated, rather than returning a label map of something else. minimum_area-
a whole number —
0Drop an object smaller than this many pixels. 0 keeps everything, which is the default because the right floor is a property of the data rather than of the method - a nucleus at 256x256 in the Data Science Bowl runs from 4 pixels up, so a floor chosen here would silently delete the smallest real objects in one dataset while letting speckle through in another. It is applied after the objects are formed, so it removes answers rather than changing how they were reached. steps-
a whole number —
200How many Euler steps each pixel takes. Measured as not mattering: one step scores 0.9914 against 0.9923 converged, a span below the 0.0013 two runs differ by. The field is continuous, so one object’s pixels stay together, and it diverges at a seam, so the gap between two objects opens on the first step rather than after many. Left adjustable because that argument is about this data’s object sizes, and lowering it is the cheap thing to try on larger ones. step_size-
a number —
1.0How far one step moves, in pixels, on a unit-length field. This one is a cliff: 0.9923 at 1.0, 0.4715 at 2.0, 0.0303 at 10.0. A step longer than a pixel jumps over the sink it was converging on and lands in a neighbour’s basin, so this is not a speed knob - raise it and the decoding stops working.
nearest_seed
Grow the predicted interiors outward: every other foreground pixel joins the nearest.
name-
text —
nearest_seedWhich decoding, and it decides which other keys this block accepts -follow_flowstakesstepsandstep_size, the others do not. A decoding the chosenseparationcannot feed is refused by name with both halves stated, rather than returning a label map of something else. minimum_area-
a whole number —
0Drop an object smaller than this many pixels. 0 keeps everything, which is the default because the right floor is a property of the data rather than of the method - a nucleus at 256x256 in the Data Science Bowl runs from 4 pixels up, so a floor chosen here would silently delete the smallest real objects in one dataset while letting speckle through in another. It is applied after the objects are formed, so it removes answers rather than changing how they were reached.
watershed
Flood the foreground from the predicted interiors, over the rim evidence as terrain.
name-
text —
watershedWhich decoding, and it decides which other keys this block accepts -follow_flowstakesstepsandstep_size, the others do not. A decoding the chosenseparationcannot feed is refused by name with both halves stated, rather than returning a label map of something else. minimum_area-
a whole number —
0Drop an object smaller than this many pixels. 0 keeps everything, which is the default because the right floor is a property of the data rather than of the method - a nucleus at 256x256 in the Data Science Bowl runs from 4 pixels up, so a floor chosen here would silently delete the smallest real objects in one dataset while letting speckle through in another. It is applied after the objects are formed, so it removes answers rather than changing how they were reached.