Documentation

LeanMlir.Proofs.Codegen.ViTRender

ViT per-example render: forward, backward traversal, SGD step, AdamW tail #

The ViT peer of ConvNeXtRender. It holds the per-example ViT-Tiny forward chain rendered as pretty of the multi-head vector-LN graph (patch embed, vDEPTH = 12 vitBlockGraphMHV blocks, final vector-LN, CLS-slice dense head; vitFwdRenderV), whose graph denotes vitForwardKV at depth 12 (vitFwdGraphKMHV_faithful, stated for every depth); the shared backward traversal vitBackAll; the SGD-inline step vitTrainStepRenderV; the AdamW tail vitAdamTrainStepText, which ViTRenderB reuses; and the parameter list vitParamSig. It writes one artifact, verified_mlir/vit_train_step.mlir, the step ViTStepTie is stated at; every other ViT artifact is written by ViTRenderB.

Render is value-independent (skel erases the ℝ/Mat/Vec fields), so placeholders (0, zero mats/vecs) are passed; the emitted epsStr/sStr literals carry the real ε / SDPA-scale.

ViT-Tiny: ic=3, 224², patch 16×16/s16 (N=196 patches, 197 tokens), D=192 = 3 heads × 64, MLP 768, 12 blocks, nClasses classes (10 as committed), BS=32, ε=1e-5, SDPA scale = 1/√64 = 0.125.

References #

