Documentation

LeanMlir.Proofs.Training.SgdDescent.Cnn

Lipschitz constants for the CNN softmax-CE loss — descent through the pool #

SgdDescent.Mlp discharged sgd_descends' smoothness hypothesis for every MLP weight layer; this file extends the program to the Chapter-3 MNIST CNN (conv → relu → conv → relu → maxpool → dense → relu → dense → relu → dense). What's genuinely new versus the MLP:

The capstone cnn_conv2_sgd_descends mirrors mlp_input_sgd_descends: under the four margins (relu₂, pool selection up to twins, relu₃, relu₄) at the step radius and the small-step condition, one inexact SGD step on the second conv kernel (one example, every other parameter fixed) decreases that example's cross-entropy loss by ≥ lr·‖∇L‖₂²/2, with the segment-Lipschitz constant explicit.

cnn_conv1_sgd_descends extends the program one layer deeper: the step now crosses conv2 AS A FUNCTION OF ITS INPUT. Conv is linear there, its Jacobian entry a single kernel tap (convTap, extracted point-free from the certified input-VJP), and its ℓ1 operator factor is LOCALITY — (channels)·kH·kW·w₂, not a spatial count. Under FIVE margins (relu₁ + the conv2 four, at conv1 radii) every routing decision freezes and the loss provably drops.

cnn_conv2_bias_sgd_descends / cnn_conv1_bias_sgd_descends close the biases: the bias-map Jacobian is a Kronecker channel indicator (conv2d_bias_pdiv, extracted from the certified bias VJP), the per-entry drift is exactly |e o| (no input bound a). Each conv layer's drift chain, margins, segment-Lipschitz gradient and descent step are stated once, for any parameter map with per-entry drift ρ·‖e‖₁ (Conv2Slot, Conv1Slot): the kernel rungs are ρ = a, the bias rungs ρ = 1 — the bare D radii and a² ↦ 1 in the constants. Both conv kernels, both conv biases and the dense-head weights and biases of the Chapter-3 CNN (the latter via the MLP rungs, SgdDescent.Mlp and SgdDescent.MlpBias) each have a single-layer, single-example descent statement, conditional on the margins above and the oracle-accuracy, small-step and dominance hypotheses. The rungs that replace the oracle accuracy by the proven accuracy of the FloatModel binary32 gradient are in SgdDescent.CnnFloat.

The index plumbing, the 2×2 max-pool window facts and the twin relations (ConvPatchEq, ConvPatchEq2) it reads tensors through are in ConvIndex; the conv's kernel, input and bias Jacobians and drifts (conv2d_weight_pdiv, convTap, conv2d_input_l1_drift, conv2d_bias_pdiv) in ConvGrad.

theorem Proofs.ce_head3_input_grad {p d₃ d₄ nC : ℕ} (W₃ : Mat p d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (u : Vec p) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ u l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ u)) q ≠ 0) (j : Fin p) :
pdiv (fun (y : Vec p) (x : Fin 1) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ y))))) label) u j 0 = ∑ l : Fin d₃, W₃ j l * ((if dense W₃ b₃ u l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ u)) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ u))))) k - oneHot nC label k)))

Loss input-gradient of the 3-dense head CE∘d₅∘relu∘d₄∘relu∘d₃ at the pooled vector — one pdiv_comp hop (peel dense W₃) on top of ce_head2_input_grad, exactly as ce_head2 was one hop on ce_head_relu. Note there is NO leading mask: the pool output feeds dense W₃ directly.

theorem Proofs.pool_relu_input_grad {c h w d₃ d₄ nC : ℕ} (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (z₂ : Vec (c * (2 * h) * (2 * w))) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), z₂ k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₂))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) z₂)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) z₂)))) q ≠ 0) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) :
pdiv (fun (y : Vec (c * (2 * h) * (2 * w))) (x : Fin 1) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) y))))))) label) z₂ (t3Idx ci hi wi) 0 = (if z₂ (t3Idx ci hi wi) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₂)) ci hi wi then ∑ l : Fin d₃, W₃ (t3Idx ci (winRow hi) (winCol wi)) l * ((if dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) z₂)) l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) z₂)))) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) z₂))))))) k - oneHot nC label k))) else 0

Loss input-gradient at the conv output — the key glue of the conv rung. The chain pdivs through the relu (mask) and the pool (frozen selector): at a smooth point the sum over pooled coordinates collapses to the single argmax term, so

∂(CE∘head3∘pool∘relu)/∂z₂[ci,hi,wi] = relu'(z₂[ci,hi,wi]) · 𝟙[(ci,hi,wi) is its window's argmax] · head3grad(window(ci,hi,wi)).

NB the pool acts on the POST-relu activation, so the smoothness and argmax conditions are stated on relu z₂, not z₂.

theorem Proofs.gather_relu_input_grad {c h w d₃ d₄ nC : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (z₂ : Vec (c * (2 * h) * (2 * w))) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), z₂ k ≠ 0) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) z₂)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) z₂)))) q ≠ 0) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) :
pdiv (fun (y : Vec (c * (2 * h) * (2 * w))) (x : Fin 1) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) y))))))) label) z₂ (t3Idx ci hi wi) 0 = (if z₂ (t3Idx ci hi wi) > 0 then 1 else 0) * if σ ci (winRow hi) (winCol wi) = (winRowMod hi, winColMod wi) then ∑ l : Fin d₃, W₃ (t3Idx ci (winRow hi) (winCol wi)) l * ((if dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) z₂)) l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) z₂)))) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) z₂))))))) k - oneHot nC label k))) else 0

Loss input-gradient at the conv output, through a fixed gather — the gather peer of pool_relu_input_grad. With the pool replaced by poolGatherFlat σ, the route is σ's selection rather than an argmax, so no pool hypothesis is needed:

∂(CE∘head3∘gather∘relu)/∂z₂[ci,hi,wi] = relu'(z₂[ci,hi,wi]) · 𝟙[(hi,wi) is σ's cell of its window] · head3grad(window(ci,hi,wi)).

theorem Proofs.cnn_conv2_loss_gradAt {c h w d₃ d₄ nC kH kW : ℕ} (b₂ : Vec c) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (v : Vec (c * c * kH * kW)) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d (Kernel4.unflatten v) b₂ x₁).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten)))) q ≠ 0) (o cc : Fin c) (kh : Fin kH) (kw : Fin kW) :
gradAt (fun (v' : Vec (c * c * kH * kW)) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v') b₂ x₁).flatten))))))) label) v (k4Idx o cc kh kw) = ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), (if ci = o then convPad kH kW x₁ cc kh kw hi wi else 0) * ((if (conv2d (Kernel4.unflatten v) b₂ x₁).flatten (t3Idx ci hi wi) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten)) ci hi wi then ∑ l : Fin d₃, W₃ (t3Idx ci (winRow hi) (winCol wi)) l * ((if dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten)) l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten)))) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten))))))) k - oneHot nC label k))) else 0)

Closed form of the conv2 loss gradient at any four-margin point — the chain rule through the conv weight map (gradAt_comp_t3) with the pool-collapsed head gradient (pool_relu_input_grad) and the point-free conv weight Jacobian (conv2d_weight_pdiv). The conv-layer peer of mlp_input_loss_gradAt; the spatial triple sum (vs the MLP's Kronecker collapse) is weight sharing.

theorem Proofs.dense_transpose_eq {p n : ℕ} (W : Mat p n) (c : Vec n) (l : Fin p) :
∑ k : Fin n, W l k * c k = dense (fun (j : Fin n) (i' : Fin p) => W i' j) (fun (x : Fin p) => 0) c l

The unmasked peer of reluMask_dense_transpose_eq: a bare Wᵀ contraction ∑ₖ Wₗₖ·cₖ = dense (transpose W) 0 c l. The pool feeds dense W₃ with no leading ReLU mask, so the W₃ contraction in the certified conv-2 gradient collapses through this, where the masked W₄/W₅ contractions collapse through reluMask_dense_transpose_eq. NB this is generic in (W, c), so fire it only where the goal has no other matrix contraction (e.g. the spatial ∑ convPad·cot) — see head3_cot_reluMask.

theorem Proofs.head3_cot_reluMask {p d₃ d₄ nC : ℕ} (W₃ : Mat p d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (u : Vec p) (j : Fin p) :
∑ l : Fin d₃, W₃ j l * ((if dense W₃ b₃ u l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ u)) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ u))))) k - oneHot nC label k))) = dense (fun (j' : Fin d₃) (i' : Fin p) => W₃ i' j') (fun (x : Fin p) => 0) (FloatModel.reluMask (dense W₃ b₃ u) (dense (fun (j' : Fin d₄) (i' : Fin d₃) => W₄ i' j') (fun (x : Fin d₃) => 0) (FloatModel.reluMask (dense W₄ b₄ (relu d₃ (dense W₃ b₃ u))) (dense (fun (j' : Fin nC) (i' : Fin d₄) => W₅ i' j') (fun (x : Fin d₄) => 0) fun (k : Fin nC) => softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ u))))) k - oneHot nC label k)))) j

