ViT — the attention-block cotangent chain #
TokenParamGrad.lean certifies each ViT param output for any cotangent dy at that
site's output. This file defines the cotangent the actual backward chain delivers at each
site — the ViT analogue of ConvNeXtChainClose — and the step ties (ViTStepTie,
ViTStepTieGB) feed those cotangents to those bridges at the real forward. The definitions are
per example; everything in a ViT is per-example separable, so the batched tie lifts them with
batchMapAux.
The chain composes the rendered backward denotations — exactly the render's
backward tokens: per-token dense input-VJP (denseRowBack's denotation
rowDenseBackFlat = rowwise dX = W·dy), the GELU mask at either form (dy ⊙ gf.scalarDeriv
at the saved pre-GELU), the rowwise scalar-LN input-VJP (lnRowBack's denotation
rowLNBackFlat = rowwise bnGradInput), the row-softmax backward (softmaxRowBack's
denotation rowSoftmaxBackFlat, recomputing the weights from the saved pre-softmax
scores), and the SDPA matmuls spelled with the forward matmulF/transposeF on
cotangents (matMulFlat/transposeFlat):
block: bout = h + fc2(gelu(fc1(LN₂ h))), h = x + Wo·SDPA(Wq·LN₁x, Wk·LN₁x, Wv·LN₁x)
The MLP residual passes dyOut straight to the fc2 output AND down the LN₂ branch
(the cotangent at h is dyOut + LN₂-back(…)); the attention residual likewise (the block
input's is cotH + LN₁-back(…)). The new wrinkle vs all prior nets is the three-way
fan-in at LN₁'s output — the Q/K/V dense-backs all read from LN₁ x, so their three
cotangents SUM (vitCotLn1), the biPath fan-in at width 3.
The substantive new ties (vitCotD{Q,K,V}_eq_sdpaBack{Q,K,V}): at the pinned saved
activations (pre-softmax scores = the scaled Q·Kᵀ, post-softmax weights =
sdpaWeights), the matmul-spelled chain segments ARE the proven closed forms
sdpaBack{Q,K,V} (Attention.lean) — dP = dO·Vᵀ → softmax-back → ·1/√d → dQ = dS·K / dK = dSᵀ·Q / dV = Pᵀ·dO, flattened. So the rendered attention backward is pinned to the
audited SDPA backward suite.
Cotangent at the fc1 output (pre-GELU): the GELU mask at the saved
pre-activation m1 (geluBack's denotation).
Equations
- Proofs.vitCotM1 gf Wfc2 m1 dyOut i = Proofs.vitCotG✝ Wfc2 dyOut i * gf.scalarDeriv (m1 i)
Instances For
vitCotM1 with its private fc2 back unfolded: fc2's per-token input-VJP, then the GELU mask.
Cotangent at the LN₂ output (= the fc1 input): fc1's per-token input-VJP.
Equations
- Proofs.vitCotLn2 gf Wfc1 Wfc2 m1 dyOut = Proofs.StableHLO.rowDenseBackFlat Np1 D mlpDim Wfc1 (Proofs.vitCotM1 gf Wfc2 m1 dyOut)
Instances For
dP = dAtt·Vᵀ — the rendered matmulF/transposeF on the cotangent against the
saved v.
Equations
- Proofs.vitCotDP v dAtt = Proofs.StableHLO.matMulFlat Np1 D Np1 dAtt (Proofs.StableHLO.transposeFlat Np1 D v)
Instances For
dS — softmaxRowBack's denotation at the saved pre-softmax scaled scores ss.
Equations
- Proofs.vitCotDS ss v dAtt = Proofs.StableHLO.rowSoftmaxBackFlat Np1 Np1 ss (Proofs.vitCotDP v dAtt)
Instances For
dQ = (1/√d · dS)·K against the saved k.
Equations
- Proofs.vitCotDQ d ss k v dAtt = Proofs.StableHLO.matMulFlat Np1 Np1 D (fun (i : Fin (Np1 * Np1)) => Proofs.sdpaScale d * Proofs.vitCotDS ss v dAtt i) k
Instances For
dV = Pᵀ·dAtt against the saved post-softmax weights p.
Equations
- Proofs.vitCotDV p dAtt = Proofs.StableHLO.matMulFlat Np1 Np1 D (Proofs.StableHLO.transposeFlat Np1 Np1 p) dAtt
Instances For
The three-way fan-in at LN₁'s output: the Q/K/V dense-backs all read from
LN₁ x, so their cotangents SUM — the biPath fan-in at width 3, the new
structural wrinkle vs every prior net.
Equations
- Proofs.vitCotLn1 Wq Wk Wv dQ dK dV i = Proofs.StableHLO.rowDenseBackFlat Np1 D D Wq dQ i + Proofs.StableHLO.rowDenseBackFlat Np1 D D Wk dK i + Proofs.StableHLO.rowDenseBackFlat Np1 D D Wv dV i
Instances For
Cotangent at the final-LN output: classifier-back (dotOut's denotation
Mat.mulVec Wcls) scattered to row 0 (clsPadF's denotation clsPadFlat).
Equations
- Proofs.vitCotFl N D nClasses Wcls dy = Proofs.StableHLO.clsPadFlat N D (Wcls.mulVec dy)
Instances For
dP-segment tie: the rendered matmulF(dOut, transposeF V) is the proven
sdpaDWeights V dOut = dOut·Vᵀ, flattened.
dV tie: at the saved post-softmax weights (sdpaWeights Q K), the rendered
matmulF(transposeF P, dOut) IS the proven sdpaBackV = weightsᵀ·dOut.
dS-segment tie: softmaxRowBack's denotation, recomputing the weights from the
saved pre-softmax scaled scores, applied to the flattened sdpaDWeights, IS the
proven sdpaDScaled (the per-row pᵢ⊙(dwᵢ − ⟨pᵢ,dwᵢ⟩) closed form).
dQ tie: at the saved activations, the rendered
matmulF(scaleF(softmaxRowBack(matmulF(dOut, transposeF V))), K) IS the proven
sdpaBackQ = (1/√d · softmax-back(dOut·Vᵀ))·K.
dK tie: likewise the rendered transposed chain IS the proven
sdpaBackK = (1/√d · softmax-back(dOut·Vᵀ))ᵀ·Q.