Per-channel BatchNorm (Chapter 5 Milestone B8) — the block-diagonal VJP #
Chapters 4–5 used a per-example global BatchNorm: one scalar (γ, β) over the
whole oc·h·w activation (LayerNorm-shaped). Real ResNet wants per-channel BN:
normalize each channel-slice independently with its own (γ_c, β_c), γ/β : Vec oc.
Because each channel is independent, the whole Jacobian is block-diagonal across
the channel axis — the genuinely-new piece. We get it for free by generalizing the
existing rowwise_has_vjp_mat (Tensor.lean, multi-head attention) from a single
per-row map to a per-row family g : Fin m → (Vec n → Vec p): viewing the
activation as Mat oc (h·w) (row = channel), per-channel BN is exactly
fun A => fun c => bnForward (h·w) ε (γ c) (β c) (A c). Its VJP runs each channel's
bn_has_vjp on that channel's cotangent slice; the cross-channel blocks vanish.
Everything closes under [propext, Classical.choice, Quot.sound].
Block-diagonal Jacobian of a per-row family. Applying a different map g r
to each row r of a matrix keeps the matrix Jacobian block-diagonal across the
row axis: output row k depends only on input row k (via g k). The per-row
generalization of pdivMat_rowIndep (which fixes one g for all rows).
Row-wise lifting of a per-row HasVJP family. Each row r gets its own map
g r (with its own VJP); the matrix backward runs (g r).backward on row r's
cotangent. The per-row peer of rowwise_has_vjp_mat.
Equations
- Proofs.rowwisePerRow_has_vjp_mat g hg hg_diff = { backward := fun (A : Proofs.Mat m n) (dY : Proofs.Mat m p) (r : Fin m) (c : Fin n) => (hg r).backward (A r) (dY r) c, correct := ⋯ }
Instances For
A per-row family flattens to a differentiable Vec → Vec map. The
Differentiable witness vjp_comp_at / the network composition needs to thread a
per-channel BN through a block.
Per-channel BatchNorm (matrix view): BN each row (= channel-slice of m = h·w
spatial cells) with its own (γ_c, β_c). The real-ResNet BN that bnForward's
global scalar version approximates.
Equations
- Proofs.bnPerChannelMat oc m ε γ β A c = Proofs.bnForward m ε (γ c) (β c) (A c)
Instances For
Per-channel BN VJP (block-diagonal). Each channel runs its own bn_has_vjp;
the cross-channel Jacobian blocks vanish (pdivMat_rowIndep_perRow).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Per-channel BN as a flat-vector op Vec (oc·m) → Vec (oc·m) (row-major, channel
c = the m-wide slab at flat positions finProdFinEquiv (c, ·)).
Equations
- Proofs.bnPerChannelFlat oc m ε γ β v = (Proofs.bnPerChannelMat oc m ε γ β (Proofs.Mat.unflatten v)).flatten
Instances For
Per-channel BN flat VJP — the block-diagonal matrix VJP bridged to Vec.
Equations
- Proofs.bnPerChannelFlat_has_vjp oc m ε hε γ β = Proofs.hasVJPMat_to_hasVJP (Proofs.bnPerChannelMat_has_vjp oc m ε hε γ β)
Instances For
Per-channel BN is differentiable everywhere (ε > 0). The composition witness.
Per-channel BN VJP correctness (ℝ-headline): the flat backward equals the
pdiv-contracted (block-diagonal) Jacobian of per-channel BN.
Per-channel consolidated BN input-gradient — the renderable closed form: run
the per-example three-term bn_grad_input on each channel-slice (m = h·w spatial
cells), reusing that channel's γ_c. This is exactly what a bnPerChannelBack SHlo
op / renderLNBack-per-channel emits; the abstract bnPerChannelFlat_has_vjp.backward
is the spec it must match.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Renderable backward is faithful (ℝ-headline): the per-channel consolidated
gradient equals the pdiv-contracted Jacobian of per-channel BN, under 0 < ε.
Each channel reduces to the per-example bn_input_grad_correct. The licence to
render per-channel BN's backward as the three-term formula per channel.
The network carries its activations in the Tensor3 flat layout (oc*h)*w
(flatConv etc.), but bnPerChannelFlat is defined on the Mat-split layout
oc*(h*w) (row c = the h·w spatial cells of channel c). The two Vecs have
the same size; they differ only in how finProdFinEquiv associates the product. So
the bridge is a pure re-association reindex (a permutation of coordinates) — a
reindexCLM whose VJP is the scatter pdiv_reindex gives, exactly like
decimateFlat. Conjugating bnPerChannelFlat by this bridge yields per-channel BN
acting on the network's Tensor3 activations, with its VJP for free via vjp_comp.
The two re-association indices are mutual inverses — the bridge is a genuine
relabeling (so conjugating by it really is per-channel BN, just in Tensor3
coordinates). Pure finProdFinEquiv round-trip.
Tensor3 → Mat-split reindex: read the ((c,hi),wi) cell at Mat position
(c, (hi,wi)). A reindexCLM, hence continuous-linear / differentiable.
Equations
- Proofs.reassocFwd oc h w y k = y (Proofs.reassocFwdIdx oc h w k)
Instances For
Mat-split → Tensor3 reindex (the inverse relabeling).
Equations
- Proofs.reassocBack oc h w y k = y (Proofs.reassocBackIdx oc h w k)
Instances For
Training BN computes μ/σ² from the activation it is normalizing; inference BN consumes frozen statistics (the driver's EMA'd running mean/var) and is therefore a plain affine map — pointwise in the activation, with no reduction at all. That is exactly why an eval forward built on it is class-batch-independent: an example's logits do not depend on which other examples share its batch.
The chain below mirrors bnForward → bnPerChannelMat → bnPerChannelFlat → bnPerChannelTensor3
one-for-one, so the eval op drops into the same layout bridge as the training op.
Inference BN on one channel's m activations: yᵢ = γ · (xᵢ − μ) · (var + ε)^(−1/2) + β,
with μ/var supplied rather than computed from x. The bnForward peer — note it takes
x pointwise, where bnForward reduces over all of x to get its own μ/σ².
Instances For
Per-channel inference BN (Mat layout) — row c gets channel c's frozen stats.
Equations
- Proofs.bnPerChannelEvalMat oc m ε γ β μ v A c = Proofs.bnEvalForward m ε (γ c) (β c) (μ c) (v c) (A c)
Instances For
Per-channel inference BN (flat layout) — the bnPerChannelFlat peer.
Equations
- Proofs.bnPerChannelEvalFlat oc m ε γ β μ v x = (Proofs.bnPerChannelEvalMat oc m ε γ β μ v (Proofs.Mat.unflatten x)).flatten
Instances For
Per-channel inference BN (Tensor3 layout) — the bnPerChannelTensor3 peer, through
the same reassoc bridge. This is what SHlo.bnPerChannelEvalF denotes.
Equations
- Proofs.bnPerChannelEvalTensor3 oc h w ε γ β μ v = Proofs.reassocBack oc h w ∘ Proofs.bnPerChannelEvalFlat oc (h * w) ε γ β μ v ∘ Proofs.reassocFwd oc h w
Instances For
Inference BN is differentiable everywhere — it is affine in x, so unlike the
training BN this needs no 0 < ε hypothesis: ε only enters the constant scale factor.
The rendered per-channel γ gradient: dγ_c = Σ_{s} dy_(c,s) · x̂_(c,s) (the
reduce over batch/spatial of dy·x̂ in cifarBnTrainStepStructured's bnParamGradPC).
x̂ is recomputed from the saved BN input v (the conv output). Lives here (not
CifarBnClose) so the bnGammaSgd SHlo op's den can reference it.
Equations
- Proofs.bnPerChannel_grad_gamma oc m ε v dy c = ∑ s : Fin m, dy (finProdFinEquiv (c, s)) * Proofs.bnXhat m ε (Proofs.Mat.unflatten v c) s
Instances For
The rendered per-channel β gradient: dβ_c = Σ_{s} dy_(c,s).
Equations
- Proofs.bnPerChannel_grad_beta oc m dy c = ∑ s : Fin m, dy (finProdFinEquiv (c, s))
Instances For
VJP of the forward reindex — the scatter pdiv_reindex gives. Mirrors
decimateFlat_has_vjp.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
The bridge is a permutation, so each reindex's VJP backward is just the inverse
reindex (the single matching delta survives the scatter). These two collapse the
vjp_comp backwards into a clean closed form for bnPerChannelTensor3.
Per-channel BatchNorm on the Tensor3 (oc*h)*w activation layout. Conjugate
the Mat-split bnPerChannelFlat by the layout bridge: relabel to Mat-split,
normalize each channel over its h·w spatial cells, relabel back. This is the
op B9 wires into the ResNet-34 trainer (its den target).
Equations
- Proofs.bnPerChannelTensor3 oc h w ε γ β = Proofs.reassocBack oc h w ∘ Proofs.bnPerChannelFlat oc (h * w) ε γ β ∘ Proofs.reassocFwd oc h w
Instances For
Per-channel BN (Tensor3 layout) VJP — block-diagonal across channels, lifted
through the layout bridge by vjp_comp (twice).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Per-channel BN (Tensor3 layout) VJP correctness (ℝ-headline): the backward
equals the pdiv-contracted (block-diagonal) Jacobian of per-channel BN on the
network's activation layout. The licence to wire per-channel BN into ResNet-34.
The composed vjp_comp backward collapses (the bridge reindexes are permutations):
per-channel BN's Tensor3 backward is the Mat-split block-diagonal backward,
conjugated by the layout bridge.
Renderable per-channel BN backward on the Tensor3 (oc*h)*w layout — relabel
to Mat-split, run the per-channel consolidated three-term bnPerChannel_grad_input,
relabel back. This is exactly what the bnPerChannelBack SHlo op emits (per-channel
renderLNBack, reducing over the spatial axis); its faithfulness spec is below.
Equations
- Proofs.bnPerChannelTensor3_grad_input oc h w ε γ x dy = Proofs.reassocBack oc h w (Proofs.bnPerChannel_grad_input oc (h * w) ε γ (Proofs.reassocFwd oc h w x) (Proofs.reassocFwd oc h w dy))
Instances For
Renderable Tensor3 backward is faithful (ℝ-headline): equals the
pdiv-contracted (block-diagonal) Jacobian of per-channel BN on the network's
activation layout, under 0 < ε. The licence to render per-channel BN's backward
in ResNet-34.
Transpose-reindex Fin (oc*(N*(h*w))) → Fin (N*(oc*(h*w))): a per-channel Mat
index (c, (n, s)) maps to the network [N,C,H,W] flat index (n, (c, s)) —
swap the batch and channel axes (s ↔ (hi,wi) carried along). A permutation.
Equations
- One or more equations did not get rendered due to their size.
Instances For
[N,C,H,W] → [C,N·H·W] reindex (gather the network cell at the Mat position).
Equations
- Proofs.bnchwFwd N oc h w y k = y (Proofs.bnchwFwdIdx N oc h w k)
Instances For
[C,N·H·W] → [N,C,H,W] reindex (the inverse relabeling).
Equations
- Proofs.bnchwBack N oc h w y k = y (Proofs.bnchwBackIdx N oc h w k)
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Batch-norm per channel on the network's [N,C,H,W] layout. Conjugate the
Mat-split bnPerChannelFlat (with m = N·h·w, the whole batch's cells per channel)
by the transpose bridge: relabel [N,C,H,W] → [C, N·H·W], normalize each channel
over ALL its batch+spatial cells, relabel back. The EfficientNet normalization.
Equations
- Proofs.bnBatchTensor4 N oc h w ε γ β = Proofs.bnchwBack N oc h w ∘ Proofs.bnPerChannelFlat oc (N * (h * w)) ε γ β ∘ Proofs.bnchwFwd N oc h w
Instances For
Batch-norm (network layout) VJP — block-diagonal across channels (now coupling the whole batch within each channel), lifted through the transpose bridge.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Renderable batch-norm backward on the [N,C,H,W] layout — relabel to the
per-channel Mat, run the consolidated three-term bnPerChannel_grad_input over the
whole batch (m = N·h·w), relabel back. Exactly what the batched bnBatch StableHLO
fragment emits (reduce over [0,2,3] per channel).
Equations
- Proofs.bnBatchTensor4_grad_input N oc h w ε γ x dy = Proofs.bnchwBack N oc h w (Proofs.bnPerChannel_grad_input oc (N * (h * w)) ε γ (Proofs.bnchwFwd N oc h w x) (Proofs.bnchwFwd N oc h w dy))
Instances For
Renderable batch-norm backward is faithful (ℝ-headline): equals the
pdiv-contracted (block-diagonal-across-channels, batch-coupled) Jacobian of
batch-norm on the network's [N,C,H,W] layout, under 0 < ε. The licence to render
EfficientNet's batch-norm backward as the per-channel three-term formula over the batch.