The 3-dense head cotangent in dense/reluMask form. The raw nested ∑ₗ W₃·(𝟙[z₃]·∑_q W₄·(𝟙[z₄]·∑_k W₅·(softmax−onehot))) that pool_relu_input_grad / cnn_conv2_loss_gradAt leave at the pooled vector u equals dense W₃ᵀ 0 (mask z₃ (dense W₄ᵀ 0 (mask z₄ (dense W₅ᵀ 0 (softmax−onehot))))) — the two masked contractions via reluMask_dense_transpose_eq, the unmasked W₃ via dense_transpose_eq. Stated head-locally (no spatial sum) so the generic dense_transpose_eq fires only on the W₃ row. The head peer the conv grad-close bounds against via dense_close (W₃) and cot_step_close (W₄/W₅).

theorem Proofs.cnn_conv2_loss_gradAt_reluMask {c h w d₃ d₄ nC kH kW : ℕ} (b₂ : Vec c) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (v : Vec (c * c * kH * kW)) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d (Kernel4.unflatten v) b₂ x₁).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten)))) q ≠ 0) (o cc : Fin c) (kh : Fin kH) (kw : Fin kW) :
gradAt (fun (v' : Vec (c * c * kH * kW)) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v') b₂ x₁).flatten))))))) label) v (k4Idx o cc kh kw) = ∑ s : Fin (2 * h * (2 * w)), convPadWin kH kW x₁ cc kh kw s * cotWin (fun (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) => (if (conv2d (Kernel4.unflatten v) b₂ x₁).flatten (t3Idx ci hi wi) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten)) ci hi wi then dense (fun (j : Fin d₃) (i' : Fin (c * h * w)) => W₃ i' j) (fun (x : Fin (c * h * w)) => 0) (FloatModel.reluMask (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten))) (dense (fun (j : Fin d₄) (i' : Fin d₃) => W₄ i' j) (fun (x : Fin d₃) => 0) (FloatModel.reluMask (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten))))) (dense (fun (j : Fin nC) (i' : Fin d₄) => W₅ i' j) (fun (x : Fin d₄) => 0) fun (k : Fin nC) => softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten v) b₂ x₁).flatten))))))) k - oneHot nC label k)))) (t3Idx ci (winRow hi) (winCol wi)) else 0) o s

The certified conv-2 loss gradient, head restated in dense/reluMask form — the conv peer of mlp_input_loss_gradAt_reluMask. The two head Wᵀ contractions (under the d₄/d₃ ReLU masks) collapse via reluMask_dense_transpose_eq, the unmasked W₃ contraction via dense_transpose_eq; the conv-output ReLU mask 𝟙[z₂>0] and the pool argmax selector are kept explicit (their float closeness is handled by reluMask_close and MaxPool2MarginQ.poolBack_close). The whole conv gradient is then packaged as the spatial dot ∑ₛ convPadWin·cotWin (convWeightGrad_eq_dot) — the exact quantity the FloatModel gradient's conv-weight dot rounds, so the conv grad-close bounds against this.

