Documentation

LeanMlir.Proofs.Foundation.DataParallelSyncBf16

Data parallelism at bf16 — every node shards exactly but the conv weight gradient #

DataParallelSync.lean states P3/P4 at the f32 nodes. The bf16 data-parallel renders ResNet-34 and ResNet-50 train from (resnet34in_momdp64bf16, resnet50in_momdp64bf16, resnet50in160_lambaccdp8x64wxclipbcebf16) swap six of those nodes for bf16 kinds, and each has its own den: operands rounded going in, the result rounded once on the way out. This file is what those six kinds do under sharding.

kindunder shardinglemma
convBf16, convStridedBf16 (forward, .batchOp)exactden_convBf16_shard, den_convStridedBf16_shard
convBackBatchedBf16, convStridedBackBatchedBf16exactden_convBackBatchedBf16_shard, den_convStridedBackBatchedBf16_shard
convWeightGradBBf16, convStridedWeightGradBBf16, all-reducedrounded per replicaden_allReduceMeanF_convWeightGradBBf16_sub_global and its strided peer

The first four round per element of a per-example map, so replica r's value is batchShard r of the same node at batch R·N, exactly as at f32.

⭐⭐ The weight gradients are not. The emitted weight-gradient convolution contracts the batch in one op and stores bf16 once, so the node's den is rnd (Σ_n …) with the rounding outside the batch sum (den_convWeightGradBBf16_eq_rnd). On R replicas each rounds its OWN partial sum S_r before the f32 all-reduce; on one device the global sum Σ_r S_r is rounded once. So the collective is (1/R)·Σ_r rnd S_r where the batch-R·N node is rnd (Σ_r S_r), and den_allReduceMeanF_convWeightGradBBf16_sub_global states the difference exactly. At rnd := id the two agree (…_shard_id), which is the f32 statement.

The divisor step at bf16 #

A DP render divides its loss by the per-replica batch, so its cotangents are R × the shard of the global ones (DataParallelSync.lean, "The 1/R"). At f32 linearity carries that factor through every backward; at bf16 it has to pass through rnd as well. The *_smul lemmas below take that as a hypothesis, ∀ x, rnd (s * x) = s * rnd x, and rndP_two_pow_mul proves it for the repo's rounding model at every power of two — so for bf16 (rndP 7) at R = 4 (rndP_mul_four), the replica count of every ImageNet run.

What is NOT claimed #

⚠ No whole-net statement: there is no single-device bf16 chain for either net to tie a twin to, here or in the f32 tier. These are the per-node cases a bf16 twin would walk its chain with. ⚠ rndP has an unbounded exponent (Binary32Instance.lean): bf16 overflow and subnormals are outside it, where scaling by 4 can move a value across the format's boundary.

theorem Proofs.den_batchOp_shard_node {R N a b : } (op : StableHLO.BatchableOp a b) (t : String) (e : Fin RStableHLO.SHlo (N * a)) (X : Vec (R * N * a)) (he : ∀ (r : Fin R), StableHLO.den (e r) = batchShard R N a X r) (r : Fin R) :

A per-example node on replica r is shard r of the same node at batch R·N. Stated node to node, because at bf16 there is no ℝ-level forward to name on the right.

theorem Proofs.den_convBf16_shard {R N ic oc h w kH kW : } (rnd : ) (wN bN t : String) (W : Kernel4 oc ic kH kW) (b : Vec oc) (e : Fin RStableHLO.SHlo (N * (ic * h * w))) (X : Vec (R * N * (ic * h * w))) (he : ∀ (r : Fin R), StableHLO.den (e r) = batchShard R N (ic * h * w) X r) (r : Fin R) :

The bf16 forward conv shards exactly — every 1×1 and 3×3 in the ResNet bf16 renders.

