Documentation

LeanMlir.Types

The reference path's specification types #

Layer (one architecture primitive), NetSpec (a named layer list plus input size and per-net conventions), TrainConfig (optimizer, schedule, loss and augmentation settings), LossKind, OptimizerKind and DatasetKind. Spec counts and validates specs; MlirCodegen (NetSpec → StableHLO at run time) and the JAX emitter (jax/Jax/Codegen.lean) lower them; Train runs them. None of this is the verified path, which trains from the committed verified_mlir/ renders (Verified.Spec, Verified.Train). Also here: the iree-compile argument builders.

References #

inductive Activation :

Pointwise activation carried by a layer: ReLU, ReLU6, identity, swish (SiLU), hard-swish, GELU.

Instances For
    @[instance_reducible]
    Equations
    @[instance_reducible]
    Equations
    Equations
    Instances For
      inductive Normalization :

      Normalization choice — picked at the block level by primitives that can run with either LayerNorm or BatchNorm (e.g. ConvNeXt, in its original LN form or a hypothetical BN variant for ablation).

      Instances For
        inductive Padding :

        Convolution padding intent: same keeps the spatial size (at stride 1), valid pads nothing. Read by the JAX emitter; see PadStyle for how same is spelled when strided.

        Instances For
          Equations
          Instances For
            @[instance_reducible]
            Equations
            @[instance_reducible]
            Equations
            Equations
            Instances For
              inductive PadStyle :

              How a layer's .same padding is spelled when it is STRIDED.

              .same states an intent ("keep the output size"), and at stride 1 with an odd kernel the two spellings below are bit-identical — which is every conv in the kit except the strided ones. They differ only when strided on an even input, where the two grids sit one input position apart:

              • .xlaSame — XLA 'SAME', extra row on the HIGH side. 7×7/s2 on 224 pads (2,3); 3×3/s2 on an even input pads (0,1). This is the reference for the TF-origin ports (MobileNetV2/V4, EfficientNet), where asymmetric 'SAME' is the published net.
              • .symmetric — torchvision / He et al., (k-1)//2 on both sides, i.e. nn.Conv2d(padding=k//2). This is the reference for the ResNet family.

              NetSpec.convPadStyle picks one per net. Neither shape, op count nor arity distinguishes the two, so a net whose reference and render disagree here shows no structural difference. In the JAX emitter .symmetric passes no padding argument, so the Python helpers conv2d/conv_bn's own symmetric default applies and the convention is stated once.

              Instances For
                @[instance_reducible]
                Equations
                Equations
                Instances For
                  Equations
                  Instances For
                    @[instance_reducible]
                    Equations
                    inductive Layer :

                    One architecture primitive of a NetSpec. A constructor is either trained by the reference codegen (Layer.paramSlots gives its parameter tensors) or shape-only (paramSlots is none; Layer.nParamsUntrained gives its displayed parameter count, and neither emitter lowers it unless its docstring says otherwise).

                    • conv2d (ic oc kSize : Nat) (pad : Padding) (act : Activation) : Layer

                      ic → oc convolution, kSize × kSize, stride 1, with bias, followed by act. The JAX emitter reads pad; MlirCodegen pads SAME and refuses .valid (MlirCodegen.unsupported).

                    • convBn (ic oc kSize stride : Nat) (pad : Padding) : Layer

                      ic → oc convolution without bias, kSize × kSize at stride, then BatchNorm and an activation: the net's NetSpec.convBnAct. MlirCodegen emits ReLU and refuses any other convBnAct (MlirCodegen.unsupported). pad as for conv2d.

                    • maxPool (size stride : Nat) : Layer

                      size × size max-pool at stride (SAME-padded in MlirCodegen).

                    • globalAvgPool : Layer

                      Global average pool over the spatial axes, [B, C, H, W] → [B, C].

                    • flatten : Layer

                      Flatten every non-batch axis.

                    • layerNorm (dim : Nat) : Layer

                      Bare LayerNorm over a [B, dim] feature vector: normalize over dim, then the per-feature affine γ ⊙ x̂ + β (2·dim params). Not the channels-first LN inside convNextStage/convNextDownsample — this is the rank-2 head-side one, which sits between two layers and so cannot be a norm := field on either of them; ConvNeXt's GAP → LN → Linear head uses it. Lowered by the JAX emitter; MlirCodegen has no lowering for it.

                    • dense (fanIn fanOut : Nat) (act : Activation) : Layer

                      Fully-connected fanIn → fanOut with bias, followed by act.

                    • residualBlock (ic oc nBlocks firstStride : Nat) : Layer

                      nBlocks ResNet basic blocks ic → oc (two 3×3 conv-BN each); the first runs at firstStride and carries a 1×1 conv-BN projection when ic ≠ oc or the stride is not 1.

                    • bottleneckBlock (ic oc nBlocks firstStride : Nat) : Layer

                      nBlocks ResNet bottleneck blocks ic → oc (1×1, 3×3, 1×1 conv-BN at width oc / 4); the first runs at firstStride and carries a 1×1 conv-BN projection when ic ≠ oc or the stride is not 1.

                    • separableConv (ic oc stride : Nat) : Layer

                      Depthwise-separable convolution: 3×3 depthwise then 1×1 pointwise ic → oc, each with BN. Lowered by the JAX emitter only; MlirCodegen has no case for it.

                    • invertedResidual (ic oc expand stride nBlocks : Nat) : Layer

                      nBlocks MobileNetV2 inverted residuals ic → oc: 1×1 expand conv-BN to expand × the block input (omitted when expand = 1), 3×3 depthwise conv-BN, 1×1 project conv-BN; the first block runs at stride.

                    • mbConv (ic oc expand kSize stride nBlocks : Nat) (useSE : Bool) (act : Activation := Activation.swish) : Layer

                      nBlocks EfficientNet MBConv blocks ic → oc: 1×1 expand (omitted when expand = 1), kSize × kSize depthwise, optional squeeze-excitation (squeeze width mbConvSeMid of the block input), 1×1 project, each conv with BN; activation act, the first block at stride.

                    • mbConvV3 (ic oc expandCh kSize stride : Nat) (useSE : Bool) (act : Activation := Activation.relu) : Layer

                      One MobileNetV3 block ic → oc with an explicit expanded width expandCh (the expand conv is omitted when expandCh = ic), kSize × kSize depthwise, optional SE, 1×1 project; activation act.

                    • fusedMbConv (ic oc expand kSize stride nBlocks : Nat) (useSE : Bool) (act : Activation := Activation.swish) : Layer

                      nBlocks Fused-MBConv blocks ic → oc: a full kSize × kSize expand conv-BN (straight to oc when expand = 1), optional SE, and a 1×1 project conv-BN when expand ≠ 1.

                    • uib (ic oc expand stride preDWk postDWk : Nat) : Layer

                      MobileNetV4 Universal Inverted Bottleneck ic → oc: optional preDWk × preDWk depthwise, 1×1 expand to ic · expand, optional postDWk × postDWk depthwise, 1×1 project, each conv with BN. k = 0 omits that depthwise.

                    • fireModule (ic squeeze expand1x1 expand3x3 : Nat) : Layer

                      SqueezeNet fire module: 1×1 squeeze ic → squeeze, then 1×1 and 3×3 expands concatenated. Lowered by the JAX emitter only; MlirCodegen has no case for it.

                    • patchEmbed (ic dim patchSize nPatches : Nat) : Layer

                      ViT patch embedding: a patchSize × patchSize stride-patchSize conv ic → dim with bias, a CLS token, and a learned [nPatches + 1, dim] position embedding.

                    • transformerEncoder (dim heads mlpDim nBlocks : Nat) (causalMask keepSequence flashAttn rope : Bool := false) : Layer

                      nBlocks pre-LN transformer encoder blocks (LN, multi-head attention with Q/K/V/O denses, LN, dim → mlpDim → dim MLP), plus a final LN.

                      • causalMask adds a triangular −∞ mask to the QKᵀ scores before softmax (TinyGPT sets it; vision transformers leave it false).
                      • keepSequence skips the ViT-style CLS-token slice at the end, keeping the [B, N, D] token shape (DDPM's bottleneck attention sets it so the tokens can flow into a spatialUnflatten).
                      • flashAttn emits the attention as a tiled online-softmax stablehlo.while instead of the dense [B,H,T,T] scores — O(T·blk) rather than O(T²) memory, same math. Used by the train step's forward and backward; the eval forward stays dense.
                      • rope applies Rotary Position Embedding to Q/K inside attention (forward rotation and its VJP), a relative position encoding that extends past the trained length; pair it with tokenPositionEmbed's posEmb := false.
                    • mambaBlock (dim stateSize expand nBlocks : Nat) : Layer

                      Selective state-space block (Mamba / S6); dim hidden, stateSize = N, expand the inner-dim multiplier. Shape-only (its count in Layer.nParamsUntrained is approximate).

                    • swinStage (dim heads mlpDim windowSize nBlocks : Nat) : Layer

                      Swin Transformer stage: nBlocks blocks of (W-MSA + MLP) with residuals, alternating with the shifted-window variant; window-local attention at fixed resolution. Shape-only.

                    • patchMerging (inDim outDim : Nat) : Layer

                      Swin patch merging: 2×2 spatial downsample, channels inDim → outDim (stated explicitly to match any hierarchy). Shape-only.

                    • unetDown (ic oc : Nat) : Layer

                      UNet encoder stage: two 3×3 conv-BN-ReLU (ic → oc → oc), then a 2×2 max-pool. The pre-pool feature map is saved as the skip for the matching unetUp.

                    • unetUp (ic oc : Nat) : Layer

                      UNet decoder stage: ×2 bilinear upsample, concatenation with the matching encoder skip (oc channels), then two 3×3 conv-BN-ReLU (ic + oc → oc → oc). ic is the channel count coming in from the previous decoder stage or bottleneck.

                    • bilinearUpsample (scale : Nat) : Layer

                      Parameter-free bilinear upsample by the integer factor scale: channels unchanged, (H, W) → (scale·H, scale·W). Lowered by MlirCodegen (forward and VJP).

                    • transformerDecoder (dim heads mlpDim nBlocks nQueries : Nat) : Layer

                      DETR-style transformer decoder: nBlocks blocks of self-attention over nQueries learned object queries, cross-attention with the encoder output, and an FFN; the object-query embedding is part of this layer's parameters. Shape-only.

                    • detrHeads (dim nClasses : Nat) : Layer

                      DETR prediction heads on each of the decoder's output tokens: a class head (dim → nClasses + 1, the extra slot "no object") and a 3-layer box MLP (dim → dim → dim → 4, predicting (cx, cy, w, h)). Shape-only.

                    • shuffleBlock (ic oc groups nUnits : Nat) : Layer

                      ShuffleNet v1 stage: nUnits units ic → oc, the first a stride-2 downsampling unit (avg-pool skip), the rest residual; each unit is 1×1 grouped conv → channel shuffle → 3×3 depthwise → 1×1 grouped conv, with groups channel partitions. Shape-only.

                    • shuffleV2Block (ic oc nUnits : Nat) : Layer

                      ShuffleNet v2 stage (Ma et al. 2018): nUnits units ic → oc, the first a stride-2 downsampling unit (both branches see the full input and are concatenated), the rest stride-1 basic units (channel split, identity on one half, 1×1 → 3×3 DW → 1×1 on the other, concatenate, shuffle). No grouped 1×1 convs and no element-wise adds. Shape-only.

                    • evoformerBlock (msaChannels pairChannels nBlocks : Nat) : Layer

                      AlphaFold-2 Evoformer stack over the dual MSA (s × r × msaChannels) and pair (r × r × pairChannels) representations: MSA row attention with pair bias, column attention, transition, outer-product mean into the pair, triangle multiplicative updates, triangle attention and pair transition, bundled because the dual representation does not fit a linear NetSpec. Shape-only.

                    • structureModule (singleChannels pairChannels nBlocks : Nat) : Layer

                      AlphaFold-2 Structure Module: nBlocks rounds of Invariant Point Attention over residues, backbone frame updates and side-chain χ-angle prediction, with weights shared across the rounds. Shape-only.

                    • mobileVitBlock (ic dim heads mlpDim nTxBlocks : Nat) : Layer

                      MobileViT block (Mehta & Rastegari 2022), ic → ic: local 3×3 conv and 1×1 projection to the transformer width dim, unfold into patches, nTxBlocks transformer blocks (heads heads, FFN width mlpDim), fold back, 1×1 projection to ic, concatenation with the input and a 3×3 fusion conv. Shape-only.

                    • convNextStage (channels nBlocks : Nat) (norm : Normalization := Normalization.ln) (act : Activation := Activation.gelu) : Layer

                      ConvNeXt stage (Liu et al. 2022): nBlocks residual blocks at fixed channels, each a 7×7 depthwise conv, LN, 1×1 expand (×4), GELU, 1×1 project and layer scale. No downsampling (see convNextDownsample). norm selects LayerNorm (the paper) or BatchNorm, act GELU (the paper) or another activation.

                    • convNextDownsample (ic oc : Nat) (norm : Normalization := Normalization.ln) : Layer

                      ConvNeXt inter-stage downsample: LayerNorm then a 2×2 stride-2 conv ic → oc. norm as in convNextStage, so a stage and its downsample stay in sync.

                    • convNextStem (ic oc patch : Nat) : Layer

                      ConvNeXt patchify stem: a patch × patch stride-patch conv ic → oc with bias, then a channels-first LayerNorm and no activation. Lowered by the JAX emitter; MlirCodegen has no lowering for it.

                    • waveNetBlock (residualCh skipCh nLayers : Nat) : Layer

                      WaveNet (van den Oord 2016) residual stack: nLayers blocks at dilations 2⁰, …, 2^(nLayers−1), each a dilated causal conv, gated activation (tanh ⊙ sigmoid), 1×1 projection back to residualCh, residual skip, and a separate 1×1 conv producing a skipCh output summed into the head. Shape-only.

                    • positionalEncoding (inputDim numFrequencies : Nat) : Layer

                      Sinusoidal positional encoding (Vaswani 2017 / NeRF 2020), γ(p) = (sin(2⁰πp), cos(2⁰πp), …, sin(2^(L−1)πp), cos(2^(L−1)πp)). Parameter-free; output dim inputDim · 2 · numFrequencies. Shape-only.

                    • nerfMLP (encodedPosDim encodedDirDim hiddenDim : Nat) : Layer

                      NeRF MLP core (Mildenhall et al. 2020): 8 hidden layers of hiddenDim with ReLU, a skip re-concatenating the position encoding at layer 5, and two heads (density σ, and RGB from a direction-conditioned branch). encodedPosDim is the dim of γ(x) (typically 60), encodedDirDim of γ(d) (typically 24). Shape-only.

                    • darknetBlock (channels nBlocks : Nat) : Layer

                      Darknet residual stack (YOLOv3): nBlocks blocks at fixed channels, each 1×1 conv (c → c/2), 3×3 conv (c/2 → c) and residual add. No downsample. Shape-only.

                    • cspBlock (ic oc nBlocks : Nat) : Layer

                      Cross-Stage Partial block (Wang et al. 2019; the YOLOv4–v11 family): split the channels, run one half through nBlocks bottleneck residual blocks, concatenate with the other half and 1×1-project to oc. One primitive for the C3 / C2f / C3k2 variants. Shape-only.

                    • inceptionModule (ic b1out b2reduce b2out b3reduce b3out b4out : Nat) : Layer

                      Inception module (GoogLeNet): four branches concatenated on channels — 1×1 (b1out); 1×1 → 3×3 (b2reduce, b2out); 1×1 → 5×5 (b3reduce, b3out); 3×3 max-pool → 1×1 (b4out). Output channels b1out + b2out + b3out + b4out. Shape-only.

                    • asppModule (ic oc : Nat) : Layer

                      Atrous Spatial Pyramid Pooling (DeepLab v3+): five branches of oc channels — 1×1 conv, 3×3 atrous convs at rates 6, 12 and 18, and global-avg-pool → 1×1 conv → upsample — each with BN and ReLU, concatenated and fused by a 1×1 conv back to oc. Shape-only.

                    • fpnModule (c2 c3 c4 c5 target : Nat) : Layer

                      Feature Pyramid Network (Lin et al. 2017): 1×1 lateral convs from the four backbone stages (c2–c5) to target channels, top-down ×2 upsample-and-add, and a 3×3 smoothing conv per level; four target-channel outputs. Shape-only (see fpnDetect for the trained detector head).

                    • fpnDetect (oc c3 c4 c5 g5 A tower : Nat) : Layer

                      FPN multi-scale detector head. Taps the three preceding backbone stage outputs C3/C4/C5 (channels c3/c4/c5, strides 8/16/32), runs a top-down neck (all → oc channels), an optional RetinaNet tower of tower 3×3 convs per level (bias, ReLU, channel-preserving, not shared across levels), then a per-scale 1×1 conv head (oc → A·15), and flattens and concatenates the three heads into one [B, Ntot] output (Ntot = A·15·(g3² + g4² + g5²), g5 the coarsest grid, imageH / 32). Parameter order is fpnDetectParamShapes, head biases last. tower = 0 emits no tower ops. The loss is the FPN multi-scale YOLO loss, routed by TrainConfig.fpnScales.

                    • denseBlock (ic growthRate nLayers : Nat) : Layer

                      DenseNet dense block: nLayers BN-ReLU-1×1 (to 4·growthRate)-BN-ReLU-3×3 sub-layers, each adding growthRate channels to the running concatenation; output ic + nLayers · growthRate channels. Shape-only.

                    • transitionLayer (ic oc : Nat) : Layer

                      DenseNet transition: BN, 1×1 conv ic → oc, 2×2 stride-2 average pool. Shape-only.

                    • spatialFlatten : Layer

                      [B, C, H, W] → [B, H·W, C] (channel axis last), no parameters. Pair with spatialUnflatten around a token-stream op (typically transformerEncoder) in an NCHW pipeline.

                    • spatialUnflatten (channels height width : Nat) : Layer

                      [B, H·W, C] → [B, C, H, W], no parameters. Carries (C, H, W) because the rank-3 token shape does not determine (H, W).

                    • tokenPositionEmbed (vocabSize seqLen dModel : Nat) (idsInput gather : Bool := false) (posEmb : Bool := true) : Layer

                      Token plus learned position embedding for autoregressive language models (TinyGPT). Input: flat [B, seqLen · vocabSize] one-hot; output [B, seqLen, dModel]. Parameters: the token embedding [vocabSize, dModel] and, when posEmb, the position embedding [seqLen, dModel].

                      • idsInput takes [B, seqLen] f32 token ids and builds the one-hot in-graph (iota, compare, select): same parameters and math, no O(V·T) host upload.
                      • gather makes the forward a stablehlo.gather of the [V, D] table by the ids and the backward a stablehlo.scatter-add, so no [B, T, V] one-hot is materialized (O(T·D) rather than O(T·V) memory). Implies ids-shaped input.
                      • posEmb := false drops the position table, for RoPE models: every weight is then independent of seqLen, so a model trained at one length runs at any length.
                    • lmHead (dModel vocabSize seqLen : Nat) : Layer

                      Language-model head: per-position logits. Input [B, seqLen, dModel], output flat [B, seqLen · vocabSize] so the per-pixel CE loss handles it by reshape. Parameters: dense [dModel, vocabSize] and bias.

                    • timeCondAdd (channels nFreq : Nat) : Layer

                      Diffusion time conditioning added onto a [B, channels, H, W] feature map: a learned dense projection of a 2·nFreq sin/cos embedding of the per-image timestep, broadcast over H, W. The timestep is read in-graph from the last input channel (Ddpm.prependTChannel's t/Tmax plane), so there is no extra input. Parameters W [2·nFreq, channels] and b [channels] start at zero, so conditioning starts as a no-op.

                    • pairTile (seqLen inDim outDim : Nat) (pairIn : Nat := 0) : Layer

                      Pair tile, the distogram stem of AlphaFold 1 / trRosetta. The host feeds two blocks of seqLen per-residue feature rows of width inDim — the rows i and the rows j of a crop — flat as [B, 2 · seqLen · inDim]. Each block goes through its own weight inDim → outDim (W on the i block, Wj on the j block) and the two are outer-summed, y[b, c, i, j] = (W xᵢ)[c] + (Wj xⱼ)[c]: an NCHW map [B, outDim, seqLen, seqLen] for the conv stack that follows. The same function as a bias-free 1×1 conv on the outer concatenation [xᵢ ; xⱼ], without materializing it; no bias of its own, because the convBn that follows it subtracts any per-channel constant (its β is the bias — a pairTile bias had gradient exactly 0 in the FD check). Backward: the cotangent summed over j gives the W gradient by the dense rule, summed over i the Wj gradient; the features are host-side, so there is no input gradient. First layer only. With pairIn = K > 0 the host appends K pair planes [K, seqLen, seqLen] to the flat row ([B, 2 · seqLen · inDim + K · seqLen²]) — the language model's contact-head logits, a template distogram — and they are concatenated onto the pair map as its last K channels, [B, outDim + K, seqLen, seqLen]; constants of the step, so the backward reads the cotangent's first outDim channels and drops the rest.

                    Instances For
                      Equations
                      Instances For
                        @[instance_reducible]
                        Equations
                        structure NetSpec :

                        A reference-path architecture: a name, the layer list, the input size, and the per-net conventions (NetSpec.convBnAct, NetSpec.convPadStyle) the emitters read. Spec counts its parameters (NetSpec.totalParams) and validates it; NetSpec.train compiles and trains it.

                        • name : String
                        • layers : List Layer
                        • imageH : Nat
                        • imageW : Nat
                        • detStride : Nat

                          Total downsampling stride from input to the detection feature map, for YOLO-style specs: the grid is imageH / detStride. 32 for the standard ResNet stride-32 backbone (grid = 224/32 = 7, 448/32 = 14); set to 16 for a stride-16 tap (last block stride 1) to double the grid (448/16 = 28). Only consulted on the detection path; irrelevant to classifiers/seg.

                        • buildTag : String

                          Optional suffix on buildPrefix, so two runs of the same architecture under different training configs get their own params, MLIR, and vmfb instead of overwriting each other.

                          buildPrefix is derived from name alone, which is right — the name describes the architecture, and the loss is not part of the architecture. But it means a loss ablation is a set of runs that all claim the same .lake/build/<net>_params.bin and the same <net>_train_step.vmfb. Run them sequentially and each silently clobbers the last; run them concurrently, one per GPU, and they race on the vmfb mid-compile.

                          Set this to the arm's name and the ablation parallelizes cleanly. Empty (the default) leaves buildPrefix as name alone.

                        • convBnAct : Activation

                          Activation applied after a .convBn layer by the JAX emitter, per net (default ReLU); MlirCodegen refuses any value but ReLU (MlirCodegen.unsupported). MobileNetV2 is ReLU6 throughout (stem and head included) and EfficientNet-B0 SiLU/swish throughout, so their references set this to match the verified renders.

                          A ReLU/ReLU6 mismatch is invisible on small activations (the two agree below 6), so a forward tie at small weight scale passes with it present; it shows only under He-scaled weights. A passing tie is not evidence that the activations agree.

                          Scoped per net, not per layer, because every net in the kit uses one activation at all of its .convBn sites; a net that mixed them would need a Layer field instead.

                        • convPadStyle : PadStyle

                          How .same is spelled at a strided top-level .conv2d/.convBn, per net, in the JAX emitter. See PadStyle. Defaults to .xlaSame. The ResNet-family mains set .symmetric because torchvision's Conv2d(3,64,7,stride=2,padding=3) is the net their verified render implements.

                          It changes a strided top-level conv, which in the kit is the stem, and mbconv_block's strided depthwise (EfficientNet-B0's four, TF SAME since D3 on 2026-10-12). Block-internal convs elsewhere (basic_block_down, bottleneck_block_down, …) call the helpers without a padding argument and so take the symmetric default; the other mobile/TF-origin blocks pass 'SAME' or the symmetric pad explicitly inside their own helpers, untouched by this field.

                          Scoped per net rather than per layer for the same reason as convBnAct: no net in the kit mixes the two conventions across its own top-level convs.

                        Instances For
                          @[instance_reducible]
                          Equations
                          Equations
                          • One or more equations did not get rendered due to their size.
                          Instances For
                            inductive LossKind :

                            The "kind" of supervised loss the train step computes. Picks the label-tensor shape + the forward/backward formula. Modifiers like useFocal, labelSmoothing, and the aug flags (useMixup etc.) layer on top — LossKind only captures the primary loss shape.

                            Resolved once per run by TrainConfig.lossKindFor (an explicit TrainConfig.lossKind, else derived from the dataset and the soft-label augs); compileVmfbs validates the modifiers against it and drives the codegen flag set from it.

                            • classCE : LossKind

                              Default: int32 [B] class label, softmax cross-entropy. Compatible with useFocal (focal modifier) and labelSmoothing.

                            • softLabelCE : LossKind

                              Float [B, NC] soft labels (mixup/cutmix/knn-mixup output). Compatible with labelSmoothing (already baked in by the caller).

                            • perPixelCE : LossKind

                              Int32 [B, H, W] per-pixel label tensor (segmentation), softmax cross-entropy per pixel.

                            • perPixelDice : LossKind

                              Soft Dice over the softmax probabilities, int32 [B, H, W] labels — same ABI as perPixelCE, different loss block. Dice is computed per-class over the whole batch and meaned: 1 - mean_c (2·Σ p_c·y_c + ε) / (Σ p_c + Σ y_c + ε).

                              The point of it: per-pixel CE is a mean over pixels, so a class occupying 0.5% of pixels contributes 0.5% of the loss and the cheapest descent direction is to predict it away. Dice is a ratio per class, so every class carries equal weight no matter how few pixels it owns.

                              Batch-Dice (reducing over B as well as H,W) rather than per-sample: with rare classes and small batches a sample may contain none of a class at all, which makes per-sample Dice for it degenerate.

                            • perPixelDiceCE : LossKind

                              perPixelDice + perPixelCE, summed (loss and gradient both). The standard medical-segmentation default: Dice supplies the class-balanced signal, CE regularizes it (pure Dice has a noisy gradient early, when the softmax is near-uniform and every denominator is large).

                            • perPixelWeightedCE (weights : List Float) : LossKind

                              Class-weighted per-pixel softmax CE: weights[c] scales the loss and the gradient of every pixel whose true class is c. Same ABI as perPixelCE; weights.length must equal the class count.

                              This is the lever perPixelDice was supposed to be and isn't. Dice's gradient carries a factor of p_i from the softmax Jacobian, so it vanishes exactly where a collapsed class needs rescuing: on BraTS it falls to a small fraction of CE's gradient once the collapsed class's p is tiny (scripts/probes/seg_dice_vanishing_grad_probe.py measures it). CE's seed is (p - y)/N, which is -1/N at p = 0: flat, and wholly indifferent to the collapse. Scaling that by w_c therefore keeps a live signal all the way down, which Dice cannot do.

                              Reduction is the weighted mean Σ_k w_{y_k}·CE_k / Σ_k w_{y_k} (torch's CrossEntropyLoss(weight=…, reduction='mean') semantics), not /N. Two reasons, both practical: the loss stays on the same scale as unweighted CE so the arms of an ablation are readable against each other, and — since both sums are linear in w — the loss is invariant to the overall scale of weights. Only the ratios matter, so a caller cannot accidentally change the effective learning rate by normalizing its weight vector differently. Σ_k w_{y_k} depends only on the labels, so it is a constant w.r.t. the logits and contributes no gradient term.

                            • perPixelMultiCE (weights : List (List Float)) : LossKind

                              K per-pixel softmax heads over one [B, Σ_k NC_k, H, W] output, each with its own class weights (weights[k].length = NC_k): the loss is the sum of the K weighted per-pixel CEs (each normalized by its own Σw) and the gradient their concatenation along the channel axis. The int32 [B, H, W] label carries the K labels mixed-radix, y = y₀ + NC₀·(y₁ + NC₁·(y₂ + …)), so the ABI is perPixelCE's. One head is exactly perPixelWeightedCE. The distogram demo's distance + orientation heads (trRosetta's ω, θ, φ) are the use.

                            • perPixelFocalCE (gamma : Float) : LossKind

                              Per-pixel focal CE (Lin et al., RetinaNet): -(1-p_t)^γ · log p_t, meaned over pixels. Same ABI as perPixelCE. γ = 0 is exactly perPixelCE.

                              The third distinct answer to the imbalance, and mechanically the opposite of perPixelWeightedCE — worth stating, because "focal and class weights both reweight the loss" hides the whole point:

                              • weighted CE amplifies the rare class. A static, per-class factor from the label frequencies.
                              • focal suppresses the easy class. A dynamic, per-pixel factor from the current prediction. At p_t → 1 (confident background, most of BraTS) the (1-p_t)^γ factor crushes the gradient toward 0. At p_t → 0 it tends to 1 and the gradient tends to CE's: focal does not amplify the collapsed class, it defunds the majority drowning it out.

                              So focal needs no frequency statistics and cannot be mis-tuned by a bad weight vector, but it also cannot help a class that is rare and easy.

                              α is deliberately omitted. The paper's α_t is a per-class weight, i.e. exactly perPixelWeightedCE's mechanism — folding it in here would confound the two arms of the very ablation this exists for. Compose them later, once each is understood alone.

                              NB the gradient here is the true one, including the derivative of the (1-p_t)^γ factor. Contrast the YOLOv1 objectness path, which detaches that weight (MlirCodegen.lean, %y1f_w0) — a defensible approximation, but one whose d_logits is not the derivative of any loss it states, and so could not be FD-verified the way this is.

                            • floatTargetMse : LossKind

                              Float [B, C, H, W] target tensor with per-pixel MSE (DDPM, autoencoder regression). Caller passes ddpmOutShape.

                            • yolov1Masked : LossKind

                              YOLOv1: float [B, perCell, gridH, gridW] target + float [B, gridH, gridW] per-cell mask. 5-term masked MSE with √ ε-floor on the box-dim terms.

                            • bce : LossKind

                              Binary cross-entropy with logits over multi-hot [B, NC] targets — timm "ResNet Strikes Back" RSB-A2's loss. Each class is an independent sigmoid; the mixup/cutmix soft-label path produces the [B,NC] target directly (hard labels are one-hot'd, with optional label smoothing). JAX-only: compileVmfbs rejects it on the MLIR path. Reduction is timm's mean over B×C.

                            Instances For
                              Equations
                              Instances For
                                @[instance_reducible]
                                Equations
                                @[instance_reducible]
                                Equations
                                Equations
                                • One or more equations did not get rendered due to their size.
                                Instances For
                                  inductive SegLoss :

                                  Which loss block the segmentation path emits. All three share one ABI (int32 [B,H,W] labels, the trainStepAdamF32Seg dispatch, the mIoU eval harness) — only the emitted loss + gradient differ, so this rides alongside useSeg rather than replacing it.

                                  Instances For
                                    @[instance_reducible]
                                    Equations
                                    Equations
                                    Instances For
                                      @[instance_reducible]
                                      Equations

                                      Does this loss run on the segmentation path? Every per-pixel kind shares the int32 [B,H,W] label ABI and the seg train-step dispatch.

                                      compileVmfbs, NetSpec.train and runTraining all ask it of the one resolved kind (TrainConfig.lossKindFor).

                                      Equations
                                      Instances For

                                        Which seg loss block to emit. Non-seg kinds answer .ce and are never asked (the codegen only consults this under useSeg).

                                        Equations
                                        Instances For
                                          inductive OptimizerKind :

                                          Optimizer selector for the training loop (TrainConfig.optimizer). Both backends read it; the reference (MLIR) path emits .sgd and .adam and rejects the other two.

                                          • sgd : OptimizerKind

                                            Plain SGD, or SGD + heavy-ball momentum when TrainConfig.momentum > 0.

                                          • adam : OptimizerKind

                                            Adam / AdamW (decoupled weight decay when weightDecay > 0).

                                          • rmsprop : OptimizerKind

                                            RMSprop with momentum — the native MobileNetV2 / EfficientNet optimizer. v = ρ·v + (1-ρ)·g²; buf = μ·buf + g/(√v+ε); p -= lr·buf, with ρ = rmspropDecay, μ = momentum, ε = rmspropEps. Weight decay stays coupled into the gradient (the form those papers use), unlike AdamW.

                                          • lamb : OptimizerKind

                                            LAMB (You et al. 2019) — the large-batch optimizer in timm's "ResNet Strikes Back" RSB-A2 recipe. Adam moments (m, v, t) form the per-param direction r = m̂/(√v̂+ε) + λ·θ (DECOUPLED weight decay λ = weightDecay folded into the direction), then a layer-wise trust ratio ‖θ‖ / ‖r‖ rescales the step: θ -= lr · (‖θ‖/‖r‖) · r. The trust ratio is 1.0 wherever ‖θ‖ or ‖r‖ is 0 (timm convention). β1=0.9, β2=0.999, ε=1e-6. opt_state shape matches .adam: (m, v, t).

                                          Instances For
                                            @[instance_reducible]
                                            Equations
                                            inductive TrainResize :

                                            The kernel the ImageNet train crop is resized with (TrainConfig.trainResize), always antialiased, as PIL is.

                                            • bicubic : TrainResize

                                              Bicubic: DeiT's and ConvNeXt's --train-interpolation, and TF EfficientNet's.

                                            • bilinear : TrainResize

                                              Bilinear: torchvision RandomResizedCrop's default, the 2018 PyTorch-examples recipe.

                                            • random : TrainResize

                                              timm's random: bilinear or bicubic, a fair coin per image (train.py's default, which the RSB tiers and MobileNetV4 train under).

                                            Instances For
                                              @[instance_reducible]
                                              Equations
                                              @[instance_reducible]
                                              Equations
                                              Equations
                                              Instances For
                                                @[instance_reducible]
                                                Equations
                                                structure TrainConfig :

                                                One reference-path training recipe: learning rate, batch size and epochs, the optimizer (optimizer), the schedule, the loss (lossKind), augmentation, weight averaging, precision and the detector knobs. Read by NetSpec.train / NetSpec.runTraining and by the JAX emitter; each field's docstring says which side reads it where only one does. Defaults leave a knob off.

                                                • learningRate : Float
                                                • batchSize : Nat
                                                • epochs : Nat
                                                • seed : Nat
                                                • momentum : Float
                                                • optimizer : OptimizerKind

                                                  The optimizer. .rmsprop and .lamb are JAX-only: compileVmfbs rejects them.

                                                • rmspropDecay : Float

                                                  RMSprop running-mean-square decay ρ (only used when optimizer = .rmsprop).

                                                • rmspropEps : Float

                                                  RMSprop denominator ε — NOT 1e-8: MobileNetV2 uses 1.0, EfficientNet 1e-3; the large value is part of those recipes.

                                                • rmspropTf : Bool

                                                  TensorFlow 1's ApplyRMSProp (tf.train.RMSPropOptimizer, what TF-slim's train_image_classifier.py ran MobileNetV2 under): the learning rate multiplies the normalised gradient INSIDE the momentum term, buf = μ·buf + lr·g/√(s + ε), and the parameter step is p − buf with no rate of its own. Off keeps the emitted buf = μ·buf + g/√(s + ε), p − lr·buf, byte for byte — the same optimizer only while the rate is constant, a different one under a decaying schedule (imagenet_parity.md D13; Proofs.rmsPropTfStep_theta_lr_change). The verified peer is the rmstf renders.

                                                • weightDecay : Float
                                                • cosineDecay : Bool
                                                • warmupEpochs : Nat
                                                • minLR : Float

                                                  The cosine's floor, timm's lr_min (DeiT --min-lr 1e-5): lr = minLR + (LR − minLR)·½(1 + cos(π·prog)). ImageNet streaming path only; 0 emits the old formula byte for byte. The verified peer is trainAdamSched's minLR.

                                                • schedPerEpoch : Bool

                                                  timm's CosineLRScheduler as DeiT and the RSB tiers run it (t_in_epochs): the rate is set ONCE PER EPOCH, warmup is linear from warmupLRInit (timm --warmup-lr 1e-6) to the peak over warmupEpochs, and the cosine runs over ALL epochs counted from epoch 0 (warmup_prefix = False), floored at minLR. Off keeps the per-step warmup-then-cosine byte for byte. The verified peer is trainAdamSched's schedPerEpoch / warmupLRInit.

                                                • warmupLRInit : Float
                                                • cosineFromStep0 : Bool

                                                  tf-models' schedule, the MobileNetV4 paper's config: LinearWarmup over CosineDecay(decay_steps = total_steps) — the cosine counts EVERY step from step 0 (the warmup only overrides its first warmup_steps) and the warmup ramps linearly from 0 to the cosine's own value at its end, the step 0-based (the first step's rate is 0). The per-step form this emitter writes otherwise restarts the cosine at the end of warmup. Off keeps that form, byte for byte; per-step only (schedPerEpoch off), with minLR as the floor. The verified peer is trainAdamSched's cosineFromStep0.

                                                • augment : Bool
                                                • colorDistortSlim : Bool

                                                  TF-slim's inception_preprocessing.distort_color in fast_mode — what train_image_classifier.py ran (it passes no fast_mode, so the default True): after the crop, resize and flip, on the image in [0, 1], random_brightness(max_delta=32/255) then random_saturation(0.5, 1.5), in that one order (the four-ordering draw with hue and contrast is the non-fast mode, which MobileNetV2 did not run), then clip_by_value(0, 1). Emitted back in 0..255 for the shared normalize. Off emits nothing, byte for byte (imagenet_parity.md D12). imagenet (tfds) path only.

                                                • inputScalePm1 : Bool

                                                  TF-slim's input scaling, (x − 0.5) × 2 on [0, 1] — _MEAN_RGB = _STD_RGB = 127.5 — in place of the ImageNet mean / std, train and eval alike (preprocess_for_train and preprocess_for_eval both end with it). Off keeps the mean / std constants, byte for byte. imagenet (tfds) path only.

                                                • labelSmoothing : Float
                                                • useFocal : Bool

                                                  Focal loss (Lin et al. 2017): replace CE loss with -(1-p_y)^γ · log(p_y). Down-weights well-classified examples, up-weights hard ones. Restricted to the int-label path (no soft labels) and labelSmoothing must be 0 — focal mixes poorly with both. γ=2.0 is the paper default.

                                                • focalGamma : Float
                                                • useDiouBox : Bool

                                                  YOLOv1 box loss: false = the published √-MSE coord terms; true = a DIoU box loss on box0 with a positive box parameterization (cx=(j+σ(tx))/gW, w=exp(tw)). Only consulted on the .yolov1Masked path. NB: a DIoU-trained model must be decoded with the same σ/exp (scripts/demos/yolo_map_visdrone.py --box-param diou).

                                                • anchors : List (Float × Float)

                                                  YOLO anchor priors (w_rel, h_rel) for the anchor-based detector. Empty = single-box YOLOv1. When non-empty, the yolo loss routes to the A-anchor path (perCell = A·15, box_a = anchor_a·exp(pred)); the target/head must use the matching A·15-channel layout (preprocess_visdrone --anchors).

                                                • fpnScales : List (Nat × List (Float × Float))

                                                  FPN multi-scale detector. Per-scale (grid, anchors) for P3/P4/P5 (e.g. [(56, a3), (28, a4), (14, a5)]). Empty = not an FPN detector. When non-empty AND the spec ends in a .fpnDetect layer, the loss routes to emitMultiScaleYoloLoss over the [B, Ntot] head concat, and the single target input is a flat [B, Σ A·15·g_s²] block (sliced per scale in the loss).

                                                • fpnAffineScale : Float

                                                  Box-aware affine augmentation for the FPN path: per-image scale gain 1 + U(-1,1)·fpnAffineScale and translate U(-1,1)·fpnAffineTranslate (as a fraction of the frame), fired with probability fpnAffineProb. 0 scale gain and 0 probability (the defaults) leave the pipeline byte-identical to the HSV+hflip pack, so the existing arms stay reproducible.

                                                  Separate from augment on purpose. augment is the committed and measured pack (HSV + hflip, worth 0.1243 → 0.1674 at 50 epochs); this is the arm that changes object SCALE, which on VisDrone is the axis the difficulty actually lives on — and therefore the one that can hurt as easily as help, since a 2–5 px object scaled down goes under P3's stride-8 resolution entirely. It must be A/B'd under its own tag, on top of augment, not instead of it.

                                                • fpnAffineTranslate : Float
                                                • fpnAffineProb : Float
                                                • fpnAffineWhThrPx : Float

                                                  Drop a transformed box whose clipped side falls under fpnAffineWhThrPx pixels, or which keeps less than fpnAffineAreaThr of its area inside the frame. Ultralytics uses 2 px and 0.1; the 2 px default is wrong here, because a large share of VisDrone GT is 2–5 px to begin with and that threshold would silently delete the classes the detector is worst at.

                                                • fpnAffineAreaThr : Float
                                                • yoloClsWeights : List Float

                                                  Per-class weights for the detector's classification term. weights.length must equal the detector class count (10 for VisDrone). Empty (the default) is the unweighted path and emits byte-identical MLIR.

                                                  Motivated by measurement, not folklore: on the unweighted e12 checkpoint the class argmax collapsed onto the two most frequent classes (car and pedestrian), leaving classes never predicted and their per-class mAP near zero — see scripts/probes/fpn_obj_separation.py and scripts/probes/fpn_class_freq.py. Weights depend only on the target, so they are exactly constant w.r.t. the logits and the weighted gradient stays finite-difference checkable.

                                                  Normalize so Σ_c f_c·w_c = 1 (expected weight 1 under the GT class distribution) to keep this a pure redistribution — otherwise it silently rescales the class term against the box and objectness terms too.

                                                • yoloClsFocalGamma : Float

                                                  Focal γ on the CLASS term. 0 = plain (weighted) softmax-CE and emits byte-identical MLIR. 2.0 is the RetinaNet value.

                                                  FL = −w·(1−p_t)^γ·log p_t on assigned cells, p_t the softmax probability of the true class. Distinct from focalGamma, which is the OBJECTNESS focal and applies to every cell.

                                                  Why this lever rather than more class weighting: the FPN_CLSW ladder measured static per-class weights as net harmful (mAP none 0.1774 / sqrt 0.1771 / inv 0.1368) because a fixed constant raises rare-class recall by flooding those classes with false positives, and AP is precision-sensitive — tricycle detections went 7,419 → 31,322 against 1,045 GT. Focal down-weights EASY examples whatever their class, and the weight tracks p_t as it moves, so it cannot buy recall with a permanent precision tax.

                                                • headPriorBias : List Float

                                                  RetinaNet prior-bias init: initialize the head's bias to log π_c instead of zero, so the net starts predicting the class prior rather than a uniform distribution. Empty (the default) leaves the head at zero bias and changes nothing.

                                                  Orthogonal to lossKind — it is an init, not a loss — and deliberately so: it is the natural partner of .perPixelFocalCE, whose (1-p_t)^γ factor is a no-op at a uniform softmax because there is no confidence to suppress. Prior-bias init manufactures that confidence at step 0. See NetSpec.applyHeadPriorBias for the measured size of the effect.

                                                • detPriorPi : Float

                                                  RetinaNet prior-bias init for the FPN detector head: initialize every objectness bias to −log((1−π)/π) so the head starts at sigmoid = π. 0.0 (the default) leaves the head biases at zero, which reproduces the biasless head exactly. Typical value 0.01.

                                                  The sigmoid-head twin of headPriorBias above, and the lever the loss-breakdown measurements pointed at: objectness had signal but no dynamic range, because a bias-free 1×1 conv spends its weights manufacturing the constant background offset. See NetSpec.applyDetPriorBias.

                                                • useMixup : Bool

                                                  DeiT-style data augmentation knobs. Setting useMixup or useCutmix switches the train-step to the soft-label codegen path; the dataloader produces a [B, NC] smoothed soft-label tensor instead of an int32 [B] vector. mixupAlpha and cutmixAlpha control the Beta-distribution shape; the paper defaults are 0.8 and 1.0 respectively. randomErasing operates on the int-label path (no soft-label conversion needed).

                                                • mixupAlpha : Float
                                                • useCutmix : Bool
                                                • cutmixAlpha : Float
                                                • mixSwitchProb : Float

                                                  With both on, the per-batch choice between them: timm's Mixup(switch_prob = p) draws CutMix with probability p (DeiT and the RSB tiers: 0.5), seeded from (seed, step). 0 keeps the strict step-parity alternation byte for byte, on the trainer and on the shim's _mix alike.

                                                • useKnnMixup : Bool

                                                  KNN-Mixup: pair each sample with its nearest neighbor in the batch (pixel-space L2) rather than a random partner. Closer manifold mixing → harder, more realistic intermediate samples. Mutually exclusive with useMixup/useCutmix (KNN takes precedence).

                                                • knnMixupAlpha : Float
                                                • randomErasing : Bool
                                                • randomErasingProb : Float
                                                • erasingPixel : Bool

                                                  timm's RandomErasing(mode='pixel') in place of the zero-filled box: the box filled with N(0, 1) per pixel and channel on the normalised image, aspect log-uniform in [0.3, 1/0.3], up to 10 draws for a box that fits, and the box sized from the image actually erased (so a trainRes recipe erases its own crop). Off keeps the zero-fill emit byte-identical.

                                                • augBicubic : Bool

                                                  PIL-bicubic geometric augmentation: timm's ShearX/Y, TranslateX/Y and Rotate run PIL BICUBIC (the model's data config), and TF has no bicubic projective warp, so the emitter writes PIL's affine sampler out in the TF graph (a = −1, border clamp, truncation) in place of ImageProjectiveTransformV3(BILINEAR). Off keeps every generated file byte-identical.

                                                • cropTorchvision : Bool

                                                  torchvision's RandomResizedCrop.get_params (timm's RandomResizedCropAndInterpolation draws the same) in place of tf.image.sample_distorted_bounding_box: area U(0.08, 1) with no min_object_covered floor, aspect log-uniform on [3/4, 4/3], the first of 10 draws that fits, else a centre crop at the violated ratio bound. Off keeps every generated file byte-identical; the TF-slim and EfficientNet recipes (MobileNetV2, B0) train under TF's sampler and keep it. Gate: scripts/gates/crop_sampler_gate.py.

                                                • cropFallbackCenter : Bool

                                                  With TF's sampler: EfficientNet's fallback, the eval centre crop (224/256 of the shorter side) where the sampler returned the whole image, in place of the whole image.

                                                • cropMinCovered : Float

                                                  With TF's sampler: sample_distorted_bounding_box's min_object_covered. 0.1 is TF EfficientNet's and every existing recipe's; tf-models' random_crop_image passes its area_range[0] = 0.08 (the MobileNetV4 paper's config). The default keeps the 0.1 literal, byte for byte.

                                                • cropAreaMin : Float

                                                  With TF's sampler: area_range's floor. 0.08 is torchvision's and every existing recipe's; TF-slim's distorted_bounding_box_crop passes (0.05, 1.0). The default keeps the 0.08 literal, byte for byte.

                                                • cropMaxAttempts : Nat

                                                  With TF's sampler: max_attempts before the whole image is returned. 10 is every existing recipe's; TF-slim's distorted_bounding_box_crop passes 100. The default keeps the 10 literal, byte for byte.

                                                • cropAspectLo : Float

                                                  With TF's sampler: aspect_ratio_range as (lo, hi), when hi > 0. 0 keeps the (3./4, 4./3.) literals, byte for byte; TF-slim's distorted_bounding_box_crop passes (0.75, 1.33) — 1.33, not 4/3.

                                                • cropAspectHi : Float
                                                • resizeAntialias : Bool

                                                  antialias on the train crop's and the eval's tf.image.resize. On, the default, is PIL's behaviour and every existing recipe's (planning/archive/resize_eval_reconciliation.md measured close to a point of top-1 on RSB-A3 without it); off is tf.image.resize's own default, which tf-models' classification pipeline runs — the MobileNetV4 paper's config.

                                                • resizeLegacyTf1 : Bool

                                                  TF 1's resize kernels (tf.compat.v1.image.resize_bicubic under trainResize := .bicubic, resize_bilinear under .bilinear) on the train crop's resize and the eval resize: no antialias and half_pixel_centers=False — the sampling grid aligned to pixel corners, where tf.image.resize centres it on half pixels. The bicubic is TF TPU EfficientNet's _resize_image default; the bilinear is what TF-slim's tf.image.resize_images ran in the TF 1 that trained MobileNetV2. Off keeps tf.image.resize with resizeAntialias, byte by byte; .random (timm's coin) keeps it regardless.

                                                • trainResize : TrainResize

                                                  The train crop's resize kernel. .bicubic, the default, keeps every generated file byte-identical.

                                                • useRandAugment : Bool

                                                  RandAugment-Color (Cubuk et al. 2019, color subset). Applied per-image before mixup/cutmix, after crop/hflip. randAugmentN ops drawn uniformly from {identity, brightness, contrast, color, autocontrast} per image, each at magnitude randAugmentM (0–10, paper default 9). No labels touched.

                                                • randAugmentN : Nat
                                                • randAugmentM : Float
                                                • randAugmentGeometric : Bool

                                                  Upgrade useRandAugment from the color-only "lite" path to the full RandAugment(N, M) sampler over the color+GEOMETRIC op set (shear/rotate/ translate via ImageProjectiveTransformV3, shared with AutoAugment). This is what ConvNeXt's recipe wants. Only meaningful when useRandAugment is on; leaving it false keeps the back-compat color-lite path (e.g. ViT).

                                                • randAugmentMstd : Float

                                                  DeiT/ConvNeXt RandAugment refinements, meaningful only with the geometric sampler on. randAugmentMstd (timm mstd, DeiT uses 0.5) draws each op's magnitude from N(M, mstd) clipped to [0,10] instead of a fixed M. randAugmentInc (timm inc1) uses the increasing-severity magnitude→arg mappings: solarize/posterize flip so higher M = more distortion, and the enhancement ops center at 1.0 ± sign·scaled (random direction).

                                                • randAugmentInc : Bool
                                                • randAugmentProb : Float

                                                  Probability that each of the randAugmentN geometric RandAugment layers applies its op (timm AugmentOp's p, TF's prob_to_apply). 0.5 is timm's default and keeps every generated file byte-identical; MobileNetV4's paper recipe uses 0.7.

                                                • randAugmentFillMean : Bool

                                                  The geometric RandAugment ops fill the out-of-image region with timm's img_mean (round(255·mean) = (124, 116, 104)) instead of the paper code's grey 128. Needs augBicubic (the bilinear path takes a scalar fill). Off keeps the 128 byte for byte.

                                                • randAugmentIdentityOp : Bool

                                                  tf-models' RandAugment draws each layer's op from the op list PLUS an identity branch (maxval = len(available_ops) + 1 in _distort_common), so an op is applied with p / (n + 1) rather than timm's p / n. Off keeps timm's n-way draw, byte for byte.

                                                • randAugmentTranslateConst : Float

                                                  tf-models' translate_const: TranslateX/Y move by (m / 10) · translate_const PIXELS (100 in the MobileNetV4 paper's config, so ±150 px at m15) instead of timm's translate_pct 0.45 of the side. 0 keeps timm's form, byte for byte. Bilinear-path only (augBicubic off).

                                                • augNearest : Bool

                                                  tf-models' geometric ops resample NEAREST (transform(interpolation='nearest'), the wrap/unwrap fill 128) where this emitter's bilinear path resamples BILINEAR at the same fill. Off keeps BILINEAR, byte for byte. Bilinear-path only (augBicubic off).

                                                • useAutoAugment : Bool

                                                  AutoAugment, ImageNet learned policy (Cubuk et al. 2018) — the full 25 sub-policies, applied per-image after crop/hflip on the imagenet (tfds) path. Unlike useRandAugment (color subset only), this includes the GEOMETRIC ops (shear/rotate) via tf.raw_ops.ImageProjectiveTransformV3 (core TF, no tfa) plus the full color set (posterize/solarize/equalize/autocontrast/etc). Subsumes the color RandAugment, so leave useRandAugment off when this is on. EfficientNet's original recipe; no labels touched.

                                                • autoAugmentV0 : Bool

                                                  With useAutoAugment: TF TPU EfficientNet's policy v0 (= timm auto_augment_policy_v0, img_mean fill on the bicubic warps) in place of the default table. EfficientNet-B0 only.

                                                • autoAugmentTf : Bool

                                                  With autoAugmentV0: the policy as TF TPU EfficientNet's own autoaugment.py applies it — the sub-policies are the same 25, op for op; what moves is the geometry. 'TranslateY' in ABSOLUTE pixels, (m/10) · translate_const with distort_image_with_autoaugment's translate_const=250 (set randAugmentTranslateConst := 250; 225 px at the table's level 9), where timm's TranslateYRel moves 0.45 of the side; the geometric ops through contrib_image.{transform,rotate,translate}, whose default interpolation is NEAREST (augNearest, augBicubic off) at the wrap/unwrap fill 128 (timm: PIL bicubic at img_mean); Posterize through TF's own shift ops, which clamp a shift of 8 to 7 so 0 bits keeps the top bit (timm/PIL: a black image). The level mappings are already TF's (_RA_INC off). Off keeps timm's v0, byte for byte. EfficientNet-B0's ImageNet recipe.

                                                • repeatedAug : Nat

                                                  Repeated Augmentation (Hoffer et al. 2020; timm RASampler), RSB-A2's 3×. Each image contributes repeatedAug independently-augmented copies per epoch. On the tfds path this is a stream-level flat_map(repeat K) before the augment _pp, plus a re-shuffle so the copies spread across batches — an APPROXIMATION of timm's exact index-level RASampler. steps_per_epoch is unchanged, so an epoch sees ~1/K as many unique images ×K views, per the RSB recipe. 1 disables.

                                                • repeatedAugAdjacent : Bool

                                                  Keep the repeatedAug copies ADJACENT, as timm's RASampler places them (the K views of an image land in the same optimizer step): the re-shuffle after the repeat is dropped. Off keeps the spread-across-batches stream byte for byte.

                                                • trainRes : Nat

                                                  Train/test resolution split (RSB-A3): TRAIN at trainRes×trainRes, EVAL at the spec's imageH/imageW. 0 = no split (train and eval same resolution). The generated forward infers the square resolution from the flat input length, so the conv stack + global-avg-pool run at either size (A3 trains @160, tests @224 → about half the pixels per step). imagenet (tfds) path only.

                                                • testCropRatio : Float

                                                  Explicit test-time center-crop ratio (RSB-A3 uses 0.95). 0 = the default _IMG_SIZE/(_IMG_SIZE+32) ≈ 0.875 ratio. imagenet (tfds) path only.

                                                • imgSize : Nat

                                                  The tfds pipeline's _IMG_SIZE — the side images are trained AND evaluated at when trainRes is 0 — in place of the literal 224 every ImageNet spec is declared at (NetSpec.imageH). The generated forward infers the side from the flat length, so the spec stays shared across a net's recipes: MobileNetV4's full tier trains at the paper's 256 on the 224 spec. 0 keeps the 224 literal, byte for byte. imagenet (tfds) path only.

                                                • evalCropTf : Bool

                                                  The eval centre crop in tf-models' order: a square of _CROP_PCT × the SHORTER side cut out of the JPEG (center_crop_image_v2), then resized to _IMG_SIZE bilinear (resize_image) — the MobileNetV4 paper's own validation. Off keeps timm's form (resize the short side to _IMG_SIZE / _CROP_PCT bicubic, then crop), byte for byte. On, timm's form is still emitted as _imagenet_decode_center_crop_timm and the shim swaps it in under SHIM_EVAL_SIZE (timm's test protocol), as jax/scripts/eval_full50k.py PROTOCOL=timm does.

                                                • evalCropTfEfficientNet : Bool

                                                  With evalCropTf: TF TPU EfficientNet's _decode_and_center_crop arithmetic rather than tf-models' center_crop_image_v2 — the square's side int((224/(224+32)) · min(h, w)), truncated, its offsets ((h − side) + 1) // 2 (tf-models floors both), cut out of the JPEG and resized BICUBIC (_resize_image; legacy under resizeLegacyTf1) where tf-models resizes bilinear — the same arithmetic the train side's cropFallbackCenter uses. Off keeps tf-models' form, byte for byte.

                                                • evalCropSlim : Bool

                                                  With evalCropTf: TF-slim's inception_preprocessing.preprocess_for_eval, the validation MobileNetV2's README command scores through, in place of tf-models' form. tf.image.central_crop(image, 0.875) keeps the centred _CROP_PCT of BOTH sides — the offsets int(h · (1 − _CROP_PCT) / 2) in float64, the sizes h − 2 · offset — so the window has the JPEG's own aspect; then resize_bilinear takes it to _IMG_SIZE × _IMG_SIZE, which does not keep it (TF 1's kernel under resizeLegacyTf1). tf-models' form crops a square of the SHORTER side; timm's resizes first. Off keeps tf-models' form, byte for byte.

                                                • dropPath : Float

                                                  Stochastic depth (Huang et al. 2016): drop each residual block's branch with a probability that ramps linearly from 0 to dropPath across the network's residual blocks; surviving branches are scaled by 1/keep (inverted, so inference is drop-free). 0 disables. JAX path: threads a per-step RNG through forward, and drops at ConvNeXt, bottleneck, MBConv, UIB and transformer-encoder blocks.

                                                • wdExcludeNormBias : Bool

                                                  AdamW no_weight_decay exclusion (timm/DeiT): when true, decoupled weight decay skips 1-D params (all biases, LayerNorm γ/β, the CLS token) and the positional embedding, decaying only ≥2-D weight matrices. Matches the ViT/DeiT reference; off keeps the legacy decay-everything behavior for the other nets. AdamW path only.

                                                • wdExcludeDepthwise : Bool

                                                  With wdExcludeNormBias: no decay on the depthwise kernels either — TF-slim's mobilenet.training_scope puts weights_regularizer=None on slim.separable_conv2d, which is what expanded_conv builds its depthwise from, and l2_regularizer(weight_decay) on slim.conv2d (stem, expand / project 1×1s, head, logits). The _wd_mask leaf test is the kernel's shape, [C, 1, k, k]: rank 4, one input channel, more than one output. Off keeps the rank-only mask, byte for byte (imagenet_parity.md D11); the verified peer is rankWdDecaysNoDw (the dw marker).

                                                • wdDecayBiases : Bool

                                                  With wdExcludeNormBias: the biases DECAY — TF TPU EfficientNet's main.py adds weight_decay · Σ l2_loss(v) over every trainable variable whose name lacks 'batch_normalization', so BN γ/β alone are exempt and the squeeze-excite and classifier biases (and every kernel, the depthwise included) are decayed. The emitted _wd_mask is then STRUCTURAL rather than rank-based: a (W, γ, β) triple is a conv-BN site (γ, β off), a (W, b) pair decays both. Off keeps the rank-only mask, byte for byte. The verified peer is rankWdDecaysKeepBias (the wn marker).

                                                • valEveryEpochs : Nat

                                                  Validate every N epochs (plus always the final epoch) instead of every epoch. N ≤ 1 keeps every-epoch validation (byte-identical codegen). Cuts eval wall-time on large streaming datasets where the val pass is data-loading-bound (e.g. ImageNet, where every val pass rebuilds the tfds val pipeline). ImageNet-streaming main only.

                                                • gradAccumSteps : Nat

                                                  Gradient accumulation: run gradAccumSteps micro-batches of batchSize before each optimizer update, giving an EFFECTIVE batch of batchSize × gradAccumSteps at the peak-activation cost of ONE micro-batch. The reproducibility lever for large-batch recipes (e.g. RSB LAMB @ bs2048) on small GPUs: batchSize=512 × gradAccumSteps=4 on 4×16GB instead of an 8×A100 node. learningRate should target the EFFECTIVE batch. BatchNorm uses per-micro-batch (Ghost-BN) statistics — not identical to true large-batch BN, but a benign/beneficial variant at micro≥256. N ≤ 1 keeps the single-shot update (byte-identical codegen). ImageNet-streaming main.

                                                • useEMA : Bool

                                                  DeiT-style training-loop knobs that average weights for the eval checkpoint. Both can be on simultaneously; eval picks EMA when both are enabled. Storage cost: one extra nParams-sized buffer per knob; runtime cost is one F32.ema call per step (EMA) or per epoch (SWA), well below GPU step time.

                                                • emaDecay : Float
                                                • emaWarmup : Bool

                                                  The shadow's decay ramps in as min(decay, (1+t)/(10+t)) (TF's warmup-corrected EMA), so a short run's shadow does not hold its init; false is a CONSTANT decay from step 0, timm's ModelEma as DeiT scores it. The verified peer is trainAdamSched's emaWarmup.

                                                • useSWA : Bool
                                                • swaStartEpoch : Nat

                                                  First epoch (zero-indexed) that contributes to the SWA average. Typical recipe: 0.75 × epochs, e.g. epoch 60 of 80.

                                                • useSWAG : Bool

                                                  SWAG (Maddox et al. 2019): extends SWA with diagonal Σ_diag (via running mean of θ²) plus a low-rank component built from the last swagK per-epoch deviations from the SWA mean. At eval, sample swagSamples weight vectors from N(swaMean, ½Σ_diag + ½ Σ_low), run forward each, average logits. Requires useSWA=true.

                                                • swagK : Nat
                                                • swagSamples : Nat
                                                • useTTA : Bool

                                                  TTA (test-time augmentation): at periodic eval, run ttaSamples independently-augmented forwards per batch and average the logits. Augmentations are the same dataloader pipeline used for training (e.g. random crop + hflip for Imagenette), minus the soft-label ones (mixup/cutmix) which need the label. Eval-only — no train cost, M× eval cost.

                                                • ttaSamples : Nat
                                                • lossKind : Option LossKind

                                                  The loss, when set. Left at none, TrainConfig.lossKindFor derives it from the dataset (detection → .yolov1Masked, a per-pixel label record → .perPixelCE) and the soft-label augs (.softLabelCE), else .classCE. See LossKind.

                                                • bceTargetThresh : Option Float

                                                  .bce only: binarise the target at this threshold before the loss (timm --bce-target-thresh, BinaryCrossEntropy.target_threshold), so a mixed soft label counts as 1 where it exceeds t and 0 elsewhere. timm's RSB configuration sets 0.2.

                                                • bootstrapBackbone : Option (String × Nat)

                                                  Bootstrap from a pretrained backbone checkpoint. When set to some (paramsPath, prefixFloats), runTraining overwrites the first prefixFloats * 4 bytes of the He-init with bytes read from paramsPath. The companion <basename>_bn_stats.bin is auto-loaded too if present (the backbone's BN running stats must match the spec's BN layer count + sizes — true for YOLOv1 loading R34 weights since both have identical backbone layers).

                                                  Example for YOLOv1+R34: bootstrapBackbone := some (".lake/build/resnet_34_params.bin", 21284672).

                                                • bootstrapBackboneRange : Option (String × Nat × Nat × Nat)

                                                  Offset-aware bootstrap, as some (paramsPath, dstOffFloats, srcOffFloats, countFloats). Same job as bootstrapBackbone but for a spec whose FIRST layer differs from the checkpoint's, so the transferable weights are no longer a prefix — see NetSpec.patchInitWithPretrainedRange.

                                                  The motivating case is r34UnetBrats: a 4-modality MRI stem ([64,4,7,7], 12,544 floats) in front of an R34 body pretrained on 3-channel RGB ([64,3,7,7], 9,408 floats). The stem keeps its He-init — MRI is not RGB, so re-learning it is the correct behaviour, not a concession — and the remaining 21,275,264 floats of the backbone transfer intact.

                                                  Takes precedence over bootstrapBackbone when both are set. BN running stats follow the same rule as the prefix path: loaded only on an exact size match, zeros otherwise.

                                                • checkpointEveryNEpochs : Nat

                                                  Save intermediate {pfx}_params_e{N}.bin and {pfx}_bn_stats_e{N}.bin snapshots every checkpointEveryNEpochs epochs. 0 disables. Default 10 = align with the eval cadence so training can be killed mid-run and resumed from the most recent checkpoint (or borrowed for downstream tasks like YOLOv1 bootstrap — see bootstrapBackbone).

                                                • evalEveryNEpochs : Nat

                                                  Run the validation eval every N epochs (plus always on the final epoch). 0 means final-epoch only.

                                                  Default 10 suits a 100-epoch classifier; a 10-epoch ablation at the default gets one eval, at the very end. On the seg path the eval is the only instrument that can see a collapsed class — no scalar in the training log can — so set it to 1-2 for ablation arms; the eval is one forward pass over val.

                                                • bf16 : Bool

                                                  bf16 mixed precision on the JAX path: cast matmul operands (dense, attention QKV / scores / output, MLP, patch embed) to bfloat16, keeping master weights, LayerNorm, softmax, and conv in fp32 (convs follow bf16Conv).

                                                • bf16Conv : Bool

                                                  bf16 conv compute: additionally cast the standard conv path (conv2d / conv_bn, hence the full ResNet/VGG/CIFAR-CNN conv stack) to bfloat16, returning fp32. Independent of bf16 so a backend where bf16 convolution is slower (MIOpen) can keep convs in fp32 (default false) while CUDA/cuDNN can opt in. Only meaningful when bf16 := true. Depthwise/separable convs (MobileNet/EfficientNet) still stay fp32.

                                                • f32StemHead : Bool

                                                  Under bf16, keep the patch embed and the top-level .dense layers (on a ViT, the classifier head alone: the block Linears live inside transformerEncoder) in fp32. This is the verified renders' own carve-out, so the knob exists for a pair's ONE-variable claim: ViTRenderB keeps the head f32 in every net and the patch-embed f32 on ViT (bf16Conv := false, a measured cuDNN weight-grad limit), while this emitter's mm sent both through bf16. A plain @ at default precision, i.e. the same XLA dot the render's precision = [DEFAULT, DEFAULT] f32 dot_general lowers to. Off by default, so every other trainer is byte-identical.

                                                • evalF32 : Bool

                                                  evaluate() scores through an f32 forward, as the verified path does and as timm scores: mm / convdt read a module-level dtype that the evaluator swaps for the duration of its once-traced eval_batch. Off keeps the bf16 training forward at eval, byte for byte.

                                                • runningBN : Bool

                                                  Running batch-norm statistics. When true, the JAX imagenet trainer tracks per-BN-layer running mean/var (EMA of batch stats, momentum bnMomentum) threaded through forward as has_aux, and EVAL normalizes with the running stats instead of the eval batch's own — the paper-faithful behaviour. Off (default) keeps the current batch-stats-at-eval path. Wired for convBn, residual, bottleneck, inverted-residual, MBConv, UIB and fused-MBConv blocks.

                                                • bnMomentum : Float

                                                  BatchNorm running-statistic decay, i.e. the weight on the OLD estimate: running = bnMomentum·running + (1−bnMomentum)·batch. Only consulted when runningBN is on (it is an eval-time statistic and touches no gradient).

                                                  This is the TensorFlow convention, the reciprocal of PyTorch's. torch.nn.BatchNorm2d(momentum=m) weights the NEW batch by m, so timm's PyTorch default momentum = 0.1 is bnMomentum = 0.9 here — a 10-step-vs-100-step averaging window, not a 10× smaller one. Getting the sense backwards silently makes the running stats 10× noisier, and it is eval-only, so no loss curve moves.

                                                  Per-net, because the nets chase different references (checked against the pinned timm 1.0.28, create_model(...).modules()):

                                                  netreferencevalue
                                                  R50 / R34timm (RSB) — momentum=0.10.9
                                                  EfficientNet-B0the TF paper (77.1/93.3) — TF's decay=0.990.99
                                                  MobileNetV2the TF-slim paper (72.0) — decay=0.9970.997
                                                  MNv4the repo's own 100-epoch JAX run, not a paper0.99

                                                  timm itself runs 0.9 on every one of those nets, tf_efficientnet_b0 included: BN_MOMENTUM_TF_DEFAULT exists in _efficientnet_builder.py but get_bn_args_tf() has no caller in 1.0.28, so the tf_* ports inherit only bn_eps = 1e-3. So "0.99 is right for EfficientNet" is a statement about the original TF codebase, not about timm — flip a net to 0.9 only along with the reference it is being scored against.

                                                  The default 0.99 keeps every net that does not set it byte-identical, on both the JAX emitter (Jax/Codegen.lean's _bn) and the verified host-side EMA (Verified.Train's bnMom). Under gradient accumulation both sides compensate to bnMomentum^(1/K) per micro-batch.

                                                • bnEps : Float

                                                  BatchNorm ε — _bn's eps, on the running-BN path (runningBN). 1e-5 is PyTorch's and timm's default; the TF papers (MobileNetV2 in slim, EfficientNet) use 1e-3. The default keeps the emitted eps=1e-5 literal, so every net that does not set it is byte-identical. The verified peer is the render's epsStr, a different artifact per ε (the eps<d…> variant marker and its own _fwd_eval_eps<d…> eval graph), not a host-side knob.

                                                • adamEps : Float

                                                  AdamW's ε, the 1e-8 in the emitted update (jnp.sqrt(vi) + 1e-8): PyTorch's and timm's default. Keras' is 1e-7, which the MobileNetV4 paper's config inherits (Table 10). The default keeps the 1e-8 literal, byte for byte. The verified peer is the render's adamEps (MobileNetV4RenderB), a different artifact per ε.

                                                • lnEps : Float

                                                  LayerNorm ε of the transformer layer_norm (ViT Ti/S/B; ConvNeXt's two LayerNorms are separate helpers, already at 1e-6). 1e-5 is PyTorch's nn.LayerNorm default and every existing trainer's; DeiT builds its blocks with partial(nn.LayerNorm, eps=1e-6). The verified peer is the render's eps (ViTRenderB, variant marker eps0000001).

                                                • geluExact : Bool

                                                  The exact GELU x · Φ(x) — jax.nn.gelu(x, approximate=False), which is PyTorch's nn.GELU and what DeiT and ConvNeXt train under — in place of jax.nn.gelu's default, the tanh approximation, at every GELU the emitter writes (the transformer MLP, the ConvNeXt block, a .gelu activation). The two differ by up to 4.7e-4. Off keeps every generated file byte-identical. The verified peer is the render's GeluForm, .erf for this flag: a different artifact per form, not a host-side knob.

                                                • dropPathOverN : Bool

                                                  Stochastic-depth ramp over i/N (TF EfficientNet's drop_rate · idx / len(blocks)) instead of i/(N−1) (timm's linspace(0, rate, depth)), on the MBConv path only. Off keeps every generated file byte-identical.

                                                • dropPathAllBlocks : Bool

                                                  tf-models' stochastic-depth ramp (MobileNet._mobilenet_base with flat_stochastic_depth_drop_rate: false): rate · (block_idx + 1) / num_blocks over EVERY entry of the block list — the stem, the fused stage, the UIBs, the head convs; not the pool, not the classifier — so MobileNetV4-Conv-M's UIB i (entry i + 2 of 25) drops at 0.075 · (i + 3) / 25, where timm's linspace gives i / 20. Applied only where the block skips, as both do. On the UIB path only. Off keeps i / (N − 1), byte for byte.

                                                • vitInit : Bool

                                                  timm/DeiT ViT weight init, replacing the generic Xavier-uniform for transformer-shaped nets. Off by default so every existing run is byte-identical; turn it on per-recipe.

                                                  timm's init_weights_vit_timm gives every nn.Linear (QKV, attn-out, MLP fc1/fc2, and the classifier head) trunc_normal_(std=0.02) with zero bias, and leaves the patch-embed nn.Conv2d on PyTorch's default kaiming_uniform_(a=sqrt(5)), i.e. U(±1/sqrt(fan_in)).

                                                  NB trunc_normal_'s default bounds a=-2, b=2 are ABSOLUTE, so at std=0.02 they sit at ±100σ and the truncation never fires — it is plain normal(0, 0.02). Emitted as such, matching the CLS-token and positional-embedding init already in the emitter.

                                                  Why it matters: Xavier scales as 1/sqrt(dim) against a fixed 0.02, so the generic path is 1.8× too wide at ViT-B, 2.6× at ViT-S and 3.6× at ViT-Ti, while the patch embed is ~6× too NARROW (it divides by the output fan dim·p·p rather than the input fan ic·p·p).

                                                • cnxInit : Bool

                                                  ConvNeXt paper weight init, replacing the generic Xavier-uniform for the ConvNeXt-shaped nets. Off by default; turn it on per-recipe.

                                                  ConvNeXt's reference implementation applies trunc_normal_(std=.02) with zero bias to every nn.Conv2d AND nn.Linear (_init_weights in facebookresearch/ConvNeXt, and timm's convnext.py agrees), leaving the LayerNorms at (1, 0) and LayerScale at 1e-6 — both of which the emitter already gets right.

                                                  This is a separate flag from vitInit, not a shared "timmInit". The two specs disagree on the conv path: ViT leaves its patch-embed nn.Conv2d on PyTorch's default U(±1/sqrt(fan_in)), while ConvNeXt trunc-normals its convs like everything else. One boolean cannot express both, and a flag named for the vendor rather than the distribution would invite exactly the misapplication below.

                                                  Neither flag applies to the ResNet/MobileNet/EfficientNet family. "timm init" is not one thing: those nets use kaiming_normal_(mode='fan_out', nonlinearity='relu'), and the emitter's emitConvBnInit — uniform ±sqrt(6/(oc·k²)), i.e. std sqrt(2/fan_out) — is ALREADY on that scale, differing only in distribution shape. Giving a ResNet conv trunc_normal(0.02) would be a regression, not a fix.

                                                  Why it matters: the generic emitConvBiasInit is Xavier over ic·kh·kw + oc, so ConvNeXt-T's stem lands at std 0.118 against the paper's 0.02 — 5.9× too wide, the same failure vitInit fixes for ViT.

                                                • initTruncNormal : Float

                                                  TF-slim's truncated_normal_initializer(stddev) on EVERY kernel — mobilenet.training_scope sets one weights_initializer for slim.conv2d, slim.separable_conv2d (the depthwise) and slim.fully_connected, so the stem, the 1×1s, the depthwise kernels and the logits all start as truncated_normal(-2, 2) · σ (0.09 for MobileNetV2, "if negative uses xavier"), biases at 0. Replaces the fan rules on every conv-BN kernel and the dense head; 0 keeps them, byte for byte. The verified peer is VerifiedConfig.truncNormalSigma (variance-matched).

                                                • initHeadTfUniform : Bool

                                                  TF TPU EfficientNet's dense_kernel_initializer on the classifier (efficientnet_model.py): U(±1/√shape[1]) on the Keras (in, units) kernel, so the range is set by the CLASS count — U(±1/√1000) at ImageNet, variance 1/3000 against Glorot's 2/2280 (imagenet_parity.md D6); the bias is Keras' zeros. Off keeps Glorot, byte for byte. The verified peer is VerifiedConfig.headTfUniform.

                                                • expLRDecayRate : Float

                                                  Exponential LR decay, the EfficientNet/MobileNet schedule: after warmup, lr = LR · rate^((epoch − warmup) / decayEpochs). 0 = off (use cosine). EfficientNet: rate 0.97, decayEpochs 2.4; MobileNetV2: rate 0.98, decayEpochs 1.0. Selected over cosine when > 0.

                                                • expLRDecayEpochs : Float
                                                • expLRStaircase : Bool

                                                  The exponential schedule as TF builds it — exponential_decay(staircase=True) on the GLOBAL step, rate^⌊epoch / decayEpochs⌋, with any warmup overriding it only while it runs (TF EfficientNet's build_learning_rate) — instead of the continuous exponent counted from the end of warmup. Only read when expLRDecayRate > 0. Off keeps every net's generated file byte-identical.

                                                • dropout : Float

                                                  Classifier dropout: dropout rate applied before the final dense head during training (inverted, scaled by 1/keep so eval is drop-free). 0 = off. EfficientNet-B0 / MobileNetV2 use 0.2. Threaded via the same drop_key as stochastic depth; requires the running-BN training flag (or drop_key≠None) to gate train-vs-eval.

                                                • gradClipNorm : Float

                                                  Clip gradients by global L2 norm before the optimizer step. 0 = off. DeiT default 1.0 — essential for stable ViT-from-scratch training: it lets you use the proper ~1e-3 LR without the collapse-to-chance seen at higher LR with no clipping.

                                                • headLrMult : Float

                                                  Per-group LR multiplier for the (from-scratch) dense head, relative to the base LR used by the pretrained conv backbone. 1.0 = uniform LR. Used for bootstrap fine-tuning where the He-init head must learn input- dependence far faster than the backbone should drift — a single LR can't do both (head under-trains → collapse-to-marginal; raise it globally and the backbone destabilizes). e.g. YOLOv1 detection uses ~10.

                                                Instances For
                                                  @[instance_reducible]
                                                  Equations
                                                  inductive DatasetKind :

                                                  Which on-disk dataset a reference-path run reads, and so its record layout, image size, normalization and label format.

                                                  • mnist : DatasetKind
                                                  • cifar10 : DatasetKind
                                                  • imagenette : DatasetKind
                                                  • imagenet : DatasetKind
                                                  • detection : DatasetKind

                                                    Box detection (VisDrone, NEU-DET). Images are ImageNet-normalized on Lean read; the labels carry the detector target, whose layout the run's config picks: the YOLOv1 target + per-cell mask + box tail at 224/7×7, the same at another grid (loadDetBinDims), per-anchor targets (cfg.anchors), or the flat FPN block (cfg.fpnScales). See scripts/datasets/preprocess_visdrone.py for the on-disk format.

                                                  • brats : DatasetKind

                                                    Brain-tumour segmentation on the Medical Segmentation Decathlon Task01_BrainTumour volumes (BraTS-derived). 2D axial slices: images are 240×240×4 (FLAIR / T1w / T1gd / T2w modalities as channels, z-scored per volume over brain voxels — no ImageNet normalization), labels are 240×240 uint8 per-pixel classes (0=background, 1=edema, 2=non-enhancing tumour, 3=enhancing tumour). Segmentation kind: labelBytesPerRecord = 240*240 selects .perPixelCE automatically. See scripts/datasets/preprocess_brats.py for the on-disk format.

                                                  • brats224 : DatasetKind

                                                    The same BraTS data at 224×224, produced by scripts/datasets/preprocess_brats.py --size 224 (a center crop, not a resize — see fit_plane). Identical in every other respect: same 4 modalities, same patient split at seed 0, same slice selection, same 14,415/2,569 counts.

                                                    224 exists for backbones with a /32 total stride, which 240 cannot serve (240/32 = 7.5). It is also ResNet-34's native ImageNet resolution. The crop is lossless for this dataset: across 4,000 sampled slices, the 8-pixel border it removes contains zero tumour voxels and zero brain voxels — MSD's volumes are skull-stripped and centered, so the margin is pure background. Used by demos/MainUnetBratsR34.lean.

                                                  • brats224Ctx (ctx : Nat) : DatasetKind

                                                    brats224 in 2.5D: the same slices, same split, same order, each record carrying the ctx axial neighbours on either side of its slice as extra channels — 4·(2·ctx+1) channels, the centre slice's four modalities at 4·ctx .. 4·ctx+3, neighbours replicated at the volume's ends. Produced by preprocess_brats.py --size 224 --seed 0 --context ctx into data/brats224c<2·ctx+1>/. The mask is the centre slice's, so the net still predicts one slice; the through-plane context is what a 2D slice model cannot see and a 3D one would, at a loader's cost rather than a rank-5 codegen's.

                                                  Instances For
                                                    Equations
                                                    Instances For
                                                      @[instance_reducible]
                                                      Equations
                                                      @[instance_reducible]
                                                      Equations

                                                      Does this dataset carry a per-pixel label record (a segmentation mask) rather than an int32 class? Pinned against datasetIO's label sizes by a #guard beside it.

                                                      Equations
                                                      Instances For

                                                        Image channels a BraTS record carries: the 4 modalities, times the 2·ctx+1 slices of a 2.5D build.

                                                        Equations
                                                        Instances For

                                                          The directory preprocess_brats.py writes each BraTS kind to by convention, and the trainer's and predictor's default.

                                                          Equations
                                                          Instances For

                                                            The loss a run trains with: cfg.lossKind when set, else derived from the dataset (detection → .yolov1Masked, per-pixel labels → .perPixelCE) and the soft-label augs (.softLabelCE), else .classCE. compileVmfbs, NetSpec.runTraining and NetSpec.train all resolve it here.

                                                            Equations
                                                            • One or more equations did not get rendered due to their size.
                                                            Instances For
                                                              def ireeCompileArgs (mlirPath outPath : String) :

                                                              IREE compile flags from environment. Defaults to CUDA (sm_86). Set IREE_BACKEND=rocm and IREE_CHIP=gfx1100 for AMD GPUs. Set IREE_BACKEND=llvm-cpu for CPU fallback (no chip needed).

                                                              Equations
                                                              • One or more equations did not get rendered due to their size.
                                                              Instances For

                                                                The iree-compile to run: .venv/bin/iree-compile when present (local dev), else the one on PATH (Docker, system install). Every compile in the repo resolves its compiler here.

                                                                Equations
                                                                Instances For
                                                                  def compileCheckB (name body : String) (stderrTake : Nat := 3000) :

                                                                  Compile one MLIR module and report. Writes body to .lake/build/{name}.mlir, runs iree-compile on it, prints the verdict, and returns true on success. stderrTake bounds how much of iree-compile's stderr is echoed.

                                                                  Equations
                                                                  • One or more equations did not get rendered due to their size.
                                                                  Instances For
                                                                    def compileCheck (name body : String) (stderrTake : Nat := 3000) :

                                                                    compileCheckB with the verdict discarded — the shape most gates want.

                                                                    Equations
                                                                    Instances For
                                                                      def tryCompile (src dst label : String) (stderrTake : Nat := 3000) :

                                                                      Compile src → dst, tolerating an absent compiler. Unlike compileCheck this takes an MLIR file that already exists and never throws: a missing iree-compile is reported and stepped over, so a smoke gate still runs on a machine without IREE installed.

                                                                      Equations
                                                                      • One or more equations did not get rendered due to their size.
                                                                      Instances For