The reference path's training loop, for any NetSpec MlirCodegen lowers.
NetSpec.train generates the forward, eval-forward and train-step MLIR (compileVmfbs), opens
a runtime session, and runs runTraining: init params → for each epoch { shuffle, for each batch
{ train step, EMA, log }, val eval } → save. A trainer is one call:
def main (args : List String) : IO Unit :=
resnet34.train resnet34Config (args.head?.getD "data/imagenette")
Adding an architecture means defining the spec and a TrainConfig value. The per-dataset
loading and augmentation live in the per-dataset records below.
Region Dice off a confusion matrix #
Shared by the trainer's per-epoch val line and brats-eval's per-volume scorer, so the two
score a region the same way.
(inter, |gt|, |pred|) of the class-union region cls, read off an nc × nc confusion
matrix conf[gt * nc + pred]:
inter = Σ_{g∈R} Σ_{p∈R} C[g][p], |gt| = Σ_{g∈R} Σ_p C[g][p], |pred| = Σ_{p∈R} Σ_g C[g][p].
Counts stay exact Nat.
Equations
- One or more equations did not get rendered due to their size.
Instances For
2·inter / (|gt| + |pred|), with the BraTS convention for a region the ground truth lacks:
an empty prediction scores 1 and a non-empty one 0.
Equations
Instances For
File-path prefix for the generated MLIR / vmfb / saved-params files associated with this spec. Uses the sanitized spec name so adding a new spec automatically gets a unique non-colliding prefix without the trainer author having to pick one.
Equations
- spec.buildPrefix = ".lake/build/" ++ spec.sanitizedName ++ if spec.buildTag.isEmpty = true then "" else "_" ++ MlirCodegen.sanitize spec.buildTag
Instances For
Set buildTag, so an ablation arm owns its own artifacts. Reads as
(net.withBuildTag "ce").train … at the call site. (Not buildTag — that
name belongs to the field itself.)
Equations
- One or more equations did not get rendered due to their size.
Instances For
The fully qualified <module>.<func> name the train step's main
function lives at — what trainStepAdamF32 wants as its fnName.
Mirrors how MlirCodegen.generateTrainStep builds the module
name from the third argument it's given (jit_<sanitized>_train_step).
Equations
- spec.trainFnName = "jit_" ++ spec.sanitizedName ++ "_train_step.main"
Instances For
Cache key for an MLIR string under the current IREE backend.
Combines the MLIR content with the backend env var so switching
IREE_BACKEND=rocm ↔ cuda invalidates the cache.
Equations
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Path of the runnable artifact for one emitted graph, on whichever backend is
active at run time (LowererSession.backendName). No executable is linked against a
backend: every one links lowererLink, and ffi/lowerer.c dlopens the shim
$LEAN_MLIR_LOWERER names.
- IREE —
iree-compileturns{pfx}_{suffix}.mlirinto a.vmfb, and that.vmfbis whatLowererSession.createloads. - XLA — PJRT compiles the
.mlirin-process, so no.vmfbis ever produced and the.mliris the artifact.
Every pathExists guard and LowererSession.create call below routes through
this. The guards are the reason it exists: they are what decide whether eval
runs at all, and with a hardcoded .vmfb path they go silently false on XLA
— training would look perfectly healthy and simply stop reporting val
metrics, which is a much worse failure than not loading.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The eval forward of spec over the n images of a flat f32 set (imgFloats floats each) at
batch batch, the tail batch padded with the last real image: the [n, nOut] f32 logits of
the real rows. The detector demos' infer dumps.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Compile one emitted graph to the artifact graphArtifact names: iree-compile on IREE; on
XLA nothing, since PJRT compiles the .mlir itself in-process. false on a failed compile
(the compiler's error is printed). Uncached; compileVmfbs keys its own compiles on the MLIR
text.
Equations
- NetSpec.compileArtifact mlirPath outPath = do let __do_lift ← LowererSession.backendName if (__do_lift == "xla") = true then pure true else NetSpec.runIree mlirPath outPath
Instances For
Compile mlirPath to outPath via iree-compile, but skip the work
if outPath exists and outPath ++ ".hash" matches the cache key
for the current MLIR + IREE backend. Returns (compiledNewly, success).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Generate forward / eval-forward / train-step MLIR for spec and
write them to .lake/build/<sanitized>_*.mlir. On IREE, also compile
each to a .vmfb, cached on the MLIR content + IREE backend so a
second run with no codegen changes skips iree-compile; on XLA there is
no compile step. Returns the train-step artifact graphArtifact
resolves (.mlir on XLA, .vmfb on IREE).
The loss is cfg.lossKindFor ds; a per-pixel kind switches the train-step codegen to the
segmentation path, where labels are an int32 [B, H, W] tensor instead of a [B] class
index.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dataset-specific I/O: where the training/val data lives, how big each
image is on disk, and what augmentation to apply per batch. Each
dataset (Imagenette / CIFAR-10 / MNIST) constructs one of these and
runTraining consumes it.
- trainPixels : Nat
Pixels per training image as stored on disk (before any crop).
- valPixels : Nat
Pixels per val image as stored on disk.
- channels : Nat
Channels (1 for MNIST, 3 for CIFAR/Imagenette).
- labelBytesPerRecord : Nat
Bytes per label record. 4 for int32-class classification (default), HW for per-pixel segmentation masks (BraTS: 240240 = 57600).
Named unions of raw classes to additionally report Dice for at eval. Empty (the default) for datasets whose raw classes are already the entity the field reports, where per-class IoU is the whole story.
BraTS is the exception this exists for: the literature scores nested unions (WT/TC/ET), not the raw labels, and because the unions are nested they cannot be recovered from per-class IoU after the fact.
Apply augmentation to a slice of training data. Receives the raw slice, the batch size, and a seed (epoch * 10000 + bi). Must return a buffer matching
inputFlatDim specfloats per image.Deterministic preprocessing for training when cfg.augment=false. Datasets whose on-disk training size differs from the model's input size (e.g. Imagenette: 256 stored, 224 expected) override this to do a center-crop. MNIST and CIFAR default to identity since their stored size already matches the input. NOT used for validation — val data is loaded at the model's input size directly, see
valPreprocessBatch.Deterministic preprocessing for validation. Defaults to identity because val data is loaded at the model's input size in every dataset (Imagenette: 224 via
loadImagenetteeven though train is 256; MNIST/CIFAR: same size as train). Override only if a future dataset stores val data at a different size from the input.Stochastic augmentation for test-time augmentation, i.e. the eval loop's
cfg.useTTApath.augmentBatchcannot be reused there: it receives records at the dataset's stored training size, and TTA hands it records at the validation size. For Imagenette those differ — 256 train, 224 val — so callingaugmentBatchon a val batch strides a 256-pixel crop across a 224-pixel buffer: every record after the first is read at a 46,080-float drift, and the tail of the batch reads off the end of the allocation. The image at slotithen no longer belongs to the label at sloti, which is invisible to everything downstream — it just lowers the reported accuracy.Defaults to
valPreprocessBatch, which makes TTA a no-op (M identical passes) rather than a corruption. Override per dataset with transforms defined at the validation size.
Instances For
Equations
Imagenette: 256×256 train, 224×224 val, random-crop-to-224 + hflip. When augment=false, falls back to deterministic center-crop 256→224 so the model still receives correctly-shaped input.
Equations
- One or more equations did not get rendered due to their size.
Instances For
MNIST: 28×28 IDX format, no augmentation.
Equations
- One or more equations did not get rendered due to their size.
Instances For
CIFAR-10: 5 train batches concatenated, 1 test batch, random hflip.
Equations
- One or more equations did not get rendered due to their size.
Instances For
BraTS brain-tumour segmentation (MSD Task01_BrainTumour), 2D axial
slices. 240×240 images with 4 MRI modalities as channels (FLAIR / T1w /
T1gd / T2w) and 240×240 per-pixel masks (4-class: 0=background, 1=edema,
2=non-enhancing tumour, 3=enhancing tumour). The "labels"
buffer is the mask buffer (240*240 bytes per record), which is what puts
runTraining on the .perPixelCE segmentation path.
240 is the native BraTS in-plane size and 240 = 16*15, so a depth-4 UNet's four halvings divide evenly — no resize is needed anywhere in the pipeline. Augmentation is identity for now.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Box detection at 224/7×7 (the YOLOv1 record). 224×224 RGB images; the "labels"
buffer carries the 30×7×7 float32 target tensor concatenated with the
7×7 float32 per-cell objectness mask (6076 bytes per record). The
runTraining dispatch splits this into target + mask before calling
trainStepAdamF32Yolov1. See scripts/datasets/preprocess_visdrone.py for the on-disk
format.
Equations
- One or more equations did not get rendered due to their size.
Instances For
BraTS at 224×224 — bratsIO with one number changed. Everything that
makes BraTS BraTS (4 modalities, the mask-as-labels buffer that selects the
.perPixelCE path, MSD's label numbering for the regions) is identical, so
the region table is shared rather than restated: getting WT/TC/ET out of
sync between two copies of the same dataset is exactly the silent bug
bratsIO's own comment warns about.
Equations
- One or more equations did not get rendered due to their size.
Instances For
BraTS at 224×224 in 2.5D — brats224IO with the channel count changed: 4·(2·ctx+1)
channels per record, the centre slice's mask as the label. Same regions, same label
numbering, same split; the loader takes the channel count and checks the file length
against it, so a 2D file read as 2.5D (or the reverse) fails at load rather than
misaligning every mask after the first.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
- NetSpec.datasetIO DatasetKind.imagenette = NetSpec.imagenetteIO
- NetSpec.datasetIO DatasetKind.mnist = NetSpec.mnistIO
- NetSpec.datasetIO DatasetKind.cifar10 = NetSpec.cifar10IO
- NetSpec.datasetIO DatasetKind.detection = NetSpec.detectionIO
- NetSpec.datasetIO DatasetKind.brats = NetSpec.bratsIO
- NetSpec.datasetIO DatasetKind.brats224 = NetSpec.brats224IO
- NetSpec.datasetIO (DatasetKind.brats224Ctx ctx) = NetSpec.brats224CtxIO ctx
Instances For
The named class-unions ds reports Dice for (DatasetIO.segRegions; empty for most).
Equations
- ds.segRegions = (NetSpec.datasetIO ds).segRegions
Instances For
Adam or SGD+momentum (TrainConfig.optimizer), cosine LR, running-BN-stats training loop, generic over
DatasetKind. The dataset specifies how to load the train/val
data and what augmentation to apply per batch; everything else
(init, optimizer, BN EMA, val eval, save) is identical across
datasets.
spec must have been compiled via compileVmfbs first.
Instances For
Eval-only mode: skip training entirely, load saved params + bn_stats,
run the eval forward on val data, print accuracy. Used to re-eval
existing checkpoints against a fixed eval pipeline. Requires compileVmfbs to
have produced (or cached) the _fwd_eval.vmfb already.
Equations
- One or more equations did not get rendered due to their size.
Instances For
End-to-end: compile all three vmfbs, load the train-step session, and run the training loop on the chosen dataset. The entry point the reference trainers call.
LEAN_MLIR_EVAL_ONLY=1 short-circuits to evalOnly — skips
training, loads the saved checkpoint, runs eval. Used for re-eval
after a fixed eval pipeline.
Defaults to Imagenette.
Equations
- One or more equations did not get rendered due to their size.