Equations
Instances For

    The width knobs of a ViT. A record so one renderer serves Ti/S/B instead of one size per file.

    d and tok are DERIVED, not stored. d = heads * hd definitionally, so the places the code writes vbD and the places it writes vbH * vbHd (the head-slice operand types) are the same type with no rewriting and no Nat lemma. Stored as a field with a heads * hd = d proof they would only be PROPOSITIONALLY equal and every one of those sites would need a cast.

    • tk : ℕ

      patch tokens; the token axis is tk + 1 for CLS.

    • heads : ℕ
    • hd : ℕ

      per-head dim. ViT keeps this at 64 across Ti/S/B and widens by adding heads.

    • m : ℕ

      MLP hidden width.

    • heads_pos : 0 < self.heads

      Needed for the Fin heads in the attention loop.

    Instances For

      Model dim. Derived so it is DEFEQ to heads * hd — see the note on VitDims.

      Equations
      Instances For

        Tokens including CLS.

        Equations
        Instances For

          ViT-Tiny: D = 192 = 3 × 64, MLP 768. The default everywhere.

          Equations
          Instances For

            ViT-Small: D = 384 = 6 × 64, MLP 1536. Same depth and same patch grid as Tiny — S widens only, which is why it needs no new proof and no new block chain.

            Equations
            Instances For

              ViT-Base (DeiT-B): D = 768 = 12 × 64, MLP 3072. Still depth 12 and still 16×16 patches.

              Equations
              Instances For

                Total encoder blocks = the reference's totalDrop, i.e. the ramp DENOMINATOR is this minus 1.

                Equations
                Instances For

                  Two sites per block — the attention branch and the MLP branch, dropping independently.

                  Equations
                  Instances For

                    The mask-input ordinal of block i's branch br (br = 0 attention, 1 MLP). The single source for the numbering: the forward walks blocks upward, the backward downward, and both call this. Distinct from the RAMP index, which is i for BOTH branches.

                    Equations
                    Instances For

                      The ramp index of a site ordinal — the inverse direction, used by the driver's keep table.

                      Equations
                      Instances For

                        The %dp<i>: tensor<Bxf32> inputs an SD ViT render appends to its signature — one per SITE, in ordinal order, which is the order the driver's dropScales writes them into the blob.

                        Equations
                        Instances For

                          The forward SSA names a block's backward + param-SGD reference (the ConvNeXt-FNames analogue). Per-head arrays hold the 3 heads' slices + pre-softmax + softmax-output.

                          Instances For

                            Forward saves the whole-net backward references: the per-block saves, the final-LN input (last block output), the CLS-slice output and the logits SSA.

                            Instances For

                              Per-block func-arg signature (committed forward order).

                              Equations
                              • One or more equations did not get rendered due to their size.
                              Instances For
                                def Proofs.StableHLO.vitFwdRenderV (gf : GeluForm) (funcName : String := "vit_fwd") (bs : ℕ := 32) (nClasses : ℕ := 10) :

                                ViT-Tiny depth-12 forward rendered ENTIRELY from the verified AST. Every line is pretty of a verified SHlo node; den(graph) = vitForwardKV by vitFwdGraphKMHV_faithful (at depth 12). The output is the [BS,10] logits.

                                Equations
                                • One or more equations did not get rendered due to their size.
                                Instances For
                                  def Proofs.StableHLO.vitBackAll (gf : GeluForm) (bs nClasses : ℕ) (lrStr : String) (adam : Bool) (smooth : Option (String × String × String) := none) :

                                  The whole-net backward traversal, SHARED by the SGD and AdamW renders. Returns the emitted code and, in func-arg order, one SSA per parameter — the updated param at adam := false, the un-fused gradient at adam := true. One traversal, two tails: a second copy of the depth-12 backward would be the double-writer disease one level down.

                                  Equations
                                  • One or more equations did not get rendered due to their size.
                                  Instances For

                                    The 200 parameter (name, shape) pairs in func-arg order — the single source for the argument signature, the return types, and (in the AdamW render) the %<nm>m/%<nm>v moment slots.

                                    nClasses is a parameter here, which the matched pair with jax/MainVitImagenet.lean (a 1000-class ViT-Tiny) needs.

                                    V is TRAILING and defaulted to ViT-Tiny, for the same reason vbB is: every existing call site is untouched and every committed artifact re-renders byte-identically.

                                    Equations
                                    • One or more equations did not get rendered due to their size.
                                    Instances For

                                      Does this parameter get weight decay? The renderer's half of the timm rule.

                                      It keys the positional embedding by NAME where the reference keys it by SHAPE (p.shape == _WD_POS_SHAPE), and that is deliberate rather than a transcription slip: the reference walks an unnamed pytree and has nothing else to key on, while a shape test here would also exclude any other param that happened to be 197×192. The name is the more precise identifier when you have one. #guards below pin the resulting counts against the reference's own, which is what stops the two readings drifting.

                                      The rule reads the RANK, and rank is the one thing that survives the layout difference between the two sides — the render carries Wfc1 as [192,768] where the reference has (768,192). Both are 2-D, so both decay.

                                      Equations
                                      Instances For
                                        def Proofs.StableHLO.vitWdCounts (nClasses : ℕ := 10) :

                                        The decayed / excluded split, as the renderer computes it.

                                        Equations
                                        • One or more equations did not get rendered due to their size.
                                        Instances For
                                          def Proofs.StableHLO.vitTrainStepRenderV (gf : GeluForm) (funcName : String := "vit_train_step") (lrStr : String := "0.003125") (nClasses : ℕ := 10) (bs : ℕ := 32) :

                                          ViT-Tiny depth-12 SGD train step rendered ENTIRELY from the verified AST. Forward (vitFwd12) → softmax-CE cotangent (softmax(logits) − onehot, the lossCotGraph form) → head-dense back (dotOut + weightSgd/biasSgd) → clsPadF → final-LN back (vlnBack) → 12× vBlockBack (reversed, cotangent threaded) → patch-embed back (patchEmbedWeightSgd/patchEmbedBiasSgd

                                          • clsSliceF→denseBiasSgdB for cls + posEmbedSgd for pos). Returns the 200 SGD-updated params in func-arg order. lrStr is the mean-loss-equiv literal (base/BS); cotangent has NO /B (folded into lr). The traversal itself is vitBackAll false, shared with the AdamW render.
                                          Equations
                                          • One or more equations did not get rendered due to their size.
                                          Instances For
                                            def Proofs.StableHLO.vitAdamVariant (bs : ℕ := 32) (replicas : ℕ := 1) (ema wdExclude clip sd : Bool := false) (acc : ℕ := 1) :

                                            The driver's variant slug for a (per-device batch, replica count, EMA) triple: the artifact is verified_mlir/vit_<variant>_train_step.mlir, the entry point is @vit_<variant>_train_step and LEAN_MLIR_VARIANT=<variant> selects it at run time.

                                            This is the ViT peer of cnxAdamVariant / r34AdamVariant / mnv2AdamVariant, and unlike theirs it is documentation plus a drift guard rather than the name's producer — vitAdamTrainStepText takes funcName explicitly and the #eval paths must stay string literals for regen_verified_mlir.sh's writer audit to see them. So the #guards at the bottom of this file are what tie the literals to this function; the contract is checked at lake build rather than merely described.

                                            ViT's spelling breaks the "the number is the per-device batch" convention at 4 replicas (adamdp32x4, adamdp128x4) and that is deliberate — vit_adamdp_train_step.mlir is a COMMITTED 2-replica artifact at bs32, so a 4-replica render reusing adamdp would give one path two writers computing different graphs. Encoded here so the exception cannot be forgotten.

                                            The ema marker LEADS. trainAdamSched keys its 4-region [θ|m|v|ema] blob off variant.startsWith "ema", so a trailing marker would silently select the 3-region layout for a 4-region graph — every parameter misaligned. On EfficientNet the RMSProp+EMA variant is emarms, which does not start with "rms", so a prefix test would initialise the mean-square to 0. ViT is AdamW-only, so there is no second axis here; if one is ever added, make both predicates substring tests first.

                                            Equations
                                            • One or more equations did not get rendered due to their size.
                                            Instances For
                                              def Proofs.StableHLO.vitAdamTrainStepText (gf : GeluForm) (funcName : String := "vit_adam_train_step") (bStr : String := "32.0") (replicas : ℕ := 1) (bs : ℕ := 32) (nClasses : ℕ := 10) (alpha : Float := 0.1) (ema wdExclude : Bool := false) (wdStr : String := "0.0001") (clip : Bool := false) (clipStr : String := "1.0") (traversal : Option (StateM EmitS (String × List String × String)) := none) (V : VitDims := vitTiDims) (sd : Bool := false) (acc : ℕ := 1) :

                                              ViT-Tiny depth-12 AdamW train step, rendered from the verified AST. The certified peer of the hand-written ViTRender.vitTrainStepModuleAdamSched.

                                              Same backward as vit_train_step (vitBackAll, one traversal) but taking the un-fused gradients, each fed to the proven AdamW triple. The cotangent is the LABEL-SMOOTHED one with an explicit ÷B, matching the AdamW recipe — the SGD render folds the mean into lr and does no smoothing, so the two are different functions and this parameter is not optional.

                                              Interface: 605 in (%x, 200 θ, 200 m, 200 v, %lr/%bc1/%bc2, %onehot) / 603 out (200 θ', 200 m', 200 v', %loss/%bc1/%bc2), the packed [θ|m|v] layout the AdamW trainer reads. Hand-written text besides the signature and constants: the report-only %loss and the %bc1/%bc2 passthroughs.

                                              At ema := true the blob gains a fourth region and the scalar tail goes 3 → 5, so the interface becomes 807 in / 805 out = 605/603 + 200 (the shadow) + 2 (%emad/%oemad). ema is LAST in this signature on purpose: inserted mid-list it would capture an existing positional argument at every call site.

                                              Equations
                                              • One or more equations did not get rendered due to their size.
                                              Instances For