theorem Proofs.den_convStridedBf16_shard {R N ic oc h w kH kW : } (rnd : ) (wN bN t : String) (W : Kernel4 oc ic kH kW) (b : Vec oc) (e : Fin RStableHLO.SHlo (N * (ic * (2 * h) * (2 * w)))) (X : Vec (R * N * (ic * (2 * h) * (2 * w)))) (he : ∀ (r : Fin R), StableHLO.den (e r) = batchShard R N (ic * (2 * h) * (2 * w)) X r) (r : Fin R) :

…and so does the bf16 symmetric stride-2 conv — the 7×7 stem and the downsamples.

theorem Proofs.den_convBackBatchedBf16_shard {R N ic oc h w kH kW : } (rnd : ) (wN t : String) (W : Kernel4 oc ic kH kW) (b : Vec oc) (dy : Fin RStableHLO.SHlo (N * (oc * h * w))) (DY : Vec (R * N * (oc * h * w))) (hdy : ∀ (r : Fin R), StableHLO.den (dy r) = batchShard R N (oc * h * w) DY r) (r : Fin R) :

The bf16 conv input-VJP shards exactly. Its rounding is per element of a per-example map, so it never sees another replica's rows.

theorem Proofs.den_convStridedBackBatchedBf16_shard {R N ic oc h w kH kW : } (rnd : ) (wN t : String) (W : Kernel4 oc ic kH kW) (b : Vec oc) (dy : Fin RStableHLO.SHlo (N * (oc * h * w))) (DY : Vec (R * N * (oc * h * w))) (hdy : ∀ (r : Fin R), StableHLO.den (dy r) = batchShard R N (oc * h * w) DY r) (r : Fin R) :

…and so does the bf16 strided conv input-VJP.

theorem Proofs.den_convWeightGradBBf16_eq_rnd {N ic oc h w kH kW : } (rnd : ) (xN cotN : String) (b : Vec oc) (x : Vec (N * (ic * h * w))) (W : Kernel4 oc ic kH kW) (e : StableHLO.SHlo (N * (oc * h * w))) (idx : Fin (oc * ic * kH * kW)) :
StableHLO.den (StableHLO.SHlo.convWeightGradBBf16 rnd xN b x W e) idx = rnd (StableHLO.den (StableHLO.SHlo.convWeightGradB xN b (fun (i : Fin (N * (ic * h * w))) => rnd (x i)) W (StableHLO.SHlo.operand cotN fun (i : Fin (N * (oc * h * w))) => rnd (StableHLO.den e i))) idx)

The bf16 weight-gradient node is the f32 node at rounded operands, rounded once.

theorem Proofs.den_convStridedWeightGradBBf16_eq_rnd {N ic oc h w kH kW : } (rnd : ) (xN cotN : String) (b : Vec oc) (x : Vec (N * (ic * (2 * h) * (2 * w)))) (W : Kernel4 oc ic kH kW) (e : StableHLO.SHlo (N * (oc * h * w))) (idx : Fin (oc * ic * kH * kW)) :
StableHLO.den (StableHLO.SHlo.convStridedWeightGradBBf16 rnd xN b x W e) idx = rnd (StableHLO.den (StableHLO.SHlo.convStridedWeightGradB xN b (fun (i : Fin (N * (ic * (2 * h) * (2 * w)))) => rnd (x i)) W (StableHLO.SHlo.operand cotN fun (i : Fin (N * (oc * h * w))) => rnd (StableHLO.den e i))) idx)

…and the strided one.

noncomputable def Proofs.convWGradShardSum {R N ic oc h w kH kW : } (rnd : ) (xN cotN : String) (b : Vec oc) (X : Vec (R * N * (ic * h * w))) (W : Kernel4 oc ic kH kW) (DY : Vec (R * N * (oc * h * w))) (r : Fin R) (idx : Fin (oc * ic * kH * kW)) :

Replica r's partial sum: the f32 conv weight-gradient node on shard r of the rounded global operands. The collective and the batch-R·N node are both built from these; they differ only in where rnd is applied.

Equations
  • One or more equations did not get rendered due to their size.
