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 #
- Ramachandran, Zoph, Le 2017, Searching for Activation Functions (Swish). https://arxiv.org/abs/1710.05941
- Glorot & Bengio 2010, Understanding the difficulty of training deep feedforward neural networks. https://proceedings.mlr.press/v9/glorot10a.html
- Milletari, Navab, Ahmadi 2016, V-Net (soft Dice loss). https://arxiv.org/abs/1606.04797
- Izmailov et al. 2018, Averaging Weights Leads to Wider Optima (SWA). https://arxiv.org/abs/1803.05407
- Szegedy et al. 2016, Rethinking the Inception Architecture for Computer Vision (§7 label smoothing). https://arxiv.org/abs/1512.00567
- Zhang et al. 2018, mixup: Beyond Empirical Risk Minimization. https://arxiv.org/abs/1710.09412
- Yun et al. 2019, CutMix. https://arxiv.org/abs/1905.04899
- Zhong et al. 2020, Random Erasing Data Augmentation. https://arxiv.org/abs/1708.04896
- Loshchilov & Hutter 2017, SGDR (cosine schedule). https://arxiv.org/abs/1608.03983
- Goyal et al. 2017, Accurate, Large Minibatch SGD (linear warmup / lr scaling). https://arxiv.org/abs/1706.02677
- Zheng et al. 2020, Distance-IoU Loss. https://arxiv.org/abs/1911.08287
Pointwise activation carried by a layer: ReLU, ReLU6, identity, swish (SiLU), hard-swish, GELU.
- relu : Activation
- relu6 : Activation
- identity : Activation
- swish : Activation
- hSwish : Activation
- gelu : Activation
Instances For
Equations
- instReprActivation = { reprPrec := instReprActivation.repr }
Equations
- instReprActivation.repr Activation.relu prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Activation.relu")).group prec✝
- instReprActivation.repr Activation.relu6 prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Activation.relu6")).group prec✝
- instReprActivation.repr Activation.identity prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Activation.identity")).group prec✝
- instReprActivation.repr Activation.swish prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Activation.swish")).group prec✝
- instReprActivation.repr Activation.hSwish prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Activation.hSwish")).group prec✝
- instReprActivation.repr Activation.gelu prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Activation.gelu")).group prec✝
Instances For
Equations
- instBEqActivation = { beq := instBEqActivation.beq }
Equations
- instBEqActivation.beq x✝ y✝ = (x✝.ctorIdx == y✝.ctorIdx)
Instances For
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).
- bn : Normalization
- ln : Normalization
Instances For
Equations
- instReprNormalization = { reprPrec := instReprNormalization.repr }
Equations
- instReprNormalization.repr Normalization.bn prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Normalization.bn")).group prec✝
- instReprNormalization.repr Normalization.ln prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Normalization.ln")).group prec✝
Instances For
Equations
Equations
- instBEqNormalization.beq x✝ y✝ = (x✝.ctorIdx == y✝.ctorIdx)
Instances For
Equations
- instReprPadding.repr Padding.same prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Padding.same")).group prec✝
- instReprPadding.repr Padding.valid prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Padding.valid")).group prec✝
Instances For
Equations
- instReprPadding = { reprPrec := instReprPadding.repr }
Equations
- instBEqPadding = { beq := instBEqPadding.beq }
Equations
- instBEqPadding.beq x✝ y✝ = (x✝.ctorIdx == y✝.ctorIdx)
Instances For
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)//2on 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
Equations
- instReprPadStyle = { reprPrec := instReprPadStyle.repr }
Equations
- instReprPadStyle.repr PadStyle.xlaSame prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "PadStyle.xlaSame")).group prec✝
- instReprPadStyle.repr PadStyle.symmetric prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "PadStyle.symmetric")).group prec✝
Instances For
Equations
- instBEqPadStyle.beq x✝ y✝ = (x✝.ctorIdx == y✝.ctorIdx)
Instances For
Equations
- instBEqPadStyle = { beq := instBEqPadStyle.beq }
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 → occonvolution,kSize × kSize, stride 1, with bias, followed byact. The JAX emitter readspad;MlirCodegenpads SAME and refuses.valid(MlirCodegen.unsupported). - convBn
(ic oc kSize stride : Nat)
(pad : Padding)
: Layer
ic → occonvolution without bias,kSize × kSizeatstride, then BatchNorm and an activation: the net'sNetSpec.convBnAct.MlirCodegenemits ReLU and refuses any otherconvBnAct(MlirCodegen.unsupported).padas forconv2d. - maxPool
(size stride : Nat)
: Layer
size × sizemax-pool atstride(SAME-padded inMlirCodegen). - 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 overdim, then the per-feature affineγ ⊙ x̂ + β(2·dim params). Not the channels-first LN insideconvNextStage/convNextDownsample— this is the rank-2 head-side one, which sits between two layers and so cannot be anorm :=field on either of them; ConvNeXt'sGAP → LN → Linearhead uses it. Lowered by the JAX emitter;MlirCodegenhas no lowering for it. - dense
(fanIn fanOut : Nat)
(act : Activation)
: Layer
Fully-connected
fanIn → fanOutwith bias, followed byact. - residualBlock
(ic oc nBlocks firstStride : Nat)
: Layer
nBlocksResNet basic blocksic → oc(two 3×3 conv-BN each); the first runs atfirstStrideand carries a 1×1 conv-BN projection whenic ≠ ocor the stride is not 1. - bottleneckBlock
(ic oc nBlocks firstStride : Nat)
: Layer
nBlocksResNet bottleneck blocksic → oc(1×1, 3×3, 1×1 conv-BN at widthoc / 4); the first runs atfirstStrideand carries a 1×1 conv-BN projection whenic ≠ ocor 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;MlirCodegenhas no case for it. - invertedResidual
(ic oc expand stride nBlocks : Nat)
: Layer
nBlocksMobileNetV2 inverted residualsic → oc: 1×1 expand conv-BN toexpand ×the block input (omitted whenexpand = 1), 3×3 depthwise conv-BN, 1×1 project conv-BN; the first block runs atstride. - mbConv
(ic oc expand kSize stride nBlocks : Nat)
(useSE : Bool)
(act : Activation := Activation.swish)
: Layer
nBlocksEfficientNet MBConv blocksic → oc: 1×1 expand (omitted whenexpand = 1),kSize × kSizedepthwise, optional squeeze-excitation (squeeze widthmbConvSeMidof the block input), 1×1 project, each conv with BN; activationact, the first block atstride. - mbConvV3
(ic oc expandCh kSize stride : Nat)
(useSE : Bool)
(act : Activation := Activation.relu)
: Layer
One MobileNetV3 block
ic → ocwith an explicit expanded widthexpandCh(the expand conv is omitted whenexpandCh = ic),kSize × kSizedepthwise, optional SE, 1×1 project; activationact. - fusedMbConv
(ic oc expand kSize stride nBlocks : Nat)
(useSE : Bool)
(act : Activation := Activation.swish)
: Layer
nBlocksFused-MBConv blocksic → oc: a fullkSize × kSizeexpand conv-BN (straight toocwhenexpand = 1), optional SE, and a 1×1 project conv-BN whenexpand ≠ 1. - uib
(ic oc expand stride preDWk postDWk : Nat)
: Layer
MobileNetV4 Universal Inverted Bottleneck
ic → oc: optionalpreDWk × preDWkdepthwise, 1×1 expand toic · expand, optionalpostDWk × postDWkdepthwise, 1×1 project, each conv with BN.k = 0omits 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;MlirCodegenhas no case for it. - patchEmbed
(ic dim patchSize nPatches : Nat)
: Layer
ViT patch embedding: a
patchSize × patchSizestride-patchSizeconvic → dimwith 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
nBlockspre-LN transformer encoder blocks (LN, multi-head attention with Q/K/V/O denses, LN,dim → mlpDim → dimMLP), plus a final LN.causalMaskadds a triangular −∞ mask to theQKᵀscores before softmax (TinyGPT sets it; vision transformers leave it false).keepSequenceskips 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 aspatialUnflatten).flashAttnemits the attention as a tiled online-softmaxstablehlo.whileinstead of the dense[B,H,T,T]scores —O(T·blk)rather thanO(T²)memory, same math. Used by the train step's forward and backward; the eval forward stays dense.ropeapplies 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 withtokenPositionEmbed'sposEmb := false.
- mambaBlock
(dim stateSize expand nBlocks : Nat)
: Layer
Selective state-space block (Mamba / S6);
dimhidden,stateSize= N,expandthe inner-dim multiplier. Shape-only (its count inLayer.nParamsUntrainedis approximate). - swinStage
(dim heads mlpDim windowSize nBlocks : Nat)
: Layer
Swin Transformer stage:
nBlocksblocks 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 matchingunetUp. - unetUp
(ic oc : Nat)
: Layer
UNet decoder stage: ×2 bilinear upsample, concatenation with the matching encoder skip (
occhannels), then two 3×3 conv-BN-ReLU (ic + oc → oc → oc).icis 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 byMlirCodegen(forward and VJP). - transformerDecoder
(dim heads mlpDim nBlocks nQueries : Nat)
: Layer
DETR-style transformer decoder:
nBlocksblocks of self-attention overnQuerieslearned 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:
nUnitsunitsic → 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, withgroupschannel partitions. Shape-only. - shuffleV2Block
(ic oc nUnits : Nat)
: Layer
ShuffleNet v2 stage (Ma et al. 2018):
nUnitsunitsic → 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 linearNetSpec. Shape-only. - structureModule
(singleChannels pairChannels nBlocks : Nat)
: Layer
AlphaFold-2 Structure Module:
nBlocksrounds 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 widthdim, unfold into patches,nTxBlockstransformer blocks (headsheads, FFN widthmlpDim), fold back, 1×1 projection toic, 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):
nBlocksresidual blocks at fixedchannels, each a 7×7 depthwise conv, LN, 1×1 expand (×4), GELU, 1×1 project and layer scale. No downsampling (seeconvNextDownsample).normselects LayerNorm (the paper) or BatchNorm,actGELU (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.normas inconvNextStage, so a stage and its downsample stay in sync. - convNextStem
(ic oc patch : Nat)
: Layer
ConvNeXt patchify stem: a
patch × patchstride-patchconvic → ocwith bias, then a channels-first LayerNorm and no activation. Lowered by the JAX emitter;MlirCodegenhas no lowering for it. - waveNetBlock
(residualCh skipCh nLayers : Nat)
: Layer
WaveNet (van den Oord 2016) residual stack:
nLayersblocks at dilations2⁰, …, 2^(nLayers−1), each a dilated causal conv, gated activation (tanh ⊙ sigmoid), 1×1 projection back toresidualCh, residual skip, and a separate 1×1 conv producing askipChoutput 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 diminputDim · 2 · numFrequencies. Shape-only. - nerfMLP
(encodedPosDim encodedDirDim hiddenDim : Nat)
: Layer
NeRF MLP core (Mildenhall et al. 2020): 8 hidden layers of
hiddenDimwith ReLU, a skip re-concatenating the position encoding at layer 5, and two heads (density σ, and RGB from a direction-conditioned branch).encodedPosDimis the dim of γ(x) (typically 60),encodedDirDimof γ(d) (typically 24). Shape-only. - darknetBlock
(channels nBlocks : Nat)
: Layer
Darknet residual stack (YOLOv3):
nBlocksblocks at fixedchannels, 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
nBlocksbottleneck residual blocks, concatenate with the other half and 1×1-project tooc. 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 channelsb1out + b2out + b3out + b4out. Shape-only. - asppModule
(ic oc : Nat)
: Layer
Atrous Spatial Pyramid Pooling (DeepLab v3+): five branches of
occhannels — 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 tooc. 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) totargetchannels, top-down ×2 upsample-and-add, and a 3×3 smoothing conv per level; fourtarget-channel outputs. Shape-only (seefpnDetectfor 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 →occhannels), an optional RetinaNet tower oftower3×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²),g5the coarsest grid,imageH / 32). Parameter order isfpnDetectParamShapes, head biases last.tower = 0emits no tower ops. The loss is the FPN multi-scale YOLO loss, routed byTrainConfig.fpnScales. - denseBlock
(ic growthRate nLayers : Nat)
: Layer
DenseNet dense block:
nLayersBN-ReLU-1×1 (to4·growthRate)-BN-ReLU-3×3 sub-layers, each addinggrowthRatechannels to the running concatenation; outputic + nLayers · growthRatechannels. 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 withspatialUnflattenaround a token-stream op (typicallytransformerEncoder) 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, whenposEmb, the position embedding[seqLen, dModel].idsInputtakes[B, seqLen]f32 token ids and builds the one-hot in-graph (iota, compare, select): same parameters and math, noO(V·T)host upload.gathermakes the forward astablehlo.gatherof the[V, D]table by the ids and the backward astablehlo.scatter-add, so no[B, T, V]one-hot is materialized (O(T·D)rather thanO(T·V)memory). Implies ids-shaped input.posEmb := falsedrops the position table, for RoPE models: every weight is then independent ofseqLen, 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 a2·nFreqsin/cos embedding of the per-image timestep, broadcast overH, W. The timestep is read in-graph from the last input channel (Ddpm.prependTChannel'st/Tmaxplane), so there is no extra input. ParametersW [2·nFreq, channels]andb [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
seqLenper-residue feature rows of widthinDim— the rowsiand the rowsjof a crop — flat as[B, 2 · seqLen · inDim]. Each block goes through its own weightinDim → outDim(Won theiblock,Wjon thejblock) 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 theconvBnthat 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 overjgives theWgradient by the dense rule, summed overitheWjgradient; the features are host-side, so there is no input gradient. First layer only. WithpairIn = K > 0the host appendsKpair 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 lastKchannels,[B, outDim + K, seqLen, seqLen]; constants of the step, so the backward reads the cotangent's firstoutDimchannels and drops the rest.
Instances For
Equations
- One or more equations did not get rendered due to their size.
- instReprLayer.repr Layer.globalAvgPool prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Layer.globalAvgPool")).group prec✝
- instReprLayer.repr Layer.flatten prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Layer.flatten")).group prec✝
- instReprLayer.repr Layer.spatialFlatten prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "Layer.spatialFlatten")).group prec✝
Instances For
Equations
- instReprLayer = { reprPrec := instReprLayer.repr }
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
- 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.buildPrefixis derived fromnamealone, 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.binand 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
buildPrefixasnamealone. - convBnAct : Activation
Activation applied after a
.convBnlayer by the JAX emitter, per net (default ReLU);MlirCodegenrefuses 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
.convBnsites; a net that mixed them would need aLayerfield instead. - convPadStyle : PadStyle
How
.sameis spelled at a strided top-level.conv2d/.convBn, per net, in the JAX emitter. SeePadStyle. Defaults to.xlaSame. The ResNet-family mains set.symmetricbecause torchvision'sConv2d(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, TFSAMEsince 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
Equations
- instReprNetSpec = { reprPrec := instReprNetSpec.repr }
Equations
- One or more equations did not get rendered due to their size.
Instances For
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 withuseFocal(focal modifier) andlabelSmoothing. - softLabelCE : LossKind
Float
[B, NC]soft labels (mixup/cutmix/knn-mixup output). Compatible withlabelSmoothing(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 asperPixelCE, 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 isc. Same ABI asperPixelCE;weights.lengthmust equal the class count.This is the lever
perPixelDicewas supposed to be and isn't. Dice's gradient carries a factor ofp_ifrom 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'spis tiny (scripts/probes/seg_dice_vanishing_grad_probe.pymeasures it). CE's seed is(p - y)/N, which is-1/Natp = 0: flat, and wholly indifferent to the collapse. Scaling that byw_ctherefore 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'sCrossEntropyLoss(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 inw— the loss is invariant to the overall scale ofweights. 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 isperPixelCE's. One head is exactlyperPixelWeightedCE. 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 asperPixelCE.γ = 0is exactlyperPixelCE.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. Atp_t → 0it 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 whosed_logitsis 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 passesddpmOutShape. - 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:compileVmfbsrejects it on the MLIR path. Reduction is timm'smeanover B×C.
Instances For
Equations
- One or more equations did not get rendered due to their size.
- instReprLossKind.repr LossKind.classCE prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LossKind.classCE")).group prec✝
- instReprLossKind.repr LossKind.softLabelCE prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LossKind.softLabelCE")).group prec✝
- instReprLossKind.repr LossKind.perPixelCE prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LossKind.perPixelCE")).group prec✝
- instReprLossKind.repr LossKind.perPixelDice prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LossKind.perPixelDice")).group prec✝
- instReprLossKind.repr LossKind.perPixelDiceCE prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LossKind.perPixelDiceCE")).group prec✝
- instReprLossKind.repr LossKind.floatTargetMse prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LossKind.floatTargetMse")).group prec✝
- instReprLossKind.repr LossKind.yolov1Masked prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LossKind.yolov1Masked")).group prec✝
- instReprLossKind.repr LossKind.bce prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "LossKind.bce")).group prec✝
Instances For
Equations
- instReprLossKind = { reprPrec := instReprLossKind.repr }
Equations
- instBEqLossKind = { beq := instBEqLossKind.beq }
Equations
- One or more equations did not get rendered due to their size.
Instances For
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.
- ce : SegLoss
Per-pixel softmax cross-entropy (the original UNet-demo path).
- dice : SegLoss
Soft Dice only.
- diceCE : SegLoss
Dice + CE, summed.
- weightedCE
(weights : List Float)
: SegLoss
Per-pixel CE with a per-class weight on the true class. See
LossKind.perPixelWeightedCEfor the semantics and the argument. - multiCE
(weights : List (List Float))
: SegLoss
K weighted per-pixel CE heads over one output, labels mixed-radix. See
LossKind.perPixelMultiCE. - focalCE
(gamma : Float)
: SegLoss
Per-pixel focal CE,
-(1-p_t)^γ·log p_t. SeeLossKind.perPixelFocalCE.
Instances For
Equations
- instReprSegLoss = { reprPrec := instReprSegLoss.repr }
Equations
- One or more equations did not get rendered due to their size.
- instReprSegLoss.repr SegLoss.ce prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "SegLoss.ce")).group prec✝
- instReprSegLoss.repr SegLoss.dice prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "SegLoss.dice")).group prec✝
- instReprSegLoss.repr SegLoss.diceCE prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "SegLoss.diceCE")).group prec✝
Instances For
Equations
- instBEqSegLoss.beq SegLoss.ce SegLoss.ce = true
- instBEqSegLoss.beq SegLoss.dice SegLoss.dice = true
- instBEqSegLoss.beq SegLoss.diceCE SegLoss.diceCE = true
- instBEqSegLoss.beq (SegLoss.weightedCE a) (SegLoss.weightedCE b) = (a == b)
- instBEqSegLoss.beq (SegLoss.multiCE a) (SegLoss.multiCE b) = (a == b)
- instBEqSegLoss.beq (SegLoss.focalCE a) (SegLoss.focalCE b) = (a == b)
- instBEqSegLoss.beq x✝¹ x✝ = false
Instances For
Equations
- instBEqSegLoss = { beq := instBEqSegLoss.beq }
Equations
- instInhabitedSegLoss = { default := instInhabitedSegLoss.default }
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
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 directionr = m̂/(√v̂+ε) + λ·θ(DECOUPLED weight decayλ = weightDecayfolded 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
Equations
- instReprOptimizerKind = { reprPrec := instReprOptimizerKind.repr }
Equations
- instReprOptimizerKind.repr OptimizerKind.sgd prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "OptimizerKind.sgd")).group prec✝
- instReprOptimizerKind.repr OptimizerKind.adam prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "OptimizerKind.adam")).group prec✝
- instReprOptimizerKind.repr OptimizerKind.rmsprop prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "OptimizerKind.rmsprop")).group prec✝
- instReprOptimizerKind.repr OptimizerKind.lamb prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "OptimizerKind.lamb")).group prec✝
Instances For
Equations
Equations
- instBEqOptimizerKind.beq x✝ y✝ = (x✝.ctorIdx == y✝.ctorIdx)
Instances For
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
Equations
- instReprTrainResize = { reprPrec := instReprTrainResize.repr }
Equations
- instReprTrainResize.repr TrainResize.bicubic prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "TrainResize.bicubic")).group prec✝
- instReprTrainResize.repr TrainResize.bilinear prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "TrainResize.bilinear")).group prec✝
- instReprTrainResize.repr TrainResize.random prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "TrainResize.random")).group prec✝
Instances For
Equations
- instBEqTrainResize = { beq := instBEqTrainResize.beq }
Equations
- instBEqTrainResize.beq x✝ y✝ = (x✝.ctorIdx == y✝.ctorIdx)
Instances For
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
- rmspropDecay : Float
- 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'strain_image_classifier.pyran MobileNetV2 under): the learning rate multiplies the normalised gradient INSIDE the momentum term,buf = μ·buf + lr·g/√(s + ε), and the parameter step isp − bufwith no rate of its own. Off keeps the emittedbuf = μ·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.mdD13;Proofs.rmsPropTfStep_theta_lr_change). The verified peer is thermstfrenders. - weightDecay : Float
- cosineDecay : Bool
- warmupEpochs : Nat
- minLR : Float
- schedPerEpoch : Bool
timm's
CosineLRScheduleras DeiT and the RSB tiers run it (t_in_epochs): the rate is set ONCE PER EPOCH, warmup is linear fromwarmupLRInit(timm--warmup-lr 1e-6) to the peak overwarmupEpochs, and the cosine runs over ALLepochscounted from epoch 0 (warmup_prefix = False), floored atminLR. Off keeps the per-step warmup-then-cosine byte for byte. The verified peer istrainAdamSched'sschedPerEpoch/warmupLRInit. - warmupLRInit : Float
- cosineFromStep0 : Bool
tf-models' schedule, the MobileNetV4 paper's config:
LinearWarmupoverCosineDecay(decay_steps = total_steps)— the cosine counts EVERY step from step 0 (the warmup only overrides its firstwarmup_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 (schedPerEpochoff), withminLRas the floor. The verified peer istrainAdamSched'scosineFromStep0. - augment : Bool
- colorDistortSlim : Bool
TF-slim's
inception_preprocessing.distort_colorinfast_mode— whattrain_image_classifier.pyran (it passes nofast_mode, so the defaultTrue): after the crop, resize and flip, on the image in [0, 1],random_brightness(max_delta=32/255)thenrandom_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), thenclip_by_value(0, 1). Emitted back in 0..255 for the shared normalize. Off emits nothing, byte for byte (imagenet_parity.mdD12). imagenet (tfds) path only. - inputScalePm1 : Bool
TF-slim's input scaling,
(x − 0.5) × 2on [0, 1] —_MEAN_RGB = _STD_RGB = 127.5— in place of the ImageNet mean / std, train and eval alike (preprocess_for_trainandpreprocess_for_evalboth 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.yolov1Maskedpath. NB: a DIoU-trained model must be decoded with the same σ/exp (scripts/demos/yolo_map_visdrone.py --box-param diou). 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).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.fpnDetectlayer, the loss routes toemitMultiScaleYoloLossover 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)·fpnAffineScaleand translateU(-1,1)·fpnAffineTranslate(as a fraction of the frame), fired with probabilityfpnAffineProb.0scale gain and0probability (the defaults) leave the pipeline byte-identical to the HSV+hflip pack, so the existing arms stay reproducible.Separate from
augmenton purpose.augmentis 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 ofaugment, not instead of it. - fpnAffineTranslate : Float
- fpnAffineProb : Float
- fpnAffineWhThrPx : Float
Drop a transformed box whose clipped side falls under
fpnAffineWhThrPxpixels, or which keeps less thanfpnAffineAreaThrof 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
Per-class weights for the detector's classification term.
weights.lengthmust 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.pyandscripts/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.
RetinaNet prior-bias init: initialize the head's bias to
log π_cinstead 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. SeeNetSpec.applyHeadPriorBiasfor 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 atsigmoid = π.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
headPriorBiasabove, 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. SeeNetSpec.applyDetPriorBias. - useMixup : Bool
DeiT-style data augmentation knobs. Setting
useMixuporuseCutmixswitches 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.mixupAlphaandcutmixAlphacontrol the Beta-distribution shape; the paper defaults are 0.8 and 1.0 respectively.randomErasingoperates 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 probabilityp(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_mixalike. - useKnnMixup : Bool
- 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 atrainResrecipe 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'sRandomResizedCropAndInterpolationdraws the same) in place oftf.image.sample_distorted_bounding_box: areaU(0.08, 1)with nomin_object_coveredfloor, 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'smin_object_covered. 0.1 is TF EfficientNet's and every existing recipe's; tf-models'random_crop_imagepasses itsarea_range[0]= 0.08 (the MobileNetV4 paper's config). The default keeps the0.1literal, 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'sdistorted_bounding_box_croppasses(0.05, 1.0). The default keeps the0.08literal, byte for byte. - cropMaxAttempts : Nat
With TF's sampler:
max_attemptsbefore the whole image is returned. 10 is every existing recipe's; TF-slim'sdistorted_bounding_box_croppasses 100. The default keeps the10literal, byte for byte. - cropAspectLo : Float
With TF's sampler:
aspect_ratio_rangeas(lo, hi), whenhi > 0. 0 keeps the(3./4, 4./3.)literals, byte for byte; TF-slim'sdistorted_bounding_box_croppasses(0.75, 1.33)— 1.33, not 4/3. - cropAspectHi : Float
- resizeAntialias : Bool
antialiason the train crop's and the eval'stf.image.resize. On, the default, is PIL's behaviour and every existing recipe's (planning/archive/resize_eval_reconciliation.mdmeasured close to a point of top-1 on RSB-A3 without it); off istf.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_bicubicundertrainResize := .bicubic,resize_bilinearunder.bilinear) on the train crop's resize and the eval resize: no antialias andhalf_pixel_centers=False— the sampling grid aligned to pixel corners, wheretf.image.resizecentres it on half pixels. The bicubic is TF TPU EfficientNet's_resize_imagedefault; the bilinear is what TF-slim'stf.image.resize_imagesran in the TF 1 that trained MobileNetV2. Off keepstf.image.resizewithresizeAntialias, 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.
randAugmentNops drawn uniformly from {identity, brightness, contrast, color, autocontrast} per image, each at magnituderandAugmentM(0–10, paper default 9). No labels touched. - randAugmentN : Nat
- randAugmentM : Float
- randAugmentGeometric : Bool
Upgrade
useRandAugmentfrom the color-only "lite" path to the full RandAugment(N, M) sampler over the color+GEOMETRIC op set (shear/rotate/ translate viaImageProjectiveTransformV3, shared with AutoAugment). This is what ConvNeXt's recipe wants. Only meaningful whenuseRandAugmentis 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(timmmstd, DeiT uses 0.5) draws each op's magnitude from N(M, mstd) clipped to [0,10] instead of a fixed M.randAugmentInc(timminc1) 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
randAugmentNgeometric RandAugment layers applies its op (timmAugmentOp'sp, TF'sprob_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. NeedsaugBicubic(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) + 1in_distort_common), so an op is applied withp / (n + 1)rather than timm'sp / n. Off keeps timm's n-way draw, byte for byte. - randAugmentTranslateConst : Float
tf-models'
translate_const: TranslateX/Y move by(m / 10) · translate_constPIXELS (100 in the MobileNetV4 paper's config, so ±150 px at m15) instead of timm'stranslate_pct 0.45of the side. 0 keeps timm's form, byte for byte. Bilinear-path only (augBicubicoff). - augNearest : Bool
tf-models' geometric ops resample NEAREST (
transform(interpolation='nearest'), thewrap/unwrapfill 128) where this emitter's bilinear path resamples BILINEAR at the same fill. Off keeps BILINEAR, byte for byte. Bilinear-path only (augBicubicoff). - 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) viatf.raw_ops.ImageProjectiveTransformV3(core TF, notfa) plus the full color set (posterize/solarize/equalize/autocontrast/etc). Subsumes the color RandAugment, so leaveuseRandAugmentoff when this is on. EfficientNet's original recipe; no labels touched. - autoAugmentV0 : Bool
With
useAutoAugment: TF TPU EfficientNet's policy v0 (= timmauto_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 ownautoaugment.pyapplies it — the sub-policies are the same 25, op for op; what moves is the geometry.'TranslateY'in ABSOLUTE pixels,(m/10) · translate_constwithdistort_image_with_autoaugment'stranslate_const=250(setrandAugmentTranslateConst := 250; 225 px at the table's level 9), where timm'sTranslateYRelmoves 0.45 of the side; the geometric ops throughcontrib_image.{transform,rotate,translate}, whose default interpolation is NEAREST (augNearest,augBicubicoff) at thewrap/unwrapfill 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_INCoff). 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 contributesrepeatedAugindependently-augmented copies per epoch. On the tfds path this is a stream-levelflat_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_epochis unchanged, so an epoch sees ~1/K as many unique images ×K views, per the RSB recipe. 1 disables. - repeatedAugAdjacent : Bool
Keep the
repeatedAugcopies ADJACENT, as timm'sRASamplerplaces 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'simageH/imageW. 0 = no split (train and eval same resolution). The generatedforwardinfers 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 whentrainResis 0 — in place of the literal 224 every ImageNet spec is declared at (NetSpec.imageH). The generatedforwardinfers the side from the flat length, so the spec stays shared across a net's recipes: MobileNetV4'sfulltier trains at the paper's 256 on the 224 spec. 0 keeps the224literal, 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_SIZEbilinear (resize_image) — the MobileNetV4 paper's own validation. Off keeps timm's form (resize the short side to_IMG_SIZE / _CROP_PCTbicubic, then crop), byte for byte. On, timm's form is still emitted as_imagenet_decode_center_crop_timmand the shim swaps it in underSHIM_EVAL_SIZE(timm's test protocol), asjax/scripts/eval_full50k.py PROTOCOL=timmdoes. - evalCropTfEfficientNet : Bool
With
evalCropTf: TF TPU EfficientNet's_decode_and_center_croparithmetic rather than tf-models'center_crop_image_v2— the square's sideint((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 underresizeLegacyTf1) where tf-models resizes bilinear — the same arithmetic the train side'scropFallbackCenteruses. Off keeps tf-models' form, byte for byte. - evalCropSlim : Bool
With
evalCropTf: TF-slim'sinception_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_PCTof BOTH sides — the offsetsint(h · (1 − _CROP_PCT) / 2)in float64, the sizesh − 2 · offset— so the window has the JPEG's own aspect; thenresize_bilineartakes it to_IMG_SIZE×_IMG_SIZE, which does not keep it (TF 1's kernel underresizeLegacyTf1). 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
dropPathacross 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 throughforward, and drops at ConvNeXt, bottleneck, MBConv, UIB and transformer-encoder blocks. - wdExcludeNormBias : Bool
AdamW
no_weight_decayexclusion (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'smobilenet.training_scopeputsweights_regularizer=Noneonslim.separable_conv2d, which is whatexpanded_convbuilds its depthwise from, andl2_regularizer(weight_decay)onslim.conv2d(stem, expand / project 1×1s, head, logits). The_wd_maskleaf 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.mdD11); the verified peer isrankWdDecaysNoDw(thedwmarker). - wdDecayBiases : Bool
With
wdExcludeNormBias: the biases DECAY — TF TPU EfficientNet'smain.pyaddsweight_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_maskis 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 isrankWdDecaysKeepBias(thewnmarker). - 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
gradAccumStepsmicro-batches ofbatchSizebefore each optimizer update, giving an EFFECTIVE batch ofbatchSize × gradAccumStepsat 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.learningRateshould 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 oneF32.emacall 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;falseis a CONSTANT decay from step 0, timm'sModelEmaas DeiT scores it. The verified peer istrainAdamSched'semaWarmup. - 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
swagKper-epoch deviations from the SWA mean. At eval, sampleswagSamplesweight 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
ttaSamplesindependently-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
The loss, when set. Left at
none,TrainConfig.lossKindForderives it from the dataset (detection →.yolov1Masked, a per-pixel label record →.perPixelCE) and the soft-label augs (.softLabelCE), else.classCE. SeeLossKind..bceonly: 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 exceedstand 0 elsewhere. timm's RSB configuration sets 0.2.Bootstrap from a pretrained backbone checkpoint. When set to
some (paramsPath, prefixFloats),runTrainingoverwrites the firstprefixFloats * 4bytes of the He-init with bytes read fromparamsPath. The companion<basename>_bn_stats.binis 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).Offset-aware bootstrap, as
some (paramsPath, dstOffFloats, srcOffFloats, countFloats). Same job asbootstrapBackbonebut for a spec whose FIRST layer differs from the checkpoint's, so the transferable weights are no longer a prefix — seeNetSpec.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
bootstrapBackbonewhen 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}.binand{pfx}_bn_stats_e{N}.binsnapshots everycheckpointEveryNEpochsepochs. 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 — seebootstrapBackbone). - 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 ofbf16so a backend where bf16 convolution is slower (MIOpen) can keep convs in fp32 (defaultfalse) while CUDA/cuDNN can opt in. Only meaningful whenbf16 := true. Depthwise/separable convs (MobileNet/EfficientNet) still stay fp32. - f32StemHead : Bool
Under
bf16, keep the patch embed and the top-level.denselayers (on a ViT, the classifier head alone: the block Linears live insidetransformerEncoder) in fp32. This is the verified renders' own carve-out, so the knob exists for a pair's ONE-variable claim:ViTRenderBkeeps the head f32 in every net and the patch-embed f32 on ViT (bf16Conv := false, a measured cuDNN weight-grad limit), while this emitter'smmsent both through bf16. A plain@at default precision, i.e. the same XLA dot the render'sprecision = [DEFAULT, DEFAULT]f32dot_generallowers 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/convdtread a module-level dtype that the evaluator swaps for the duration of its once-tracedeval_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 throughforwardashas_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 whenrunningBNis 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 bym, so timm's PyTorch defaultmomentum = 0.1isbnMomentum = 0.9here — 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()):net reference value R50 / R34 timm (RSB) — momentum=0.10.9 EfficientNet-B0 the TF paper (77.1/93.3) — TF's decay=0.990.99 MobileNetV2 the TF-slim paper (72.0) — decay=0.9970.997 MNv4 the repo's own 100-epoch JAX run, not a paper 0.99 timm itself runs 0.9 on every one of those nets,
tf_efficientnet_b0included:BN_MOMENTUM_TF_DEFAULTexists in_efficientnet_builder.pybutget_bn_args_tf()has no caller in 1.0.28, so thetf_*ports inherit onlybn_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'sbnMom). Under gradient accumulation both sides compensate tobnMomentum^(1/K)per micro-batch. - bnEps : Float
BatchNorm ε —
_bn'seps, 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 emittedeps=1e-5literal, so every net that does not set it is byte-identical. The verified peer is the render'sepsStr, a different artifact per ε (theeps<d…>variant marker and its own_fwd_eval_eps<d…>eval graph), not a host-side knob. - adamEps : Float
AdamW's ε, the
1e-8in 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 the1e-8literal, byte for byte. The verified peer is the render'sadamEps(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'snn.LayerNormdefault and every existing trainer's; DeiT builds its blocks withpartial(nn.LayerNorm, eps=1e-6). The verified peer is the render'seps(ViTRenderB, variant markereps0000001). - geluExact : Bool
The exact GELU
x · Φ(x)—jax.nn.gelu(x, approximate=False), which is PyTorch'snn.GELUand what DeiT and ConvNeXt train under — in place ofjax.nn.gelu's default, the tanh approximation, at every GELU the emitter writes (the transformer MLP, the ConvNeXt block, a.geluactivation). The two differ by up to 4.7e-4. Off keeps every generated file byte-identical. The verified peer is the render'sGeluForm,.erffor this flag: a different artifact per form, not a host-side knob. - dropPathOverN : Bool
Stochastic-depth ramp over
i/N(TF EfficientNet'sdrop_rate · idx / len(blocks)) instead ofi/(N−1)(timm'slinspace(0, rate, depth)), on the MBConv path only. Off keeps every generated file byte-identical. - dropPathAllBlocks : Bool
tf-models' stochastic-depth ramp (
MobileNet._mobilenet_basewithflat_stochastic_depth_drop_rate: false):rate · (block_idx + 1) / num_blocksover 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 UIBi(entryi + 2of 25) drops at0.075 · (i + 3) / 25, where timm'slinspacegivesi / 20. Applied only where the block skips, as both do. On the UIB path only. Off keepsi / (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_timmgives everynn.Linear(QKV, attn-out, MLP fc1/fc2, and the classifier head)trunc_normal_(std=0.02)with zero bias, and leaves the patch-embednn.Conv2don PyTorch's defaultkaiming_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 plainnormal(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·prather than the input fanic·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 everynn.Conv2dANDnn.Linear(_init_weightsinfacebookresearch/ConvNeXt, and timm'sconvnext.pyagrees), 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-embednn.Conv2don PyTorch's defaultU(±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'semitConvBnInit— uniform±sqrt(6/(oc·k²)), i.e. stdsqrt(2/fan_out)— is ALREADY on that scale, differing only in distribution shape. Giving a ResNet convtrunc_normal(0.02)would be a regression, not a fix.Why it matters: the generic
emitConvBiasInitis Xavier overic·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 failurevitInitfixes for ViT. - initTruncNormal : Float
TF-slim's
truncated_normal_initializer(stddev)on EVERY kernel —mobilenet.training_scopesets oneweights_initializerforslim.conv2d,slim.separable_conv2d(the depthwise) andslim.fully_connected, so the stem, the 1×1s, the depthwise kernels and the logits all start astruncated_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 isVerifiedConfig.truncNormalSigma(variance-matched). - initHeadTfUniform : Bool
TF TPU EfficientNet's
dense_kernel_initializeron 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.mdD6); the bias is Keras' zeros. Off keeps Glorot, byte for byte. The verified peer isVerifiedConfig.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'sbuild_learning_rate) — instead of the continuous exponent counted from the end of warmup. Only read whenexpLRDecayRate > 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
trainingflag (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
Equations
- instReprTrainConfig = { reprPrec := instReprTrainConfig.repr }
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). Seescripts/datasets/preprocess_visdrone.pyfor 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*240selects.perPixelCEautomatically. Seescripts/datasets/preprocess_brats.pyfor 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 — seefit_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
brats224in 2.5D: the same slices, same split, same order, each record carrying thectxaxial neighbours on either side of its slice as extra channels —4·(2·ctx+1)channels, the centre slice's four modalities at4·ctx .. 4·ctx+3, neighbours replicated at the volume's ends. Produced bypreprocess_brats.py --size 224 --seed 0 --context ctxintodata/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
- One or more equations did not get rendered due to their size.
- instReprDatasetKind.repr DatasetKind.mnist prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "DatasetKind.mnist")).group prec✝
- instReprDatasetKind.repr DatasetKind.cifar10 prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "DatasetKind.cifar10")).group prec✝
- instReprDatasetKind.repr DatasetKind.imagenette prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "DatasetKind.imagenette")).group prec✝
- instReprDatasetKind.repr DatasetKind.imagenet prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "DatasetKind.imagenet")).group prec✝
- instReprDatasetKind.repr DatasetKind.detection prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "DatasetKind.detection")).group prec✝
- instReprDatasetKind.repr DatasetKind.brats prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "DatasetKind.brats")).group prec✝
- instReprDatasetKind.repr DatasetKind.brats224 prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "DatasetKind.brats224")).group prec✝
Instances For
Equations
- instReprDatasetKind = { reprPrec := instReprDatasetKind.repr }
Equations
- instBEqDatasetKind.beq DatasetKind.mnist DatasetKind.mnist = true
- instBEqDatasetKind.beq DatasetKind.cifar10 DatasetKind.cifar10 = true
- instBEqDatasetKind.beq DatasetKind.imagenette DatasetKind.imagenette = true
- instBEqDatasetKind.beq DatasetKind.imagenet DatasetKind.imagenet = true
- instBEqDatasetKind.beq DatasetKind.detection DatasetKind.detection = true
- instBEqDatasetKind.beq DatasetKind.brats DatasetKind.brats = true
- instBEqDatasetKind.beq DatasetKind.brats224 DatasetKind.brats224 = true
- instBEqDatasetKind.beq (DatasetKind.brats224Ctx a) (DatasetKind.brats224Ctx b) = (a == b)
- instBEqDatasetKind.beq x✝¹ x✝ = false
Instances For
Equations
- instBEqDatasetKind = { beq := instBEqDatasetKind.beq }
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
- (DatasetKind.brats224Ctx ctx).bratsChannels = 4 * (2 * ctx + 1)
- x✝.bratsChannels = 4
Instances For
The directory preprocess_brats.py writes each BraTS kind to by convention, and the
trainer's and predictor's default.
Equations
- DatasetKind.brats.bratsDataDir = "data/brats"
- (DatasetKind.brats224Ctx ctx).bratsDataDir = toString "data/brats224c" ++ toString (2 * ctx + 1)
- x✝.bratsDataDir = "data/brats224"
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
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
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
compileCheckB with the verdict discarded — the shape most gates want.
Equations
- compileCheck name body stderrTake = discard (compileCheckB name body stderrTake)
Instances For
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.