ConvNeXt Item D — pinning the block cotangent chain #
ConvNeXtClose.lean (Item C) certifies each ConvNeXt param output for any cotangent dy at that
layer's output. This file pins dy to the cotangent the actual backward chain delivers — the
ConvNeXt analogue of MobileNetV2ChainClose (planning/archive/convnext_close.md
Item D). Pure-Lean, batch-1 — LayerNorm is per-example separable, so none of EfficientNet's
batched-VJP machinery (batchMap_has_vjp) is needed.
The chain through a ConvNeXt block composes the rendered backward denotations — layer-scale back
(layerScale γls applied to the cotangent: the input-VJP γ ⊙ dy is the forward map itself, the
layerScaleF-on-the-cotangent trick the Item B render uses), the 1×1 conv input-VJP
(conv2d_has_vjp3 via the flatten bridge, = convBack's denotation), the GELU mask
(dy ⊙ geluScalarDeriv, = gelu_has_vjp's backward; geluScalarDeriv_eq certifies the closed
form geluBack emits), the scalar-LN input-VJP (bn_grad_input, = bnBack's denotation), and the
depthwise input-VJP (depthwiseFlat_has_vjp, = depthwiseBack's denotation) — back through
layerScale → project → gelu → expand → LN → depthwise:
block: o = addV( layerScale γls (conv₁ₓ₁ₚᵣ( gelu( conv₁ₓ₁ₑₓ( LN( dw₇ₓ₇(x) ))))), x )
The residual addV is the outermost op and passes the block cotangent dyOut straight through to
the layer-scale output (ConvNeXt has no post-add activation — the r34 relu(add(…)) mask never
appears), and the identity skip adds dyOut back at the block input. Unlike MNV2/r34 there is no
stride split: ConvNeXt blocks keep resolution (stride-1 7×7 depthwise), so one set of cotangent
definitions covers every block. Each param output then denotes θ − lr·(certified ∂/∂θ · the actual-chain cotangent) — including the two ConvNeXt-signature families (layer-scale γ,
scalar-LN γ/β), which MNV2/r34 had no analogue of. Pins the cotangent; the = ∂loss/∂θ fold
stays separate, as for the CNN. 3-axiom clean.
Cotangent at the project conv output (= the layer-scale input): layerScale γls dyOut
— the forward map applied to the block cotangent. The residual addV passes dyOut through
to the layer-scale output unchanged (no post-add activation), and layerScale's input-VJP
γ ⊙ dy is layerScale γ itself (diagonal/symmetric — layerScale_has_vjp), which is why
the Item B render emits a second layerScaleF on the cotangent rather than a backward token.
Equations
- Proofs.cnxCotP γls dyOut = Proofs.layerScale γls dyOut
Instances For
Cotangent at the expand conv output (cExp ch, pre-GELU): continue through the project
1×1 conv-back and the GELU mask (geluScalarDeriv at the saved pre-GELU activation e).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Cotangent at the LN output (c ch): continue through the expand 1×1 conv-back. This is
the cotangent the scalar-LN γ/β grads contract with — the substantive LN pin.
Equations
- Proofs.cnxCotN γls Wex bex Wpr bpr nl g e dyOut = (Proofs.hasVJP3_to_hasVJP (Proofs.conv2d_has_vjp3 Wex bex)).backward nl (Proofs.cnxCotE γls Wpr bpr g e dyOut)
Instances For
Cotangent at the depthwise conv output (c ch): continue through the scalar-LN
input-VJP (bn_grad_input — the three-term bnBack denotation, recomputing x̂/istd from
the saved LN input d).
Equations
- Proofs.cnxCotD ε γn γls Wex bex Wpr bpr d nl g e dyOut = Proofs.bn_grad_input (c * h * w) ε γn d (Proofs.cnxCotN γls Wex bex Wpr bpr nl g e dyOut)
Instances For
Cotangent at the block input: the depthwise input-VJP of cnxCotD, plus the identity
skip's dyOut. This is what the block hands upstream — the next block's dyOut, and at
block 1 the stem's dyStem.
Equations
- Proofs.cnxCotXin ε γn γls Wdw bdw Wex bex Wpr bpr xin d nl g e dyOut i = (Proofs.depthwiseFlat_has_vjp Wdw bdw).backward xin (Proofs.cnxCotD ε γn γls Wex bex Wpr bpr d nl g e dyOut) i + dyOut i
Instances For
Cotangent at the stem conv output: bn_grad_input at the saved patchify output (the stem
is conv → scalar-LN, nothing else — no activation, no pool), applied to dyStem, the
cotangent block 1 delivers at the stem-LN output (= block 1's cnxCotXin).
Equations
- Proofs.cnxStemCot ε γst patch dyStem = Proofs.bn_grad_input n ε γst patch dyStem
Instances For
Layer-scale γ, chain-certified. The chain cotangent at the layer-scale output IS the block
cotangent dyOut — the residual addV is the outermost op and ConvNeXt has no post-add
activation, so the passthrough is exact. γlsⁿ denotes γls − lr·(certified ∂(layerScale)/∂γ · dyOut) with the saved project output p as the layer input.
Project 1×1 conv weight, chain-certified. Wprⁿ denotes Wpr − lr·(certified ∂conv/∂Wpr · layerScale γls dyOut) — the conv weight bridge at the layer-scale-back cotangent, with the
saved GELU output g as the conv input.
Expand 1×1 conv weight, chain-certified. Wexⁿ denotes Wex − lr·(certified ∂conv/∂Wex · the chain cotangent at the expand output) — through layer-scale back, project conv-back, and
the GELU mask — with the saved LN output nl as the conv input.
Expand 1×1 conv bias, chain-certified.
Block scalar-LN γ, chain-certified. The substantive LN pin: the chain cotangent at the LN
output is cnxCotN (through layer-scale back → project conv-back → GELU mask → expand
conv-back), and γⁿ denotes γ − lr·(certified ∂LN/∂γ · cnxCotN) with the saved depthwise
output d as the LN input — the Item C Vec 1 embedding at the actual chain cotangent.
Block scalar-LN β, chain-certified.
Depthwise 7×7 weight, chain-certified. Wdwⁿ denotes Wdw − lr·(certified ∂(depthwiseConv2d)/∂Wdw · the deepest in-block cotangent) — through the whole chain down to
the scalar-LN input-VJP — with the saved block input xin as the conv input.
Depthwise 7×7 bias, chain-certified.
Stem 1×1 patchify conv weight, chain-certified. Wstⁿ denotes Wst − lr·(certified ∂conv/∂Wst · bn_grad_input(patch, dyStem)) — the stem-LN input-VJP at the cotangent block 1
delivers (dyStem = block 1's cnxCotXin; generic here, as for the MNV2/r34 stems).
Stem 1×1 patchify conv bias, chain-certified.