Instances For
    noncomputable def Proofs.convStridedWGradShardSum {R N ic oc h w kH kW : } (rnd : ) (xN cotN : String) (b : Vec oc) (X : Vec (R * N * (ic * (2 * h) * (2 * w)))) (W : Kernel4 oc ic kH kW) (DY : Vec (R * N * (oc * h * w))) (r : Fin R) (idx : Fin (oc * ic * kH * kW)) :

    The strided peer of convWGradShardSum.

    Equations
    • One or more equations did not get rendered due to their size.
    Instances For
      theorem Proofs.den_allReduceMeanF_convWeightGradBBf16_shard {N ic oc h w kH kW : } (R : ) (hR : 0 < R) (rnd : ) (t xN cotN : String) (ds : List ) (b : Vec oc) (W : Kernel4 oc ic kH kW) (X : Vec (R * N * (ic * h * w))) (DY : Vec (R * N * (oc * h * w))) (dy : Fin RStableHLO.SHlo (N * (oc * h * w))) (hdy : ∀ (r : Fin R), StableHLO.den (dy r) = batchShard R N (oc * h * w) DY r) (idx : Fin (oc * ic * kH * kW)) :
      StableHLO.den (StableHLO.SHlo.allReduceMeanF R hR t ds fun (r : Fin R) => StableHLO.SHlo.convWeightGradBBf16 rnd xN b (batchShard R N (ic * h * w) X r) W (dy r)) idx = 1 / R * r : Fin R, rnd (convWGradShardSum rnd xN cotN b X W DY r idx)

      The all-reduced bf16 weight gradient is the mean of the replicas' ROUNDED partial sums. Each replica on its shard, at the shard-r block of the global cotangent.

      theorem Proofs.den_convWeightGradBBf16_global_split {N ic oc h w kH kW : } (R : ) (rnd : ) (xN cotN : String) (b : Vec oc) (W : Kernel4 oc ic kH kW) (X : Vec (R * N * (ic * h * w))) (DY : Vec (R * N * (oc * h * w))) (idx : Fin (oc * ic * kH * kW)) :
      StableHLO.den (StableHLO.SHlo.convWeightGradBBf16 rnd xN b X W (StableHLO.SHlo.operand cotN DY)) idx = rnd (∑ r : Fin R, convWGradShardSum rnd xN cotN b X W DY r idx)

      The batch-R·N bf16 weight gradient rounds the SUM of the same partial sums, once.

      theorem Proofs.den_allReduceMeanF_convWeightGradBBf16_sub_global {N ic oc h w kH kW : } (R : ) (hR : 0 < R) (rnd : ) (t xN cotN : String) (ds : List ) (b : Vec oc) (W : Kernel4 oc ic kH kW) (X : Vec (R * N * (ic * h * w))) (DY : Vec (R * N * (oc * h * w))) (dy : Fin RStableHLO.SHlo (N * (oc * h * w))) (hdy : ∀ (r : Fin R), StableHLO.den (dy r) = batchShard R N (oc * h * w) DY r) (idx : Fin (oc * ic * kH * kW)) :
      StableHLO.den (StableHLO.SHlo.allReduceMeanF R hR t ds fun (r : Fin R) => StableHLO.SHlo.convWeightGradBBf16 rnd xN b (batchShard R N (ic * h * w) X r) W (dy r)) idx - 1 / R * StableHLO.den (StableHLO.SHlo.convWeightGradBBf16 rnd xN b X W (StableHLO.SHlo.operand cotN DY)) idx = 1 / R * (r : Fin R, rnd (convWGradShardSum rnd xN cotN b X W DY r idx) - rnd (∑ r : Fin R, convWGradShardSum rnd xN cotN b X W DY r idx))

      ⭐⭐ The one difference, exactly. The all-reduced bf16 weight gradient minus 1/R of the batch-R·N bf16 node is 1/R of (sum of the rounded partial sums − the rounded sum).

      theorem Proofs.den_allReduceMeanF_convWeightGradBBf16_shard_id {N ic oc h w kH kW : } (R : ) (hR : 0 < R) (t xN cotN : String) (ds : List ) (b : Vec oc) (W : Kernel4 oc ic kH kW) (X : Vec (R * N * (ic * h * w))) (DY : Vec (R * N * (oc * h * w))) (dy : Fin RStableHLO.SHlo (N * (oc * h * w))) (hdy : ∀ (r : Fin R), StableHLO.den (dy r) = batchShard R N (oc * h * w) DY r) (idx : Fin (oc * ic * kH * kW)) :
      StableHLO.den (StableHLO.SHlo.allReduceMeanF R hR t ds fun (r : Fin R) => StableHLO.SHlo.convWeightGradBBf16 (fun (x : ) => x) xN b (batchShard R N (ic * h * w) X r) W (dy r)) idx = 1 / R * StableHLO.den (StableHLO.SHlo.convWeightGradBBf16 (fun (x : ) => x) xN b X W (StableHLO.SHlo.operand cotN DY)) idx

      At rnd := id the difference vanishes — the collective is 1/R of the batch-R·N node, which is the f32 statement den_allReduceMeanF_convWeightGradB_shard.

      theorem Proofs.den_allReduceMeanF_convStridedWeightGradBBf16_shard {N ic oc h w kH kW : } (R : ) (hR : 0 < R) (rnd : ) (t xN cotN : String) (ds : List ) (b : Vec oc) (W : Kernel4 oc ic kH kW) (X : Vec (R * N * (ic * (2 * h) * (2 * w)))) (DY : Vec (R * N * (oc * h * w))) (dy : Fin RStableHLO.SHlo (N * (oc * h * w))) (hdy : ∀ (r : Fin R), StableHLO.den (dy r) = batchShard R N (oc * h * w) DY r) (idx : Fin (oc * ic * kH * kW)) :
      StableHLO.den (StableHLO.SHlo.allReduceMeanF R hR t ds fun (r : Fin R) => StableHLO.SHlo.convStridedWeightGradBBf16 rnd xN b (batchShard R N (ic * (2 * h) * (2 * w)) X r) W (dy r)) idx = 1 / R * r : Fin R, rnd (convStridedWGradShardSum rnd xN cotN b X W DY r idx)

      Strided: the all-reduced bf16 weight gradient is the mean of the rounded partial sums.

      theorem Proofs.den_convStridedWeightGradBBf16_global_split {N ic oc h w kH kW : } (R : ) (rnd : ) (xN cotN : String) (b : Vec oc) (W : Kernel4 oc ic kH kW) (X : Vec (R * N * (ic * (2 * h) * (2 * w)))) (DY : Vec (R * N * (oc * h * w))) (idx : Fin (oc * ic * kH * kW)) :
      StableHLO.den (StableHLO.SHlo.convStridedWeightGradBBf16 rnd xN b X W (StableHLO.SHlo.operand cotN DY)) idx = rnd (∑ r : Fin R, convStridedWGradShardSum rnd xN cotN b X W DY r idx)

      Strided: the batch-R·N node rounds the sum of the same partial sums, once.

      theorem Proofs.den_allReduceMeanF_convStridedWeightGradBBf16_sub_global {N ic oc h w kH kW : } (R : ) (hR : 0 < R) (rnd : ) (t xN cotN : String) (ds : List ) (b : Vec oc) (W : Kernel4 oc ic kH kW) (X : Vec (R * N * (ic * (2 * h) * (2 * w)))) (DY : Vec (R * N * (oc * h * w))) (dy : Fin RStableHLO.SHlo (N * (oc * h * w))) (hdy : ∀ (r : Fin R), StableHLO.den (dy r) = batchShard R N (oc * h * w) DY r) (idx : Fin (oc * ic * kH * kW)) :
      StableHLO.den (StableHLO.SHlo.allReduceMeanF R hR t ds fun (r : Fin R) => StableHLO.SHlo.convStridedWeightGradBBf16 rnd xN b (batchShard R N (ic * (2 * h) * (2 * w)) X r) W (dy r)) idx - 1 / R * StableHLO.den (StableHLO.SHlo.convStridedWeightGradBBf16 rnd xN b X W (StableHLO.SHlo.operand cotN DY)) idx = 1 / R * (r : Fin R, rnd (convStridedWGradShardSum rnd xN cotN b X W DY r idx) - rnd (∑ r : Fin R, convStridedWGradShardSum rnd xN cotN b X W DY r idx))

      ⭐⭐ Strided: the one difference, exactly.

      theorem Proofs.int_log_two_pow_mul (k : ) {y : } (hy : 0 < y) :
      Int.log 2 (2 ^ k * y) = k + Int.log 2 y

      Int.log 2 shifts by k under scaling by 2^k.

      theorem Proofs.int_log_abs_two_pow_mul (k : ) {x : } (hx : x 0) :
      Int.log 2 |2 ^ k * x| = k + Int.log 2 |x|

      Int.log reads |x|, so the shift holds for either sign.

      theorem Proofs.rndP_two_pow_mul (p k : ) (x : ) :
      rndP p (2 ^ k * x) = 2 ^ k * rndP p x

      The repo's rounding model commutes with scaling by a power of two — the grid at 2^k·x is the grid at x scaled by 2^k, because the exponent is unbounded.

      theorem Proofs.rndP_mul_four (p : ) (x : ) :
      rndP p (4 * x) = 4 * rndP p x

      At R = 4, the replica count of every ImageNet run.

      theorem Proofs.convBackBatchedBf16_smul {N ic oc h w kH kW : } (rnd : ) (s : ) (hrnd : ∀ (x : ), rnd (s * x) = s * rnd x) (wN t : String) (W : Kernel4 oc ic kH kW) (b : Vec oc) (DY : Vec (N * (oc * h * w))) :
      StableHLO.den (StableHLO.SHlo.convBackBatchedBf16 rnd wN W b (StableHLO.SHlo.operand t fun (i : Fin (N * (oc * h * w))) => s * DY i)) = fun (i : Fin (N * (ic * h * w))) => s * StableHLO.den (StableHLO.SHlo.convBackBatchedBf16 rnd wN W b (StableHLO.SHlo.operand t DY)) i

      The bf16 conv input-VJP scales with its cotangent when rnd commutes with the scale.

      theorem Proofs.convStridedBackBatchedBf16_smul {N ic oc h w kH kW : } (rnd : ) (s : ) (hrnd : ∀ (x : ), rnd (s * x) = s * rnd x) (wN t : String) (W : Kernel4 oc ic kH kW) (b : Vec oc) (DY : Vec (N * (oc * h * w))) :
      StableHLO.den (StableHLO.SHlo.convStridedBackBatchedBf16 rnd wN W b (StableHLO.SHlo.operand t fun (i : Fin (N * (oc * h * w))) => s * DY i)) = fun (i : Fin (N * (ic * (2 * h) * (2 * w)))) => s * StableHLO.den (StableHLO.SHlo.convStridedBackBatchedBf16 rnd wN W b (StableHLO.SHlo.operand t DY)) i

      …and the strided one.

      theorem Proofs.convWeightGradBBf16_smul {N ic oc h w kH kW : } (rnd : ) (s : ) (hrnd : ∀ (x : ), rnd (s * x) = s * rnd x) (xN t : String) (b : Vec oc) (x : Vec (N * (ic * h * w))) (W : Kernel4 oc ic kH kW) (DY : Vec (N * (oc * h * w))) (idx : Fin (oc * ic * kH * kW)) :

      The bf16 conv weight gradient scales with its cotangent when rnd commutes with it.

      theorem Proofs.convStridedWeightGradBBf16_smul {N ic oc h w kH kW : } (rnd : ) (s : ) (hrnd : ∀ (x : ), rnd (s * x) = s * rnd x) (xN t : String) (b : Vec oc) (x : Vec (N * (ic * (2 * h) * (2 * w)))) (W : Kernel4 oc ic kH kW) (DY : Vec (N * (oc * h * w))) (idx : Fin (oc * ic * kH * kW)) :

      …and the strided one.