TorchLean API

NN.Examples.BugZoo.LayerNormDegenerateAxis

BugZoo: LayerNorm on a one-feature axis #

LayerNorm has a sharp degenerate case: if the normalized axis has length one, then the mean is the single input value and the variance is zero. The normalized value is therefore exactly zero, so the affine output is the bias and the forward result is independent of both the input and the scale.

PyTorch analogy:

torch.nn.functional.layer_norm(x, normalized_shape=(1,), weight=w, bias=b)

For every finite scalar x, the mathematical contract is:

$$ \begin{aligned} \operatorname{mean}[x] &= x,\\ \operatorname{var}[x] &= 0,\\ \left(\frac{x-\operatorname{mean}}{\sqrt{\operatorname{var}+\varepsilon}}\right) \operatorname{weight}+\operatorname{bias} &= \operatorname{bias}. \end{aligned} $$

So reverse mode must report zero gradient for weight and zero input gradient. This file keeps the contract small: the real-valued theorems record the algebra, and the concrete definitions below are the public TorchLean spec terms used by the Python reproducer notes.

The algebraic statements use real arithmetic with totalized division and square root. They do not assert finite native results for every epsilon or verify an external backward implementation.

Run python3 scripts/verification/normalization_contract_probe.py --device cpu to sweep input magnitudes and compare PyTorch forward and backward residuals in float32 and float64. --device cuda probes the GPU implementation when available; observed numbers depend on the PyTorch build and hardware.

@[reducible, inline]

A one-by-one matrix: a single batch element with a single feature.

Instances For
    @[reducible, inline]

    A length-one vector, the shape LayerNorm's γ and β take here.

    Instances For

      Build a one-by-one matrix from a scalar.

      Instances For

        Build a length-one vector from a scalar.

        Instances For
          theorem NN.Examples.BugZoo.LayerNormDegenerateAxis.one_feature_layernorm_scalar_contract (x gamma beta epsilon : ) :
          (x - x) / MathFunctions.sqrt (max (0 + epsilon) 0) * gamma + beta = beta

          The scalar algebra behind one-feature LayerNorm: normalization contributes zero, so the affine result is the bias.

          The scale/weight gradient is zero because the normalized one-feature value is zero.

          theorem NN.Examples.BugZoo.LayerNormDegenerateAxis.one_feature_layernorm_input_grad_contract (dy gamma invStd : ) :
          invStd * (dy * gamma - dy * gamma - 0) = 0

          The input gradient is zero because the one-feature LayerNorm forward is constant in the input.

          TorchLean spec value for the public PyTorch repro: forward output.

          Instances For

            TorchLean spec value for the public PyTorch repro: gradient with respect to weight.

            Instances For

              TorchLean spec value for the public PyTorch repro: gradient with respect to input.

              Instances For