theorem Proofs.head3_sum_drift {p d₃ d₄ nC : ℕ} (W₃ : Mat p d₃) (W₄ : Mat d₃ d₄) (W₅ : Mat d₄ nC) {w₃ w₄ w₅ Δ : ℝ} (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin p) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (m₃ : Fin d₃ → ℝ) (hm₃ : ∀ (l : Fin d₃), |m₃ l| ≤ 1) (m₄ : Fin d₄ → ℝ) (hm₄ : ∀ (r : Fin d₄), |m₄ r| ≤ 1) (s s' oh : Vec nC) (hs : ∀ (k : Fin nC), |s' k - s k| ≤ Δ) (q : Fin p) :
|∑ l : Fin d₃, W₃ q l * (m₃ l * ∑ r : Fin d₄, W₄ l r * (m₄ r * ∑ k : Fin nC, W₅ r k * (s' k - oh k))) - ∑ l : Fin d₃, W₃ q l * (m₃ l * ∑ r : Fin d₄, W₄ l r * (m₄ r * ∑ k : Fin nC, W₅ r k * (s k - oh k)))| ≤ ↑d₃ * (w₃ * (↑d₄ * (w₄ * (↑nC * (w₅ * Δ)))))

Frozen-mask head-gradient drift: with the two head masks frozen (0/1-valued, shared between the two points) and the softmax drifting by at most Δ, the head3 gradient closed form drifts by at most d₃·w₃·d₄·w₄·nC·w₅·Δ — the oneHot cancels in the difference.

noncomputable def Proofs.Conv2Slot.loss {P c h w d₃ d₄ nC : ℕ} (Z : Vec P → Vec (c * (2 * h) * (2 * w))) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) :
Vec P → ℝ

The loss of a conv2-slot parameter map: a parameter map Z into conv2's pre-activation, then relu, the 2×2 max-pool and the 3-dense head. Every CNN descent rung's loss has this shape: the conv2 kernel and bias (Z affine in the parameter), and through Conv1Slot the conv1 kernel and bias.

Equations
  • One or more equations did not get rendered due to their size.
Instances For
    theorem Proofs.Conv2Slot.maxPool_relu_eventuallyEq_gather {P c h w : ℕ} (Z : Vec P → Vec (c * (2 * h) * (2 * w))) (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (hT : ∀ (a b : Fin (2 * h) × Fin (2 * w)), T a b → ∀ (v' : Vec P) (ci : Fin c), Z v' (t3Idx ci a.1 a.2) = Z v' (t3Idx ci b.1 b.2)) (p : Vec P) (hZc : ContinuousAt Z p) (hp : ∀ (ci : Fin c) (ho : Fin h) (wo : Fin w), (∀ (cd : Fin 2 × Fin 2), Z p (t3Idx ci (winRowInv ho cd.1) (winColInv wo cd.2)) < 0) ∨ ∀ (cd : Fin 2 × Fin 2), cd = σ ci ho wo ∨ T (winRowInv ho (σ ci ho wo).1, winColInv wo (σ ci ho wo).2) (winRowInv ho cd.1, winColInv wo cd.2) ∨ Z p (t3Idx ci (winRowInv ho cd.1) (winColInv wo cd.2)) < Z p (t3Idx ci (winRowInv ho (σ ci ho wo).1) (winColInv wo (σ ci ho wo).2))) :
    ∀ᶠ (v' : Vec P) in nhds p, maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (Z v')) = poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) (Z v'))

    Near a point, the pooled loss is the gather loss. If at p every window of Z p is strictly negative, or has the cell σ selects strictly above every other cell except its T-twins (cells equal at every parameter value, hT), the strict inequalities persist on a neighbourhood and the twins stay equal, so the relu'd pool and the relu'd gather agree near p. This is where a tied window stops mattering: the pool has no derivative at a tie, but a tie between twins is a tie along every parameter direction, and the gather is linear.

    theorem Proofs.Conv2Slot.marginUpTo_seg {P c h w : ℕ} (Z : Vec P → Vec (c * (2 * h) * (2 * w))) {ρ D : ℝ} (hρ : 0 ≤ ρ) (hZ : ∀ (v e : Vec P) (k : Fin (c * (2 * h) * (2 * w))), |Z (v + e) k - Z v k| ≤ ρ * ∑ idx : Fin P, |e idx|) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (v d : Vec P) (hd : ∑ idx : Fin P, |d idx| ≤ D) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ρ * D < |Z v k|) (hmq : MaxPool2MarginQUpTo (ρ * D) T (Tensor3.unflatten (Z v))) (t : ℝ) (ht0 : 0 ≤ t) (ht1 : t ≤ 1) (ci : Fin c) (ho : Fin h) (wo : Fin w) :
    (∀ (cd : Fin 2 × Fin 2), Z (v + t • d) (t3Idx ci (winRowInv ho cd.1) (winColInv wo cd.2)) < 0) ∨ ∀ (cd : Fin 2 × Fin 2), cd = maxPool2Argmax (Tensor3.unflatten (Z v)) ci ho wo ∨ T (winRowInv ho (maxPool2Argmax (Tensor3.unflatten (Z v)) ci ho wo).1, winColInv wo (maxPool2Argmax (Tensor3.unflatten (Z v)) ci ho wo).2) (winRowInv ho cd.1, winColInv wo cd.2) ∨ Z (v + t • d) (t3Idx ci (winRowInv ho cd.1) (winColInv wo cd.2)) < Z (v + t • d) (t3Idx ci (winRowInv ho (maxPool2Argmax (Tensor3.unflatten (Z v)) ci ho wo).1) (winColInv wo (maxPool2Argmax (Tensor3.unflatten (Z v)) ci ho wo).2))

    The margin up to twins holds along the whole step segment, in strict form, at the selection σ = the base point's window argmax: a dead window stays strictly negative (the relu₂ margin), and a non-twin cell stays strictly below the selected one (the 2ρD gap against a per-entry drift of at most ρD).

    theorem Proofs.Conv2Slot.gather_grad_lipschitz {P c h w d₃ d₄ nC : ℕ} (σ : Fin c → Fin h → Fin w → Fin 2 × Fin 2) (Z : Vec P → Vec (c * (2 * h) * (2 * w))) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) {ρ w₃ w₄ w₅ D : ℝ} (hρ : 0 ≤ ρ) (hZ : ∀ (v e : Vec P) (k : Fin (c * (2 * h) * (2 * w))), |Z (v + e) k - Z v k| ≤ ρ * ∑ idx : Fin P, |e idx|) (hZ1 : ∀ (v e : Vec P), ∑ k : Fin (c * (2 * h) * (2 * w)), |Z (v + e) k - Z v k| ≤ ↑(2 * h * (2 * w)) * (ρ * ∑ idx : Fin P, |e idx|)) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (J : Fin c → Fin (2 * h) → Fin (2 * w) → ℝ) (hJ : ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), |J ci hi wi| ≤ ↑(2 * h * (2 * w)) * ρ) (idx : Fin P) (Q : Vec P → Prop) (hpd : ∀ (v' : Vec P), Q v' → DifferentiableAt ℝ Z v' ∧ ∀ (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)), pdiv Z v' idx (t3Idx ci hi wi) = J ci hi wi) (v d : Vec P) (hd : ∑ idx : Fin P, |d idx| ≤ D) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ρ * D < |Z v k|) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(2 * h * (2 * w)) * (ρ * D)) < |dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) (Z v))) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (ρ * D)))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) (Z v))))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (ρ * D))))))) < 1) (t : ℝ) (ht : t ∈ Set.Icc 0 1) (hQv : Q v) (hQt : Q (v + t • d)) :
    |gradAt (fun (v' : Vec P) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) (Z v')))))))) label) (v + t • d) idx - gradAt (fun (v' : Vec P) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (poolGatherFlat σ (relu (c * (2 * h) * (2 * w)) (Z v')))))))) label) v idx| ≤ 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 * ρ ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (ρ * D)))))))) * (t * D)

    Segment-Lipschitz gradient for a conv2-slot loss through a fixed gather, explicit constant. For a parameter map Z into conv2's pre-activation with per-entry drift ρ·‖e‖₁ (hZ) and ℓ1 drift (2h)·(2w)·ρ·‖e‖₁ (hZ1), differentiable with Jacobian row J (row mass ≤ (2h)·(2w)·ρ, hJ) wherever Q holds (hpd): with the pool replaced by the gather σ, under the relu₂, relu₃ and relu₄ margins at radius ρ·D every mask freezes along [v, v+d], the route is fixed by construction, the Jacobian factors out, and the difference collapses to the softmax drift. Q is needed only at the two ends of the segment (hQv, hQt): the conv2 rungs take Q := True, the conv1 slot Q = relu₁'s signs frozen. sgd_descends moves this onto the pooled loss.

    theorem Proofs.Conv2Slot.sgd_descends {P c h w d₃ d₄ nC : ℕ} (Z : Vec P → Vec (c * (2 * h) * (2 * w))) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (v gh : Vec P) {ρ lr η w₃ w₄ w₅ : ℝ} (hρ : 0 ≤ ρ) (hZ : ∀ (v e : Vec P) (k : Fin (c * (2 * h) * (2 * w))), |Z (v + e) k - Z v k| ≤ ρ * ∑ idx : Fin P, |e idx|) (hZ1 : ∀ (v e : Vec P), ∑ k : Fin (c * (2 * h) * (2 * w)), |Z (v + e) k - Z v k| ≤ ↑(2 * h * (2 * w)) * (ρ * ∑ idx : Fin P, |e idx|)) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (J : Fin P → Fin c → Fin (2 * h) → Fin (2 * w) → ℝ) (hJ : ∀ (idx : Fin P), ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), |J idx ci hi wi| ≤ ↑(2 * h * (2 * w)) * ρ) (Q : Vec P → Prop) (hpd : ∀ (v' : Vec P), Q v' → DifferentiableAt ℝ Z v' ∧ ∀ (idx : Fin P) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)), pdiv Z v' idx (t3Idx ci hi wi) = J idx ci hi wi) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (hT : ∀ (a b : Fin (2 * h) × Fin (2 * w)), T a b → ∀ (v' : Vec P) (ci : Fin c), Z v' (t3Idx ci a.1 a.2) = Z v' (t3Idx ci b.1 b.2)) (hlr : 0 ≤ lr) (hη : 0 ≤ η) (hgh : ∀ (idx : Fin P), |gh idx - gradAt (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v idx| ≤ η) (hQ : ∀ t ∈ Set.Icc 0 1, Q (v + t • -(lr • gh))) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ρ * stepRadius (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η < |Z v k|) (hmq : MaxPool2MarginQUpTo (ρ * stepRadius (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η) T (Tensor3.unflatten (Z v))) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(2 * h * (2 * w)) * (ρ * stepRadius (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η)) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (Z v))) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (ρ * stepRadius (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η)))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (Z v))))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (ρ * stepRadius (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η))))))) < 1) (h1 : lr * η * ∑ idx : Fin P, |gradAt (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v idx| ≤ (lr * ∑ idx : Fin P, gradAt (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v idx ^ 2) / 4) (h2 : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 * ρ ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (ρ * stepRadius (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η)))))))) * stepRadius (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η ^ 2 ≤ (lr * ∑ idx : Fin P, gradAt (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v idx ^ 2) / 4) :
    loss Z W₃ b₃ W₄ b₄ W₅ b₅ label (v - lr • gh) ≤ loss Z W₃ b₃ W₄ b₄ W₅ b₅ label v - (lr * ∑ idx : Fin P, gradAt (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v idx ^ 2) / 2

    One inexact SGD step through a conv2 slot decreases the loss — margin up to twins. For a parameter map Z into conv2's pre-activation with per-entry drift ρ·‖e‖₁ and ℓ1 drift (2h)·(2w)·ρ·‖e‖₁, differentiable with Jacobian rows J (row mass ≤ (2h)·(2w)·ρ) wherever Q holds along the step, and twins T (cells equal at every parameter value, hT): under the relu₂ margin, the pool margin up to twins MaxPool2MarginQUpTo, the relu₃ and relu₄ margins (all at radius ρ·D, D the step radius), the small-step condition and the two dominance conditions, one step with an η-accurate gradient oracle decreases loss by ≥ lr·‖∇loss‖₂²/2.

    The pool needs no derivative of its own. Fix σ = the base point's window argmax. Along the segment every window is dead or has σ's cell strictly above its non-twins (marginUpTo_seg), so near every segment point the loss IS the gather loss (maxPool_relu_eventuallyEq_gather); the gather loss is differentiable there and its gradient is segment-Lipschitz (gather_grad_lipschitz), and both facts transfer to loss through the local equality. Every CNN rung is this lemma at its Z, ρ, J and Q.

    theorem Proofs.Conv2Slot.marginUpTo_strict {c h w : ℕ} {δ : ℝ} (hδ : 0 ≤ δ) (x : Vec (c * (2 * h) * (2 * w))) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (hne : ∀ (k : Fin (c * (2 * h) * (2 * w))), x k ≠ 0) (hmq : MaxPool2MarginQUpTo δ T (Tensor3.unflatten x)) (ci : Fin c) (ho : Fin h) (wo : Fin w) :
    (∀ (cd : Fin 2 × Fin 2), x (t3Idx ci (winRowInv ho cd.1) (winColInv wo cd.2)) < 0) ∨ ∀ (cd : Fin 2 × Fin 2), cd = maxPool2Argmax (Tensor3.unflatten x) ci ho wo ∨ T (winRowInv ho (maxPool2Argmax (Tensor3.unflatten x) ci ho wo).1, winColInv wo (maxPool2Argmax (Tensor3.unflatten x) ci ho wo).2) (winRowInv ho cd.1, winColInv wo cd.2) ∨ x (t3Idx ci (winRowInv ho cd.1) (winColInv wo cd.2)) < x (t3Idx ci (winRowInv ho (maxPool2Argmax (Tensor3.unflatten x) ci ho wo).1) (winColInv wo (maxPool2Argmax (Tensor3.unflatten x) ci ho wo).2))

    The margin up to twins, strict at its own point: with every cell nonzero and δ ≥ 0, a dead window is strictly negative and every non-twin cell is strictly below the window argmax. marginUpTo_seg at the base point, without the drift.

    theorem Proofs.Conv2Slot.gradAt_abs_le {P c h w d₃ d₄ nC : ℕ} (Z : Vec P → Vec (c * (2 * h) * (2 * w))) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) {δ ρ w₃ w₄ w₅ : ℝ} (hδ : 0 ≤ δ) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (J : Fin c → Fin (2 * h) → Fin (2 * w) → ℝ) (hJ : ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), |J ci hi wi| ≤ ↑(2 * h * (2 * w)) * ρ) (idx : Fin P) (v : Vec P) (hZd : DifferentiableAt ℝ Z v) (hpdJ : ∀ (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)), pdiv Z v idx (t3Idx ci hi wi) = J ci hi wi) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (hT : ∀ (a b : Fin (2 * h) × Fin (2 * w)), T a b → ∀ (v' : Vec P) (ci : Fin c), Z v' (t3Idx ci a.1 a.2) = Z v' (t3Idx ci b.1 b.2)) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), Z v k ≠ 0) (hmq : MaxPool2MarginQUpTo δ T (Tensor3.unflatten (Z v))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (Z v))) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (Z v))))) q ≠ 0) :
    |gradAt (loss Z W₃ b₃ W₄ b₄ W₅ b₅ label) v idx| ≤ ↑(2 * h * (2 * w)) * ρ * (↑d₃ * (w₃ * (↑d₄ * (w₄ * (↑nC * (w₅ * 1))))))

    The loss gradient is at most the Jacobian row mass times the head's operator norm. At a point where the pool margin up to twins holds (at any δ ≥ 0) and every ReLU stage is off its kink, the loss is the gather loss near the point, so its gradient is Z's Jacobian row contracted with the gather head's gradient (gather_loss_gradAt); |softmax − onehot| ≤ 1 leaves at most d₃·w₃·d₄·w₄·nC·w₅ per cell of the row. The bound a concrete instance uses for the step radius, which it cannot compute (the softmax is transcendental).

    noncomputable def Proofs.cnnConv2KernelLoss {c h w d₃ d₄ nC kH kW : ℕ} (b₂ : Vec c) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) :
    Vec (c * c * kH * kW) → ℝ

    The loss as a function of the flattened second-conv kernel.

    Equations
    • One or more equations did not get rendered due to their size.
    Instances For
      theorem Proofs.cnn_conv2_sgd_descends {c h w d₃ d₄ nC kH kW : ℕ} (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (gh : Vec (c * c * kH * kW)) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) {lr η a w₃ w₄ w₅ : ℝ} (ha : 0 ≤ a) (hx : ∀ (cc : Fin c) (i : Fin (2 * h)) (j : Fin (2 * w)), |x₁ cc i j| ≤ a) (hT : ∀ (p q : Fin (2 * h) × Fin (2 * w)), T p q → ConvPatchEq kH kW x₁ p q) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (hlr : 0 ≤ lr) (hη : 0 ≤ η) (hgh : ∀ (idx : Fin (c * c * kH * kW)), |gh idx - gradAt (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten idx| ≤ η) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), a * stepRadius (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten lr η < |(conv2d W₂ b₂ x₁).flatten k|) (hmq : MaxPool2MarginQUpTo (a * stepRadius (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten lr η) T (conv2d W₂ b₂ x₁)) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(2 * h * (2 * w)) * (a * stepRadius (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten lr η)) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (a * stepRadius (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten lr η)))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (a * stepRadius (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten lr η))))))) < 1) (h1 : lr * η * ∑ idx : Fin (c * c * kH * kW), |gradAt (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten idx| ≤ (lr * ∑ idx : Fin (c * c * kH * kW), gradAt (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten idx ^ 2) / 4) (h2 : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 * a ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (a * stepRadius (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten lr η)))))))) * stepRadius (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten lr η ^ 2 ≤ (lr * ∑ idx : Fin (c * c * kH * kW), gradAt (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten idx ^ 2) / 4) :
      cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label (W₂.flatten - lr • gh) ≤ cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label W₂.flatten - (lr * ∑ idx : Fin (c * c * kH * kW), gradAt (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten idx ^ 2) / 2

      One inexact SGD step on the CNN's second conv kernel decreases one example's cross-entropy loss (example (x₁, label) at the frozen conv-2 input, W₂ moving, every other parameter fixed). Conv2Slot.sgd_descends at the conv2 kernel map (ρ = a, the point-free Jacobian conv2d_weight_pdiv). The pool hypothesis is the margin up to twins MaxPool2MarginQUpTo on the pre-activation conv2d W₂ b₂ x₁, twins being cells whose zero-padded input patches are identical (hT, ConvPatchEq): those are equal for every kernel, so a window of them stays tied along the step and costs nothing. That is the condition real MNIST meets at the trained Chapter-3 weights, where a constant background patch ties a window's cells (scripts/probes/mnist_pool_twin_probe.py). The remaining hypotheses: the oracle accuracy η, the relu₂/relu₃/relu₄ margins at the step radius D = lr·(‖∇L‖₁ + |kernel|·η), the small-step condition and the two dominance conditions. Conclusion: the loss drops by ≥ lr·‖∇L‖₂²/2. The conv-layer peer of mlp_input_sgd_descends.

      noncomputable def Proofs.cnnConv2GradBound (c h w d₃ d₄ nC kH kW : ℕ) (a w₃ w₄ w₅ : ℝ) :

      The explicit ℓ1 bound on the conv2-kernel loss gradient, (c·c·kH·kW)·((2h)·(2w)·a)· (d₃·w₃·d₄·w₄·nC·w₅): Conv2Slot.gradAt_abs_le per kernel entry, times the number of entries. Named so cnn_conv2_exact_sgd_descends can state its radius once.

      Equations
      Instances For
        theorem Proofs.cnn_conv2_exact_sgd_descends {c h w d₃ d₄ nC kH kW : ℕ} (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) {lr a w₃ w₄ w₅ : ℝ} (ha : 0 ≤ a) (hx : ∀ (cc : Fin c) (i : Fin (2 * h)) (j : Fin (2 * w)), |x₁ cc i j| ≤ a) (hT : ∀ (p q : Fin (2 * h) × Fin (2 * w)), T p q → ConvPatchEq kH kW x₁ p q) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (hlr : 0 ≤ lr) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), a * (lr * cnnConv2GradBound c h w d₃ d₄ nC kH kW a w₃ w₄ w₅) < |(conv2d W₂ b₂ x₁).flatten k|) (hmq : MaxPool2MarginQUpTo (a * (lr * cnnConv2GradBound c h w d₃ d₄ nC kH kW a w₃ w₄ w₅)) T (conv2d W₂ b₂ x₁)) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(2 * h * (2 * w)) * (a * (lr * cnnConv2GradBound c h w d₃ d₄ nC kH kW a w₃ w₄ w₅))) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (a * (lr * cnnConv2GradBound c h w d₃ d₄ nC kH kW a w₃ w₄ w₅))))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (a * (lr * cnnConv2GradBound c h w d₃ d₄ nC kH kW a w₃ w₄ w₅)))))))) < 1) (hC : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 * a ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (a * (lr * cnnConv2GradBound c h w d₃ d₄ nC kH kW a w₃ w₄ w₅))))))))) * lr * ↑(c * c * kH * kW) ≤ 1 / 4) :
        cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label (W₂.flatten - lr • gradAt (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten) ≤ cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label W₂.flatten - (lr * ∑ idx : Fin (c * c * kH * kW), gradAt (cnnConv2KernelLoss b₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) W₂.flatten idx ^ 2) / 2

        One exact-gradient SGD step on the conv2 kernel decreases the loss, every hypothesis at an explicit radius. cnn_conv2_sgd_descends at the exact gradient (η = 0), with the step radius lr·‖∇L‖₁ replaced by its upper bound lr·cnnConv2GradBound and the second dominance condition by C·lr·(c·c·kH·kW) ≤ 1/4 (Cauchy–Schwarz, (Σ|g|)² ≤ (c·c·kH·kW)·Σg²). No hypothesis mentions the gradient: each is a statement about the weights, the input and lr, which a concrete instance checks by exact arithmetic (Trained.CnnDescent).

        theorem Proofs.Conv1Slot.sgd_descends {P c h w d₃ d₄ nC kH kW : ℕ} (Z : Vec P → Vec (c * (2 * h) * (2 * w))) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (v gh : Vec P) {ρ lr η w₂ w₃ w₄ w₅ : ℝ} (hρ : 0 ≤ ρ) (hZ : ∀ (v e : Vec P) (k : Fin (c * (2 * h) * (2 * w))), |Z (v + e) k - Z v k| ≤ ρ * ∑ idx : Fin P, |e idx|) (hZ1 : ∀ (v e : Vec P), ∑ k : Fin (c * (2 * h) * (2 * w)), |Z (v + e) k - Z v k| ≤ ↑(2 * h * (2 * w)) * (ρ * ∑ idx : Fin P, |e idx|)) (hw₂ : 0 ≤ w₂) (hW₂ : ∀ (o cc : Fin c) (kh : Fin kH) (kw : Fin kW), |W₂ o cc kh kw| ≤ w₂) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (J : Fin P → Fin c → Fin (2 * h) → Fin (2 * w) → ℝ) (hJ : ∀ (idx : Fin P), ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), |J idx ci hi wi| ≤ ↑(2 * h * (2 * w)) * ρ) (hpd : ∀ (v' : Vec P), DifferentiableAt ℝ Z v' ∧ ∀ (idx : Fin P) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)), pdiv Z v' idx (t3Idx ci hi wi) = J idx ci hi wi) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (hT : ∀ (a b : Fin (2 * h) × Fin (2 * w)), T a b → ∀ (v' : Vec P) (ci : Fin c), (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten (t3Idx ci a.1 a.2) = (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten (t3Idx ci b.1 b.2)) (hlr : 0 ≤ lr) (hη : 0 ≤ η) (hgh : ∀ (idx : Fin P), |gh idx - gradAt (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v idx| ≤ η) (hm1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ρ * stepRadius (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η < |Z v k|) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ↑(c * kH * kW) * (w₂ * (ρ * stepRadius (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η)) < |(conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v)))).flatten k|) (hmq : MaxPool2MarginQUpTo (↑(c * kH * kW) * (w₂ * (ρ * stepRadius (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η))) T (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v))))) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (ρ * stepRadius (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η)))) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v)))).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (ρ * stepRadius (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η)))))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v)))).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (ρ * stepRadius (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η))))))))) < 1) (h1 : lr * η * ∑ idx : Fin P, |gradAt (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v idx| ≤ (lr * ∑ idx : Fin P, gradAt (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v idx ^ 2) / 4) (h2 : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑(c * kH * kW) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₂ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 * ρ ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (ρ * stepRadius (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η)))))))))) * stepRadius (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v lr η ^ 2 ≤ (lr * ∑ idx : Fin P, gradAt (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v idx ^ 2) / 4) :
        Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label (v - lr • gh) ≤ Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label v - (lr * ∑ idx : Fin P, gradAt (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v idx ^ 2) / 2

        One inexact SGD step through a conv1 slot decreases the loss. For a parameter map Z into conv1's pre-activation with per-entry drift ρ·‖e‖₁ and ℓ1 drift (2h)·(2w)·ρ·‖e‖₁, differentiable everywhere with a point-free Jacobian J (row mass ≤ (2h)·(2w)·ρ): the relu₁ margin freezes relu₁'s mask along the step, so conv2's pre-activation conv2 ∘ relu ∘ Z moves by c·kH·kW·w₂·ρ·‖e‖₁ per entry (z2_entry_drift), and at every point of the step its Jacobian is one fixed row: J, the base mask and conv2's taps (convTap, locality c·kH·kW·w₂). The rest is Conv2Slot.sgd_descends at that map, radius c·kH·kW·w₂·ρ, with Q = relu₁'s signs frozen. The conv1-kernel rung is the instance ρ = a, the conv1-bias rung ρ = 1.

        theorem Proofs.Conv1Slot.gradAt_abs_le {P c h w d₃ d₄ nC kH kW : ℕ} (Z : Vec P → Vec (c * (2 * h) * (2 * w))) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) {δ ρ w₂ w₃ w₄ w₅ : ℝ} (hδ : 0 ≤ δ) (hw₂ : 0 ≤ w₂) (hW₂ : ∀ (o cc : Fin c) (kh : Fin kH) (kw : Fin kW), |W₂ o cc kh kw| ≤ w₂) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (J : Fin P → Fin c → Fin (2 * h) → Fin (2 * w) → ℝ) (hJ : ∀ (idx : Fin P), ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), |J idx ci hi wi| ≤ ↑(2 * h * (2 * w)) * ρ) (hpd : ∀ (v' : Vec P), DifferentiableAt ℝ Z v' ∧ ∀ (idx : Fin P) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)), pdiv Z v' idx (t3Idx ci hi wi) = J idx ci hi wi) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) (hT : ∀ (a b : Fin (2 * h) × Fin (2 * w)), T a b → ∀ (v' : Vec P) (ci : Fin c), (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten (t3Idx ci a.1 a.2) = (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten (t3Idx ci b.1 b.2)) (idx : Fin P) (v : Vec P) (hz1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), Z v k ≠ 0) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v)))).flatten k ≠ 0) (hmq : MaxPool2MarginQUpTo δ T (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v))))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v)))).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v)))).flatten)))) q ≠ 0) :
        |gradAt (Conv2Slot.loss (fun (v' : Vec P) => (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (Z v')))).flatten) W₃ b₃ W₄ b₄ W₅ b₅ label) v idx| ≤ ↑(2 * h * (2 * w)) * (↑(c * kH * kW) * (w₂ * ρ)) * (↑d₃ * (w₃ * (↑d₄ * (w₄ * (↑nC * (w₅ * 1))))))

        The conv1-slot loss gradient is at most conv2's row mass times the head's operator norm. Conv2Slot.gradAt_abs_le at the map conv2 ∘ relu ∘ Z: at a point where relu₁ is off its kink, that map's Jacobian is the fixed row of z2_pdiv, of mass at most (2h)·(2w)·c·kH·kW·w₂·ρ (z2_row_l1). The bound the conv1 exact-gradient corollaries use for their step radius.

        theorem Proofs.cnn1_pool_head_input_grad {c h w d₃ d₄ nC kH kW : ℕ} (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (z₁ : Vec (c * (2 * h) * (2 * w))) (hz1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), z₁ k ≠ 0) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten)))) q ≠ 0) (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) :
        pdiv (fun (y : Vec (c * (2 * h) * (2 * w))) (x : Fin 1) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) y))).flatten))))))) label) z₁ (t3Idx ci hi wi) 0 = (if z₁ (t3Idx ci hi wi) > 0 then 1 else 0) * ∑ co : Fin c, ∑ ho : Fin (2 * h), ∑ wo : Fin (2 * w), convTap W₂ ci hi wi co ho wo * ((if (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten (t3Idx co ho wo) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten)) co ho wo then ∑ l : Fin d₃, W₃ (t3Idx co (winRow ho) (winCol wo)) l * ((if dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten)) l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten)))) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) z₁))).flatten))))))) k - oneHot nC label k))) else 0)

        Loss input-gradient at the conv1 output — the conv1 peer of pool_relu_input_grad. One more relu mask and one conv-as-input crossing: the chain picks up relu'(z₁) and contracts the point-free tap Jacobian of conv2 with the pool-collapsed conv2-rung gradient.

        theorem Proofs.cnn_conv1_loss_gradAt {ic c h w d₃ d₄ nC kH kW : ℕ} (b₁ : Vec c) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (u : Vec (c * ic * kH * kW)) (hz1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d (Kernel4.unflatten u) b₁ x₀).flatten k ≠ 0) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten)))) q ≠ 0) (o : Fin c) (cc : Fin ic) (kh : Fin kH) (kw : Fin kW) :
        gradAt (fun (u' : Vec (c * ic * kH * kW)) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u') b₁ x₀).flatten))).flatten))))))) label) u (k4Idx o cc kh kw) = ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), (if ci = o then convPad kH kW x₀ cc kh kw hi wi else 0) * ((if (conv2d (Kernel4.unflatten u) b₁ x₀).flatten (t3Idx ci hi wi) > 0 then 1 else 0) * ∑ co : Fin c, ∑ ho : Fin (2 * h), ∑ wo : Fin (2 * w), convTap W₂ ci hi wi co ho wo * ((if (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten (t3Idx co ho wo) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten)) co ho wo then ∑ l : Fin d₃, W₃ (t3Idx co (winRow ho) (winCol wo)) l * ((if dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten)) l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten)))) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten))))))) k - oneHot nC label k))) else 0))

        Closed form of the conv1 loss gradient at any five-margin point — the same chain rule, contracted with the conv1 head gradient (cnn1_pool_head_input_grad): the conv1 weight Jacobian (convPad reads of the IMAGE) times relu₁'s mask times the point-free conv2 tap Jacobian times the pool-collapsed head. Two spatial triple-sums: weight sharing at conv1, locality at conv2.

        theorem Proofs.cnn_conv1_loss_gradAt_reluMask {ic c h w d₃ d₄ nC kH kW : ℕ} (b₁ : Vec c) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (u : Vec (c * ic * kH * kW)) (hz1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d (Kernel4.unflatten u) b₁ x₀).flatten k ≠ 0) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten)))) q ≠ 0) (o : Fin c) (cc : Fin ic) (kh : Fin kH) (kw : Fin kW) :
        gradAt (fun (u' : Vec (c * ic * kH * kW)) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u') b₁ x₀).flatten))).flatten))))))) label) u (k4Idx o cc kh kw) = ∑ s : Fin (2 * h * (2 * w)), convPadWin kH kW x₀ cc kh kw s * cotWin (fun (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) => (if (conv2d (Kernel4.unflatten u) b₁ x₀).flatten (t3Idx ci hi wi) > 0 then 1 else 0) * ∑ co : Fin c, ∑ ho : Fin (2 * h), ∑ wo : Fin (2 * w), convTap W₂ ci hi wi co ho wo * ((if (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten (t3Idx co ho wo) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten)) co ho wo then dense (fun (j : Fin d₃) (i' : Fin (c * h * w)) => W₃ i' j) (fun (x : Fin (c * h * w)) => 0) (FloatModel.reluMask (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten))) (dense (fun (j : Fin d₄) (i' : Fin d₃) => W₄ i' j) (fun (x : Fin d₃) => 0) (FloatModel.reluMask (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten))))) (dense (fun (j : Fin nC) (i' : Fin d₄) => W₅ i' j) (fun (x : Fin d₄) => 0) fun (k : Fin nC) => softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d (Kernel4.unflatten u) b₁ x₀).flatten))).flatten))))))) k - oneHot nC label k)))) (t3Idx co (winRow ho) (winCol wo)) else 0)) o s

        The certified conv-1 loss gradient, head restated in dense/reluMask form — the conv-1 peer of cnn_conv2_loss_gradAt_reluMask. One conv-backward deeper than conv-2: the conv-1-output cotangent is 𝟙[z₁>0] · ∑_{co,ho,wo} convTap·(conv-2-output cotangent), with the 3-dense head collapsed by head3_cot_reluMask exactly as in conv-2. The conv-1 ReLU mask, the conv-2 backward tap (convTap, the point-free conv-2 input Jacobian), the conv-2 ReLU mask and the pool selector all stay explicit (their float closeness is mask_scalar_close / dot_perturbed_close / poolBack_close). Packaged as the spatial dot ∑ₛ convPadWin x₀·cotWin (convWeightGrad_eq_dot) the float conv-1 weight dot rounds.

        theorem Proofs.ConvPatchEq2.symm {ic h w kH kW : ℕ} {x : Tensor3 ic h w} {p q : Fin h × Fin w} (hpq : ConvPatchEq2 kH kW x p q) :
        ConvPatchEq2 kH kW x q p

        Two-layer twins are symmetric.

        theorem Proofs.ConvPatchEq2.trans {ic h w kH kW : ℕ} {x : Tensor3 ic h w} {p q u : Fin h × Fin w} (hpq : ConvPatchEq2 kH kW x p q) (hqu : ConvPatchEq2 kH kW x q u) :
        ConvPatchEq2 kH kW x p u

        Two-layer twins are transitive: the middle cell's outer reads are in bounds exactly when the ends' are. With ConvPatchEq2.symm, the equivalence windowMarginUpTo_of_cert asks of the twin relation.

        noncomputable def Proofs.cnnConv1KernelLoss {ic c h w d₃ d₄ nC kH kW : ℕ} (b₁ : Vec c) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) :
        Vec (c * ic * kH * kW) → ℝ

        The loss as a function of the flattened first-conv kernel.

        Equations
        • One or more equations did not get rendered due to their size.
        Instances For
          theorem Proofs.cnn_conv1_sgd_descends {ic c h w d₃ d₄ nC kH kW : ℕ} (W₁ : Kernel4 c ic kH kW) (b₁ : Vec c) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (gh : Vec (c * ic * kH * kW)) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) {lr η a w₂ w₃ w₄ w₅ : ℝ} (ha : 0 ≤ a) (hx : ∀ (cc : Fin ic) (i : Fin (2 * h)) (j : Fin (2 * w)), |x₀ cc i j| ≤ a) (hT : ∀ (p q : Fin (2 * h) × Fin (2 * w)), T p q → ConvPatchEq2 kH kW x₀ p q) (hw₂ : 0 ≤ w₂) (hW₂ : ∀ (o cc : Fin c) (kh : Fin kH) (kw : Fin kW), |W₂ o cc kh kw| ≤ w₂) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (hlr : 0 ≤ lr) (hη : 0 ≤ η) (hgh : ∀ (idx : Fin (c * ic * kH * kW)), |gh idx - gradAt (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten idx| ≤ η) (hm1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), a * stepRadius (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten lr η < |(conv2d W₁ b₁ x₀).flatten k|) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ↑(c * kH * kW) * (w₂ * (a * stepRadius (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten lr η)) < |(conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten k|) (hmq : MaxPool2MarginQUpTo (↑(c * kH * kW) * (w₂ * (a * stepRadius (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten lr η))) T (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten)))) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (a * stepRadius (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten lr η)))) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (a * stepRadius (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten lr η)))))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (a * stepRadius (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten lr η))))))))) < 1) (h1 : lr * η * ∑ idx : Fin (c * ic * kH * kW), |gradAt (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten idx| ≤ (lr * ∑ idx : Fin (c * ic * kH * kW), gradAt (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten idx ^ 2) / 4) (h2 : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑(c * kH * kW) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₂ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 * a ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (a * stepRadius (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten lr η)))))))))) * stepRadius (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten lr η ^ 2 ≤ (lr * ∑ idx : Fin (c * ic * kH * kW), gradAt (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten idx ^ 2) / 4) :
          cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label (W₁.flatten - lr • gh) ≤ cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label W₁.flatten - (lr * ∑ idx : Fin (c * ic * kH * kW), gradAt (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten idx ^ 2) / 2

          One inexact SGD step on the CNN's FIRST conv kernel decreases one example's cross-entropy loss (W₁ moving, every other parameter fixed). The deepest rung: the step crosses relu₁, conv2 (as a function of its input — the point-free tap Jacobian with locality factor c·kH·kW·w₂), relu₂, the pool, and the 3-dense head. Conv1Slot.sgd_descends at the conv1 kernel map (ρ = a). Under the FIVE margins at the step radius D = lr·(‖∇L‖₁ + |kernel|·η), the pool's taken up to two-layer twins (ConvPatchEq2: cells whose two-conv receptive fields in x₀ are identical, equal for every W₁), every mask freezes along the step, the pool agrees with a fixed gather near every point of it, and the loss drops by ≥ lr·‖∇L‖₂²/2.

          noncomputable def Proofs.cnnConv1GradBound (ic c h w d₃ d₄ nC kH kW : ℕ) (a w₂ w₃ w₄ w₅ : ℝ) :

          The explicit ℓ1 bound on the conv1-kernel loss gradient, (c·ic·kH·kW)·((2h)·(2w)· (c·kH·kW·w₂·a))·(d₃·w₃·d₄·w₄·nC·w₅): Conv1Slot.gradAt_abs_le at ρ = a per kernel entry, times the number of entries.

          Equations
          • Proofs.cnnConv1GradBound ic c h w d₃ d₄ nC kH kW a w₂ w₃ w₄ w₅ = ↑(c * ic * kH * kW) * (↑(2 * h * (2 * w)) * (↑(c * kH * kW) * (w₂ * a)) * (↑d₃ * (w₃ * (↑d₄ * (w₄ * (↑nC * (w₅ * 1)))))))
          Instances For
            theorem Proofs.cnn_conv1_exact_sgd_descends {ic c h w d₃ d₄ nC kH kW : ℕ} (W₁ : Kernel4 c ic kH kW) (b₁ : Vec c) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) {lr a w₂ w₃ w₄ w₅ : ℝ} (ha : 0 ≤ a) (hx : ∀ (cc : Fin ic) (i : Fin (2 * h)) (j : Fin (2 * w)), |x₀ cc i j| ≤ a) (hT : ∀ (p q : Fin (2 * h) × Fin (2 * w)), T p q → ConvPatchEq2 kH kW x₀ p q) (hw₂ : 0 ≤ w₂) (hW₂ : ∀ (o cc : Fin c) (kh : Fin kH) (kw : Fin kW), |W₂ o cc kh kw| ≤ w₂) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (hlr : 0 ≤ lr) (hm1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), a * (lr * cnnConv1GradBound ic c h w d₃ d₄ nC kH kW a w₂ w₃ w₄ w₅) < |(conv2d W₁ b₁ x₀).flatten k|) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ↑(c * kH * kW) * (w₂ * (a * (lr * cnnConv1GradBound ic c h w d₃ d₄ nC kH kW a w₂ w₃ w₄ w₅))) < |(conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten k|) (hmq : MaxPool2MarginQUpTo (↑(c * kH * kW) * (w₂ * (a * (lr * cnnConv1GradBound ic c h w d₃ d₄ nC kH kW a w₂ w₃ w₄ w₅)))) T (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten)))) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (a * (lr * cnnConv1GradBound ic c h w d₃ d₄ nC kH kW a w₂ w₃ w₄ w₅))))) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (a * (lr * cnnConv1GradBound ic c h w d₃ d₄ nC kH kW a w₂ w₃ w₄ w₅))))))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (a * (lr * cnnConv1GradBound ic c h w d₃ d₄ nC kH kW a w₂ w₃ w₄ w₅)))))))))) < 1) (hC : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑(c * kH * kW) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₂ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 * a ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (a * (lr * cnnConv1GradBound ic c h w d₃ d₄ nC kH kW a w₂ w₃ w₄ w₅))))))))))) * lr * ↑(c * ic * kH * kW) ≤ 1 / 4) :
            cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label (W₁.flatten - lr • gradAt (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten) ≤ cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label W₁.flatten - (lr * ∑ idx : Fin (c * ic * kH * kW), gradAt (cnnConv1KernelLoss b₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) W₁.flatten idx ^ 2) / 2

            One exact-gradient SGD step on the conv1 kernel decreases the loss, every hypothesis at an explicit radius. cnn_conv1_sgd_descends at η = 0, the step radius bounded by lr·cnnConv1GradBound and the second dominance condition by C·lr·(c·ic·kH·kW) ≤ 1/4, as in cnn_conv2_exact_sgd_descends. The twins are two-layer (ConvPatchEq2).

            theorem Proofs.cnn_conv2_bias_loss_gradAt {c h w d₃ d₄ nC kH kW : ℕ} (W₂ : Kernel4 c c kH kW) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (b : Vec c) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₂ b x₁).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten)))) q ≠ 0) (o : Fin c) :
            gradAt (fun (b' : Vec c) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b' x₁).flatten))))))) label) b o = ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), (if ci = o then 1 else 0) * ((if (conv2d W₂ b x₁).flatten (t3Idx ci hi wi) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten)) ci hi wi then ∑ l : Fin d₃, W₃ (t3Idx ci (winRow hi) (winCol wi)) l * ((if dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten)) l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten)))) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten))))))) k - oneHot nC label k))) else 0)

            Closed form of the conv2 bias loss gradient at any four-margin point — the chain rule through the conv bias map (gradAt_comp_t3) with the pool-collapsed head gradient (pool_relu_input_grad, reused verbatim) and the Kronecker bias Jacobian (conv2d_bias_pdiv).

            theorem Proofs.cnn_conv2_bias_loss_gradAt_reluMask {c h w d₃ d₄ nC kH kW : ℕ} (W₂ : Kernel4 c c kH kW) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (b : Vec c) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₂ b x₁).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten)))) q ≠ 0) (o : Fin c) :
            gradAt (fun (b' : Vec c) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b' x₁).flatten))))))) label) b o = ∑ s : Fin (2 * h * (2 * w)), cotWin (fun (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) => (if (conv2d W₂ b x₁).flatten (t3Idx ci hi wi) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten)) ci hi wi then dense (fun (j : Fin d₃) (i' : Fin (c * h * w)) => W₃ i' j) (fun (x : Fin (c * h * w)) => 0) (FloatModel.reluMask (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten))) (dense (fun (j : Fin d₄) (i' : Fin d₃) => W₄ i' j) (fun (x : Fin d₃) => 0) (FloatModel.reluMask (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten))))) (dense (fun (j : Fin nC) (i' : Fin d₄) => W₅ i' j) (fun (x : Fin d₄) => 0) fun (k : Fin nC) => softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b x₁).flatten))))))) k - oneHot nC label k)))) (t3Idx ci (winRow hi) (winCol wi)) else 0) o s

            The certified conv-2 BIAS loss gradient, restated as the spatial SUM of the reluMask-form cotangent — the bias peer of cnn_conv2_loss_gradAt_reluMask. The 3-dense head collapses via head3_cot_reluMask; the channel-Kronecker Jacobian if ci = o collapses the ∑ ci to ci = o (Finset.sum_ite_eq'); the remaining spatial ∑ hi wi is packaged as ∑ s, cotWin c o s (convBiasGrad_eq_sum) — the quantity the FloatModel gradient's bias SUM rounds.

            noncomputable def Proofs.cnnConv2BiasLoss {c h w d₃ d₄ nC kH kW : ℕ} (W₂ : Kernel4 c c kH kW) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) :
            Vec c → ℝ

            The loss as a function of the second-conv bias.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For
              theorem Proofs.cnn_conv2_bias_sgd_descends {c h w d₃ d₄ nC kH kW : ℕ} (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (gh : Vec c) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) {lr η w₃ w₄ w₅ : ℝ} (hT : ∀ (p q : Fin (2 * h) × Fin (2 * w)), T p q → ConvPatchEq kH kW x₁ p q) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (hlr : 0 ≤ lr) (hη : 0 ≤ η) (hgh : ∀ (o : Fin c), |gh o - gradAt (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ o| ≤ η) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), stepRadius (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ lr η < |(conv2d W₂ b₂ x₁).flatten k|) (hmq : MaxPool2MarginQUpTo (stepRadius (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ lr η) T (conv2d W₂ b₂ x₁)) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(2 * h * (2 * w)) * stepRadius (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ lr η) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * stepRadius (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ lr η))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * stepRadius (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ lr η)))))) < 1) (h1 : lr * η * ∑ o : Fin c, |gradAt (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ o| ≤ (lr * ∑ o : Fin c, gradAt (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ o ^ 2) / 4) (h2 : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * stepRadius (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ lr η))))))) * stepRadius (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ lr η ^ 2 ≤ (lr * ∑ o : Fin c, gradAt (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ o ^ 2) / 4) :
              cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label (b₂ - lr • gh) ≤ crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten))))))) label - (lr * ∑ o : Fin c, gradAt (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ o ^ 2) / 2

              One inexact SGD step on the CNN's second conv BIAS decreases one example's cross-entropy loss (b₂ moving, every other parameter fixed). Conv2Slot.sgd_descends at the conv2 bias map (ρ = 1): the four margins at the step radius D = lr·(‖∇L‖₁ + c·η) carry no input bound a (the bias Jacobian is a Kronecker indicator), the pool's taken up to the same twins as the kernel rung (ConvPatchEq; twin cells are equal for every bias too), and the parameter needs no flatten/unflatten plumbing.

              noncomputable def Proofs.cnnConv2BiasGradBound (c h w d₃ d₄ nC : ℕ) (w₃ w₄ w₅ : ℝ) :

              The explicit ℓ1 bound on the conv2-bias loss gradient, c·((2h)·(2w)·1)· (d₃·w₃·d₄·w₄·nC·w₅): Conv2Slot.gradAt_abs_le at ρ = 1 per bias entry, times the number of entries.

              Equations
              Instances For
                theorem Proofs.cnn_conv2_bias_exact_sgd_descends {c h w d₃ d₄ nC kH kW : ℕ} (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (x₁ : Tensor3 c (2 * h) (2 * w)) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) {lr w₃ w₄ w₅ : ℝ} (hT : ∀ (p q : Fin (2 * h) × Fin (2 * w)), T p q → ConvPatchEq kH kW x₁ p q) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (hlr : 0 ≤ lr) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), lr * cnnConv2BiasGradBound c h w d₃ d₄ nC w₃ w₄ w₅ < |(conv2d W₂ b₂ x₁).flatten k|) (hmq : MaxPool2MarginQUpTo (lr * cnnConv2BiasGradBound c h w d₃ d₄ nC w₃ w₄ w₅) T (conv2d W₂ b₂ x₁)) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(2 * h * (2 * w)) * (lr * cnnConv2BiasGradBound c h w d₃ d₄ nC w₃ w₄ w₅)) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (lr * cnnConv2BiasGradBound c h w d₃ d₄ nC w₃ w₄ w₅)))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ x₁).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (lr * cnnConv2BiasGradBound c h w d₃ d₄ nC w₃ w₄ w₅))))))) < 1) (hC : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(2 * h * (2 * w)) * (lr * cnnConv2BiasGradBound c h w d₃ d₄ nC w₃ w₄ w₅)))))))) * lr * ↑c ≤ 1 / 4) :
                cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label (b₂ - lr • gradAt (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂) ≤ cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label b₂ - (lr * ∑ o : Fin c, gradAt (cnnConv2BiasLoss W₂ x₁ W₃ b₃ W₄ b₄ W₅ b₅ label) b₂ o ^ 2) / 2

                One exact-gradient SGD step on the conv2 bias decreases the loss, every hypothesis at an explicit radius. cnn_conv2_bias_sgd_descends at η = 0, the step radius bounded by lr·cnnConv2BiasGradBound and the second dominance condition by C·lr·c ≤ 1/4, as in cnn_conv2_exact_sgd_descends.

                theorem Proofs.cnn_conv1_bias_loss_gradAt {ic c h w d₃ d₄ nC kH kW : ℕ} (W₁ : Kernel4 c ic kH kW) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (b : Vec c) (hz1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₁ b x₀).flatten k ≠ 0) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten)))) q ≠ 0) (o : Fin c) :
                gradAt (fun (b' : Vec c) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b' x₀).flatten))).flatten))))))) label) b o = ∑ ci : Fin c, ∑ hi : Fin (2 * h), ∑ wi : Fin (2 * w), (if ci = o then 1 else 0) * ((if (conv2d W₁ b x₀).flatten (t3Idx ci hi wi) > 0 then 1 else 0) * ∑ co : Fin c, ∑ ho : Fin (2 * h), ∑ wo : Fin (2 * w), convTap W₂ ci hi wi co ho wo * ((if (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten (t3Idx co ho wo) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten)) co ho wo then ∑ l : Fin d₃, W₃ (t3Idx co (winRow ho) (winCol wo)) l * ((if dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten)) l > 0 then 1 else 0) * ∑ q : Fin d₄, W₄ l q * ((if dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten)))) q > 0 then 1 else 0) * ∑ k : Fin nC, W₅ q k * (softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten))))))) k - oneHot nC label k))) else 0))

                Closed form of the conv1 bias loss gradient at any five-margin point — the chain rule through conv1's bias map, contracted with the conv1 head gradient (cnn1_pool_head_input_grad, reused verbatim): the Kronecker bias Jacobian times relu₁'s mask times the point-free conv2 tap Jacobian times the pool-collapsed head.

                theorem Proofs.cnn_conv1_bias_loss_gradAt_reluMask {ic c h w d₃ d₄ nC kH kW : ℕ} (W₁ : Kernel4 c ic kH kW) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (b : Vec c) (hz1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₁ b x₀).flatten k ≠ 0) (hz2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten k ≠ 0) (hmp : MaxPool2Smooth (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten))) (hz3 : ∀ (l : Fin d₃), dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten)) l ≠ 0) (hz4 : ∀ (q : Fin d₄), dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten)))) q ≠ 0) (o : Fin c) :
                gradAt (fun (b' : Vec c) => crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b' x₀).flatten))).flatten))))))) label) b o = ∑ s : Fin (2 * h * (2 * w)), cotWin (fun (ci : Fin c) (hi : Fin (2 * h)) (wi : Fin (2 * w)) => (if (conv2d W₁ b x₀).flatten (t3Idx ci hi wi) > 0 then 1 else 0) * ∑ co : Fin c, ∑ ho : Fin (2 * h), ∑ wo : Fin (2 * w), convTap W₂ ci hi wi co ho wo * ((if (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten (t3Idx co ho wo) > 0 then 1 else 0) * if MaxPool2IsArgmax (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten)) co ho wo then dense (fun (j : Fin d₃) (i' : Fin (c * h * w)) => W₃ i' j) (fun (x : Fin (c * h * w)) => 0) (FloatModel.reluMask (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten))) (dense (fun (j : Fin d₄) (i' : Fin d₃) => W₄ i' j) (fun (x : Fin d₃) => 0) (FloatModel.reluMask (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten))))) (dense (fun (j : Fin nC) (i' : Fin d₄) => W₅ i' j) (fun (x : Fin d₄) => 0) fun (k : Fin nC) => softmax nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b x₀).flatten))).flatten))))))) k - oneHot nC label k)))) (t3Idx co (winRow ho) (winCol wo)) else 0)) o s

                The certified conv-1 BIAS loss gradient, restated as the spatial SUM of the reluMask-form cotangent — the bias peer of cnn_conv1_loss_gradAt_reluMask, one conv-backward deeper. The head collapses via head3_cot_reluMask, the conv-2 backward stays the explicit ∑ convTap·c₂, and the channel-Kronecker conv-1 Jacobian collapses the ∑ ci (Finset.sum_ite_eq' + convBiasGrad_eq_sum) to the spatial SUM the float bias gradient rounds.

                noncomputable def Proofs.cnnConv1BiasLoss {ic c h w d₃ d₄ nC kH kW : ℕ} (W₁ : Kernel4 c ic kH kW) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) :
                Vec c → ℝ

                The loss as a function of the first-conv bias.

                Equations
                • One or more equations did not get rendered due to their size.
                Instances For
                  theorem Proofs.cnn_conv1_bias_sgd_descends {ic c h w d₃ d₄ nC kH kW : ℕ} (W₁ : Kernel4 c ic kH kW) (b₁ : Vec c) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (gh : Vec c) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) {lr η w₂ w₃ w₄ w₅ : ℝ} (hT : ∀ (p q : Fin (2 * h) × Fin (2 * w)), T p q → ConvPatchEq2 kH kW x₀ p q) (hw₂ : 0 ≤ w₂) (hW₂ : ∀ (o cc : Fin c) (kh : Fin kH) (kw : Fin kW), |W₂ o cc kh kw| ≤ w₂) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (hlr : 0 ≤ lr) (hη : 0 ≤ η) (hgh : ∀ (idx : Fin c), |gh idx - gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| ≤ η) (hm1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), lr * (∑ idx : Fin c, |gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| + ↑c * η) < |(conv2d W₁ b₁ x₀).flatten k|) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ↑(c * kH * kW) * (w₂ * (lr * (∑ idx : Fin c, |gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| + ↑c * η))) < |(conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten k|) (hmq : MaxPool2MarginQUpTo (↑(c * kH * kW) * (w₂ * (lr * (∑ idx : Fin c, |gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| + ↑c * η)))) T (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten)))) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (lr * (∑ idx : Fin c, |gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| + ↑c * η))))) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (lr * (∑ idx : Fin c, |gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| + ↑c * η))))))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (lr * (∑ idx : Fin c, |gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| + ↑c * η)))))))))) < 1) (h1 : lr * η * ∑ idx : Fin c, |gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| ≤ (lr * ∑ idx : Fin c, gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx ^ 2) / 4) (h2 : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑(c * kH * kW) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₂ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (lr * (∑ idx : Fin c, |gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx| + ↑c * η))))))))))) * stepRadius (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ lr η ^ 2 ≤ (lr * ∑ idx : Fin c, gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx ^ 2) / 4) :
                  cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label (b₁ - lr • gh) ≤ crossEntropy nC (dense W₅ b₅ (relu d₄ (dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten))))))) label - (lr * ∑ idx : Fin c, gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ idx ^ 2) / 2

                  One inexact SGD step on the CNN's FIRST conv BIAS decreases one example's cross-entropy loss (b₁ moving, every other parameter fixed). Conv1Slot.sgd_descends at the conv1 bias map (ρ = 1): the FIVE margins at the step radius D = lr·(‖∇L‖₁ + c·η) carry no input bound a (the bias Jacobian is a Kronecker indicator), the pool's taken up to two-layer twins (ConvPatchEq2), and the parameter needs no flatten/unflatten plumbing. With this theorem both conv kernels, both conv biases and the three dense layers' weights and biases (via the MLP rungs, SgdDescent.Mlp and SgdDescent.MlpBias) of the Chapter-3 CNN each have a single-layer, single-example descent statement.

                  noncomputable def Proofs.cnnConv1BiasGradBound (c h w d₃ d₄ nC kH kW : ℕ) (w₂ w₃ w₄ w₅ : ℝ) :

                  The explicit ℓ1 bound on the conv1-bias loss gradient, c·((2h)·(2w)·(c·kH·kW·w₂·1))· (d₃·w₃·d₄·w₄·nC·w₅): Conv1Slot.gradAt_abs_le at ρ = 1 per bias entry, times the number of entries.

                  Equations
                  Instances For
                    theorem Proofs.cnn_conv1_bias_exact_sgd_descends {ic c h w d₃ d₄ nC kH kW : ℕ} (W₁ : Kernel4 c ic kH kW) (b₁ : Vec c) (x₀ : Tensor3 ic (2 * h) (2 * w)) (W₂ : Kernel4 c c kH kW) (b₂ : Vec c) (W₃ : Mat (c * h * w) d₃) (b₃ : Vec d₃) (W₄ : Mat d₃ d₄) (b₄ : Vec d₄) (W₅ : Mat d₄ nC) (b₅ : Vec nC) (label : Fin nC) (T : Fin (2 * h) × Fin (2 * w) → Fin (2 * h) × Fin (2 * w) → Prop) {lr w₂ w₃ w₄ w₅ : ℝ} (hT : ∀ (p q : Fin (2 * h) × Fin (2 * w)), T p q → ConvPatchEq2 kH kW x₀ p q) (hw₂ : 0 ≤ w₂) (hW₂ : ∀ (o cc : Fin c) (kh : Fin kH) (kw : Fin kW), |W₂ o cc kh kw| ≤ w₂) (hw₃ : 0 ≤ w₃) (hW₃ : ∀ (i : Fin (c * h * w)) (j : Fin d₃), |W₃ i j| ≤ w₃) (hw₄ : 0 ≤ w₄) (hW₄ : ∀ (i : Fin d₃) (j : Fin d₄), |W₄ i j| ≤ w₄) (hw₅ : 0 ≤ w₅) (hW₅ : ∀ (i : Fin d₄) (j : Fin nC), |W₅ i j| ≤ w₅) (hlr : 0 ≤ lr) (hm1 : ∀ (k : Fin (c * (2 * h) * (2 * w))), lr * cnnConv1BiasGradBound c h w d₃ d₄ nC kH kW w₂ w₃ w₄ w₅ < |(conv2d W₁ b₁ x₀).flatten k|) (hm2 : ∀ (k : Fin (c * (2 * h) * (2 * w))), ↑(c * kH * kW) * (w₂ * (lr * cnnConv1BiasGradBound c h w d₃ d₄ nC kH kW w₂ w₃ w₄ w₅)) < |(conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten k|) (hmq : MaxPool2MarginQUpTo (↑(c * kH * kW) * (w₂ * (lr * cnnConv1BiasGradBound c h w d₃ d₄ nC kH kW w₂ w₃ w₄ w₅))) T (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten)))) (hm3 : ∀ (l : Fin d₃), w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (lr * cnnConv1BiasGradBound c h w d₃ d₄ nC kH kW w₂ w₃ w₄ w₅)))) < |dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten)) l|) (hm4 : ∀ (q : Fin d₄), w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (lr * cnnConv1BiasGradBound c h w d₃ d₄ nC kH kW w₂ w₃ w₄ w₅)))))) < |dense W₄ b₄ (relu d₃ (dense W₃ b₃ (maxPoolFlat c h w (relu (c * (2 * h) * (2 * w)) (conv2d W₂ b₂ (Tensor3.unflatten (relu (c * (2 * h) * (2 * w)) (conv2d W₁ b₁ x₀).flatten))).flatten)))) q|) (hsmall : 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (lr * cnnConv1BiasGradBound c h w d₃ d₄ nC kH kW w₂ w₃ w₄ w₅))))))))) < 1) (hC : 2 * ↑nC * ↑(2 * h * (2 * w)) ^ 2 * ↑(c * kH * kW) ^ 2 * ↑d₃ ^ 2 * ↑d₄ ^ 2 * w₂ ^ 2 * w₃ ^ 2 * w₄ ^ 2 * w₅ ^ 2 / (1 - 2 * (w₅ * (↑d₄ * (w₄ * (↑d₃ * (w₃ * (↑(c * kH * kW) * (w₂ * (↑(2 * h * (2 * w)) * (lr * cnnConv1BiasGradBound c h w d₃ d₄ nC kH kW w₂ w₃ w₄ w₅)))))))))) * lr * ↑c ≤ 1 / 4) :
                    cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label (b₁ - lr • gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁) ≤ cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label b₁ - (lr * ∑ o : Fin c, gradAt (cnnConv1BiasLoss W₁ x₀ W₂ b₂ W₃ b₃ W₄ b₄ W₅ b₅ label) b₁ o ^ 2) / 2

                    One exact-gradient SGD step on the conv1 bias decreases the loss, every hypothesis at an explicit radius. cnn_conv1_bias_sgd_descends at η = 0, the step radius bounded by lr·cnnConv1BiasGradBound and the second dominance condition by C·lr·c ≤ 1/4, as in cnn_conv2_exact_sgd_descends.