MLP VJP Proofs #
Formal VJP correctness for the layers of a 3-layer MLP.
All definitions over ℝ, proofs use Mathlib's Finset.sum.
Dense Jacobian — ∂(W·x + b)_j/∂x_i = W_{ij}. dense W b is the linear map
x ↦ (Σ_i x_i W_{ij})_j plus the constant b, so pdiv_of_affine reads the entry off the
basis vector and the Kronecker sum collapses.
Jacobian of dense wrt W — ∂dense(W, b, x)_j/∂W_{i, j'} = x_i·δ(j, j'). Over the
flatten bijection v ↦ dense (unflatten v) b x is linear in v plus the constant b
(pdiv_of_affine). Symmetric counterpart to pdiv_dense.
Dense VJP — proved.
Equations
- Proofs.denseHasVJP W b = { backward := fun (_x : Proofs.Vec m) (dy : Proofs.Vec n) => W.mulVec dy, correct := ⋯ }
Instances For
The Chapter-1 demo model: a linear classifier is a single dense layer.
Equations
- Proofs.mnistLinear W b = Proofs.dense W b
Instances For
denseHasVJP's correct field, restated for mnistLinear so it can be cited by
name.
Dense is everywhere differentiable. dense W b is affine in
x, hence smooth; this is the underlying Differentiable ℝ
statement that vjpCompAt needs when composing through dense
layers.
Dense weight gradient is the outer product.
Mat.outer x dy is the cotangent-contracted Jacobian of dense(W, b, x)
with respect to W, at every index.
(Mat.outer x dy) i j = ∑ k, pdiv (…) (Mat.flatten W) (fPF (i, j)) k · dy k
Dense weight backward — named accessor.
dW = x ⊗ dy (outer product).
Equations
- Proofs.denseWeightGrad x dy = Proofs.Mat.outer x dy
Instances For
The Jacobian of a diagonal 0/1 mask. If f has, at x, the derivative that keeps
coordinate k when p k and zeroes it otherwise, its pdiv is the diagonal indicator of p.
relu and relu6 at a smooth point are both this.
ReLU's local linear part at a smooth point — the diagonal
indicator CLM. At each coordinate k, projects to y k if
x k > 0, otherwise zero. Two smooth points with the same sign
pattern share this same CLM.
Equations
- Proofs.reluLinearPart n x = ContinuousLinearMap.pi fun (k : Fin n) => if x k > 0 then ContinuousLinearMap.proj k else 0
Instances For
ReLU is differentiable at smooth points. Near x every coordinate keeps its sign
(finitely many strict inequalities persist, Filter.eventually_all), so relu n agrees
with reluLinearPart n x on a neighbourhood. EventuallyEq promotes the CLM's
HasFDerivAt to ReLU's.
ReLU is DifferentiableAt at smooth points. Corollary of
relu_hasFDerivAt; lets vjpCompAt chain through ReLU.
ReLU bundled VJP — canonical (junk-at-kink) witness.
HasVJP.correct is satisfied by the canonical pdiv-derived backward:
at smooth points it is the diagonal indicator (per pdiv_relu); at
points where some coordinate is zero, pdiv (relu n) x agrees with
fderiv's junk default of 0, so the canonical backward is 0
there too — the witness is HasVJP.canonical, whose correct holds by rfl.
The codegen (MlirCodegen.lean) emits the standard subgradient
formula if x > 0 then dy else 0 instead, which agrees with the
canonical witness at smooth points and differs at the kinks (the
convention relu'(0) := 0 used by every ML framework). The
smooth-point agreement is formal: see
relu_codegen_matches_canonical below. The Lean-vs-codegen gap at
the kinks is the codegen trust boundary — see
LeanMlir/Proofs/README.md.
Equations
Instances For
Bridge: reluHasVJP's canonical backward matches the codegen
formula at smooth points.
At any point where no coordinate of x is zero, the canonical
pdiv-derived backward ∑ j, pdiv (relu n) x i j * dy j collapses
to the framework subgradient if x i > 0 then dy i else 0 that
MlirCodegen.lean actually emits. Closes the smooth-point half of
the codegen trust boundary — what's left is just the kink
convention.
ReLU pointwise VJP — no canonical-witness escape.
Constructs HasVJPAt (relu n) x at a smooth point. The backward
is the codegen-shape if x i > 0 then dy i else 0 directly; the
correct field is a real proof via pdiv_relu (the smooth-point
Jacobian) + sum-collapse, not rfl.
Equations
- Proofs.reluHasVJPAt n x h_smooth = { backward := fun (dy : Proofs.Vec n) (i : Fin n) => if x i > 0 then dy i else 0, correct := ⋯ }
Instances For
Cross-entropy at a hard label: −log (softmax logits)_label.
Equations
- Proofs.crossEntropy c logits label = -Real.log (Proofs.softmax c logits label)
Instances For
The three-layer MLP forward: dense → relu → dense → relu → dense.
Equations
- Proofs.mlpForward W₀ b₀ W₁ b₁ W₂ b₂ = Proofs.dense W₂ b₂ ∘ Proofs.relu d₂ ∘ Proofs.dense W₁ b₁ ∘ Proofs.relu d₁ ∘ Proofs.dense W₀ b₀
Instances For
MLP composition VJP — canonical witness.
The MLP forward composes dense W b (everywhere Differentiable)
with relu (non-Differentiable at the kinks). vjpComp would
require Differentiable ℝ (relu n), which doesn't hold globally,
so the chain-rule route is blocked. The canonical pdiv-derived
backward inhabits HasVJP.correct directly via rfl — the
codegen substitutes the subgradient formula at the kinks (see
LeanMlir/Proofs/README.md for the trust-boundary discussion).
Equations
- Proofs.mlpHasVJP W₀ b₀ W₁ b₁ W₂ b₂ = Proofs.HasVJP.canonical (Proofs.mlpForward W₀ b₀ W₁ b₁ W₂ b₂)
Instances For
MLP pointwise VJP — no canonical-witness escape.
Constructs HasVJPAt (mlpForward …) x by chaining vjpCompAt
through dense → relu_at → dense → relu_at → dense. Requires the
intermediate pre-activations dense W₀ b₀ x and dense W₁ b₁ z₀
to avoid zero (no coordinate ties the ReLU kink) — exactly the
"smooth input" condition. Unlike the canonical mlpHasVJP, the
backward here is built by the chain rule.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Public correctness theorems for the canonical-witness defs #
Each HasVJP def above bundles a backward function with a .correct
field; these _correct theorems expose that field as a top-level
proposition so consumers (downstream code, tests/comparator/,
doc-gen4) can refer to the contract directly without reaching into
record internals.
Public correctness theorem for reluHasVJP: the canonical
witness's backward equals the pdiv-contracted Jacobian by definition.
Public correctness theorem for mlpHasVJP: same pattern as
reluHasVJP_correct, lifted to the three-layer MLP forward.
Public correctness theorem for reluHasVJPAt — the
pointwise (smooth-input) variant. Unlike reluHasVJP_correct, this
wrapper's underlying .correct field is a real proof
(pdiv_relu + sum-collapse), not rfl; the wrapper exposes it as
a top-level proposition for tests/comparator/ re-verification.
Public correctness theorem for mlpHasVJPAt — the
pointwise variant composed via vjpCompAt through
dense → relu_at → dense → relu_at → dense. The underlying
.correct field chains real chain-rule proofs (no rfl escape at
the ReLU kinks).
ReLU6, coordinatewise: min (max (x i) 0) 6 (MobileNetV2's activation).
Equations
- Proofs.relu6 n x i = min (max (x i) 0) 6
Instances For
ReLU6's local linear part at a smooth point: projects to y k when
0 < x k < 6, otherwise zero.
Equations
- Proofs.relu6LinearPart n x = ContinuousLinearMap.pi fun (k : Fin n) => if 0 < x k ∧ x k < 6 then ContinuousLinearMap.proj k else 0
Instances For
ReLU6 is the identity inside its window. Wherever every coordinate is strictly inside
(0,6), min (max · 0) 6 does nothing — the step every structural witness takes to collapse a
relu6 stage to its BatchNorm. Stated at the top level rather than inside a witness namespace:
it is a fact about the op, and BatchSeal's consumers outlive any one witness.
ReLU6 is continuous — min (max · 0) 6 coordinatewise. The peer of
relu_continuous (below), for the ray argument of a relu6 net's seal.
relu is continuous everywhere — it is max · 0; only its derivative has a kink.