2026/07/21 62.7k words

Transformer System #numinens#transformer#pruning#structural-compression#sparsity#structured-pruning#unstructured-pruning#sparse-kernels#model-compression#deployment

M6.11 - Pruning, Structural Compression, and Executable Sparsity

M6.11 - Pruning, Structural Compression, and Executable Sparsity

0x00 Removing Parameters Is Not Yet Acceleration

A large Transformer has already been trained. Its quality is useful, but its deployment graph is too expensive. We may want fewer stored bytes, a lower memory-bandwidth requirement, smaller matrix multiplications, fewer blocks, or a model that fits on fewer devices. This creates a question that sounds simple:

Which parts of the trained model can be removed without destroying the behavior that made the model useful?

The word pruning covers the family of methods that answer this question by removing parameter values or structural components. The idea predates Transformers: an overparameterized network may contain weights, neurons, or modules whose removal changes the useful function less than their resource cost would suggest. Transformers make the question more structured. Their weights are organized into attention heads, Q/K/V/O projections, MLP channels, residual-width channels, repeated blocks, embedding tables, and normalization state. A pruning decision can therefore range from one scalar entry to an entire layer.

The introductory distinction in Section 6.4.7 of the local book remains useful:

unstructured pruning
    -> remove individual connections or scalar weights

structured pruning
    -> remove complete groups such as heads, channels, or layers

But this distinction is not sufficient for implementation. A matrix containing millions of exact zeros can still occupy its original dense tensor, load every zero from memory, and execute the same dense GEMM. Conversely, deleting an MLP channel requires changing several coupled matrices, not placing one isolated zero in one tensor. A shorter layer list changes the ordered function graph and checkpoint configuration. Hardware-constrained 2:4 sparsity stores values plus compact pattern metadata and requires an eligible sparse operator.

The pruning problem is therefore not only what should become zero? It is:

what unit is removable?
what evidence says it is removable?
what budget and schedule create the mask or structure?
what artifact exists after transformation and recovery?
what runtime can execute that artifact more cheaply?

These questions form the spine of M6.11.

0x00A Pruning Changes A Different Object From Quantization Or Distillation

Pruning appears next to quantization and distillation in model-compression surveys, but their primary changed objects are different:

MethodChanged objectTypical training or calibration stateRuntime consequence
quantizationnumerical alphabet and scale/metadata used to represent tensorscalibration and/or fake-quantized optimizationlower-bit storage and compatible low-bit kernels
distillationstudent training objective and teacher-derived supervisionteacher logits, features, sequences, or cached targetsordinary student runtime if teacher state is excluded
unstructured pruningselected scalar weight entriesscore, mask, optional reconstruction/retrainingno automatic saving unless sparse storage and kernels are used
N:M pruninga locally constrained nonzero patternlocal group selection and optional recoveryhardware/library-specific semi-structured sparse path
structured width pruningheads, MLP channels, residual channels, or coupled groupsimportance/search, tensor surgery, recoveryphysically smaller dense projections when dependencies close
depth pruningcomplete blocks/layerslayer selection or training-time preparation, then recoveryfewer block invocations and a shorter graph

The methods can be composed. A pruned model can be quantized. A teacher can supervise recovery after structural removal. A quantization-aware student can also carry an N:M mask. Composition does not make the contracts interchangeable. The mask does not specify the quantizer; the teacher loss does not specify which structure to remove; the number of zeros does not specify the runtime operator.

This article therefore reuses M6.10’s distillation contract only when recovery needs teacher supervision. It hands numerical formats back to M8. It does not treat pruning as a synonym for every method that happens to produce a smaller file.

0x00B Static Model Pruning Is Not Input-Dependent Token Pruning

There is another overloaded use of pruning: removing token rows during one forward pass. Progressive token pruning can shorten the sequence seen by later blocks. Dynamic depth systems can skip a block for one token or one request. Those methods change the executed path as a function of input state.

M6.11 owns a different object: a transformed model artifact whose stored weights or static graph have been removed before a request arrives.

static weight/head/channel/layer pruning:
    one released model has a changed parameter or graph structure

input-dependent token/depth pruning:
    the same released model selects different rows or blocks per input

M6.8 owns conditional token/depth execution. The distinction matters for state and correctness. Static head removal changes Q/K/V/O tensor shapes and checkpoint metadata. Dynamic head routing retains the available heads and adds a routing decision. Static layer deletion creates a shorter ordered graph. Early exit retains the full graph and decides how far one example travels.

0x00C Five Removal Units Produce Five Different Artifacts

Consider a Transformer with blocks, residual width , attention heads, head width , and gated-MLP width . Five common removal units are:

  1. Scalar weight: one entry of one weight matrix is zeroed or omitted.
  2. Local N:M group: at most entries survive in every consecutive group of entries along a specified matrix axis.
  3. Head or MLP channel: coupled slices are removed from several projections.
  4. Residual-width channel: one hidden coordinate is removed across much of the model’s connected residual graph.
  5. Layer/block: one complete residual transformation is removed from the ordered composition.
Five pruning removal units shown from scalar weights and local two-of-four groups through attention or MLP channels, residual-width channels, and complete Transformer layers

The ordering is not a quality ranking. Finer units give a larger combinatorial search space and can retain more freedom at a fixed nonzero count, but they also tend to need more metadata and specialized execution. Coarser units can remove useful parameters together, but often produce ordinary smaller dense operators that current runtimes already execute well.

Even within one label, the dependency differs. Removing one attention head from multi-head attention means removing matching head slices from Q, K, V, and the input dimension of the output projection. In grouped-query attention, query heads and KV heads do not have a one-to-one ownership relation. Removing one gated-MLP intermediate channel means removing the same row from both gate and up projections and the corresponding column from the down projection. Removing one residual-width channel can propagate into embeddings, normalization, attention, MLP, and output-head dimensions.

The removable unit must therefore include its dependency closure: every tensor, shape, configuration field, shard, and runtime assumption that must change with the decision.

0x00D A Pruning Method Has Five Contracts

A useful method signature is:

P = (
  removable unit and dependency closure,
  score or optimization evidence,
  budget and schedule,
  transformed artifact and recovery,
  executable runtime contract
)

Contract One: Unit And Dependency Closure

This contract names whether the decision applies to scalar entries, local groups, heads, channels, residual dimensions, layers, or a coupled group. It also names the tensor axes. “Prune 25% of attention” is incomplete: Q heads, KV heads, head width, output slices, and per-layer uniformity create different artifacts.

Contract Two: Selection Evidence

A pruning score is an approximation to a counterfactual: how much would an objective or representation change if this unit were removed? Magnitude, activations, gradients, curvature, stochastic gates, local reconstruction, ablation, and candidate search answer different approximations to that question. Later sections will derive them separately. Loop 1 does not declare one universal importance measure.

Contract Three: Budget And Schedule

The method must state:

global target or per-layer quota
one-shot or gradual schedule
fixed mask or possible regrowth
arbitrary sparsity or exact local N:M pattern
target nonzero count, parameter count, width tuple, or depth
calibration-only or training-time optimization

A global top- scalar mask does not satisfy 2:4 just because its overall density is 50%. A learned sparsity penalty does not guarantee an exact target shape unless the constrained optimization and final projection enforce it.

Contract Four: Artifact And Recovery

The immediate mask may be only an intermediate training object. Export could produce:

original dense tensors containing zeros
values plus an explicit binary mask
CSR/CSC/BSR values and coordinates
N:M packed values plus pattern metadata
physically sliced smaller dense matrices
a shorter ordered list of blocks

Recovery can be absent, local to one layer’s reconstruction, or global through continued pretraining, supervised tuning, LoRA, or distillation. These paths change compute, data, optimizer state, and what counts as the final student.

Contract Five: Executable Runtime

The deployment claim must name the loader and operator that consume the artifact. It must distinguish eligible for a sparse tactic from the runtime selected a sparse tactic. Current TensorRT documentation, for example, explicitly reports both layers whose weights satisfy its structured sparsity requirement and layers for which the builder actually chooses sparse tactics; eligibility does not force a speed win.

The contract closes only after measuring the target workload. Prefill and decode use different matrix shapes and batch regimes. Metadata can reduce an expected memory saving. Kernel launch, packing, alignment, and fallback can consume the theoretical arithmetic reduction.

0x00E Four Ledgers Must Not Be Collapsed Into Sparsity

For a weight matrix , let and density . Four ledgers remain distinct:

logical nonzeros:
    rho * n values participate in the intended sparse function

serialized artifact:
    kept values + masks/indices/pointers + alignment/padding/config

executed arithmetic:
    work issued by the selected dense or sparse kernel

end-to-end runtime:
    load + memory transfer + operator + surrounding layers + scheduling

An ordinary dense checkpoint with 50% zeros has logical density but still stores dense values. A sparse format may store only values, yet its indices can eliminate much of the byte saving at moderate density. A smaller dense matrix can reduce both stored values and dense GEMM dimensions without a sparse kernel. Deleting layers can reduce the number of block calls while leaving every surviving matrix’s width unchanged.

This article will use sparsity for the logical zero/nonzero pattern and name the physical artifact separately.

0x00F What This Article Owns

M6.11 owns:

mask algebra, density, sparsity, and metadata ledgers
magnitude, activation, gradient, movement, curvature, and gate evidence
unstructured one-shot pruning, including SparseGPT and Wanda
N:M semi-structured patterns and sparse runtime eligibility
attention-head, MLP-channel, residual-width, and layer surgery
training-time gates and pruning schedules
LLM-Pruner, Sheared LLaMA, SliceGPT, ShortGPT, and Minitron comparisons
reconstruction, continued-training, LoRA, and distillation recovery choices
artifact validation, kernel dispatch, evaluation, and release handoffs

It defers:

input-dependent token/depth routing -> M6.8
knowledge-distillation derivations -> M6.10
numerical quantization representations -> M8
packaging/release when composed with low-bit W/A/KV -> M8.9/M8.10
generic nonquantized production integration -> separate, unassigned boundary

The owner question is:

Which weights, heads, channels, or layers should be removed, how are masks and importance scores trained or reconstructed, how is quality recovered, and when does the resulting artifact produce a real dense or sparse runtime saving?

0x00G Loop 1 Freezes Identity Before It Freezes A Mask

The historical sections below already contain the article’s pruning theory, tensor surgery, paper comparisons, recovery alternatives, and deployment closure. Their existence is not by itself a replayable evidence record. Loop 1 therefore freezes the objects that every later mechanism claim must preserve:

article source and digest
primary-paper revision and PDF digest
official-code repository and immutable commit
retained source-figure file and digest
five removal units and their dependency-closure requirement
five method contracts and six possible artifact classes
four non-interchangeable accounting ledgers
consumed and deferred article owners
claim ceiling for work that this evidence bundle did not execute

The accounting coordinates come from M4.1’s parameter, state, FLOP, and traffic contract. The distinction between a logical operation, an eligible tactic, and observed execution comes from M4.2’s GPU execution contract. M6.11 then owns the pruning-specific transformation between those two boundaries.

Loop 1 executes no pruning paper, model, framework, sparse kernel, quality evaluation, or deployment. Its synthetic manifest is an identity and invariant oracle only. In particular:

zero != physical absence
mask != packed sparse artifact
eligible sparse shape != selected sparse tactic
selected sparse tactic != measured end-to-end saving
static pruning != input-dependent routing
recovery by distillation != ownership of distillation theory

The checked article-wide gate in 0x0DB records prose coverage. Replayable publication closure remains pending until Loops 2 through 12 reconstruct and audit the historical mechanisms against this immutable baseline.

0x01 Mask Algebra And Artifact Semantics

Before comparing importance scores, the transformed object must be defined. A binary mask is the simplest local representation, but it is not automatically the final storage format or executable graph.

0x01A A Binary Mask Defines The Pruned Function

For a dense weight matrix

introduce a binary mask of the same logical shape:

The masked weight is

where is elementwise multiplication. The linear operation is

Define the number of retained entries as

Density and sparsity are then

The convention must be stated because some reports use sparsity for the fraction removed while others report the fraction retained. Here, 70% sparsity means and .

At training time, a fixed hard mask commonly constrains both the forward value and the update:

def masked_linear(x, weight, mask, bias=None):
    masked_weight = weight * mask
    return linear(x, masked_weight, bias)

def enforce_fixed_mask(weight, mask):
    # Needed if optimizer state, weight decay, or an external update can
    # change masked entries outside the masked forward expression.
    with no_grad():
        weight.mul_(mask)

If masked_weight = weight * mask remains in the differentiable graph, the gradient with respect to a masked entry is multiplied by zero. That does not necessarily clear pre-existing optimizer moments or protect the stored parameter from decoupled weight decay and custom optimizer logic. A fixed-mask implementation should test the stored masked positions after every update or remove those positions from the trainable representation.

A regrowth method intentionally follows another contract: masked weights may continue receiving latent gradients or scores so the mask can change. Calling that path “fixed pruning” would be incorrect even if the forward uses the same equation.

0x01B A Zero Value Is Not An Absent Value

The dense masked equation is mathematically sufficient to define the function. It does not define physical storage or execution.

Suppose an FP16 matrix has shape [4096, 11008]:

numel = 4,096 * 11,008 = 45,088,768 weights
dense FP16 bytes = 45,088,768 * 2 = 90,177,536 bytes
                 ~= 86.0 MiB

After pruning 50% of its weights, three possible artifacts have very different byte ledgers.

Dense Values With Implicit Zeros

The checkpoint still stores all FP16 values:

At 50% logical sparsity, this remains about 86.0 MiB. A standard dense GEMM still receives the full matrix and normally issues dense arithmetic.

Dense Values Plus An Explicit Mask

If the artifact stores all dense values plus a bit-packed mask:

With one mask bit per weight, the mask adds about 5.38 MiB. With an ordinary byte-valued boolean tensor, it adds about 43.0 MiB. This artifact can be useful for training or auditing, but it is larger than the original dense checkpoint unless the values at masked coordinates are omitted or compressed elsewhere.

Coordinate Sparse Values

A row-compressed sketch stores nonzero values, one column index per nonzero, and row pointers:

At 50% density, FP16 values plus 16-bit column indices cost roughly the same 86.0 MiB as the original dense FP16 values before row pointers, alignment, and format constraints. With 32-bit column indices, this rough artifact is larger. General sparse formats become byte-efficient only when the removed-value fraction is high enough to repay metadata and padding.

This is a storage break-even argument, not a kernel-performance prediction. Sparse execution also depends on nonzero distribution, matrix shape, batch, dtype, hardware, and implementation.

0x01C N:M Sparsity Is A Local Constraint, Not A Global Percentage

For N:M sparsity, partition a specified axis into local groups containing entries. Each group must satisfy

For 2:4, every four-entry group retains at most two values. These masks both have global 50% density:

valid 2:4 groups:
    [1 0 1 0] [0 1 0 1]

invalid for 2:4 despite 50% global density:
    [1 1 1 1] [0 0 0 0]

The grouping axis is part of the ABI. For matrix multiplication, current TensorRT documentation expresses the constraint along the reduction axis of the constant weight. Transposing or packing without updating the grouping interpretation can produce the same global zero count and an ineligible artifact.

Current PyTorch semi-structured documentation describes a compressed tensor containing retained values and metadata together. For FP16/BF16 2:4, it gives the storage fraction

Half the FP16 values survive, while metadata consumes the extra of the original dense byte budget. This is much more compact than generic CSR at 50% density because the local pattern can be encoded with small fixed metadata. It is also much less flexible: dtype, dimensions, device, layout, and supported operations are constrained. 0x04 will derive this runtime contract fully.

0x01D Structured Pruning Removes Coupled Tensor Slices

A structured unit is not represented correctly by independently masking each tensor. Consider a two-projection MLP:

with

Let be the ordered set of retained intermediate channels. Physical channel removal requires

The first matrix loses rows because those rows produce the removed channels. The second loses matching columns because those columns consume them. If only is sliced, expects a wider input. If only is sliced, the removed activations are still computed.

For a gated MLP such as SwiGLU,

the same retained set must close across three tensors:

In code:

def slice_gated_mlp(state, keep):
    keep = sorted_unique_int_indices(keep)
    assert keep.min() >= 0
    assert keep.max() < state.intermediate_size

    pruned = {
        "gate_proj.weight": state["gate_proj.weight"][keep, :].contiguous(),
        "up_proj.weight": state["up_proj.weight"][keep, :].contiguous(),
        "down_proj.weight": state["down_proj.weight"][:, keep].contiguous(),
    }
    config_patch = {"intermediate_size": len(keep)}
    return pruned, config_patch

Biases, tensor-parallel shards, fused gate/up layouts, quantization groups, and optimizer state need corresponding migration when present. The small trace states the axis invariant; it is not a universal checkpoint converter.

Attention-head pruning has the same closure problem across Q/K/V outputs and the corresponding input columns of . GQA and MQA add a dependency map because several query heads can share one KV head. 0x05 will derive those layouts rather than assuming one head ID owns four independent same-sized slices.

0x01E Layer Removal Changes Function Composition

Let one residual block be a function . A depth- network is the ordered composition

Keeping ordered layer set

produces

This is not a scalar mask on one matrix. It changes the module list, parameter names, layer count, pipeline partition, activation checkpoints, and often the assumptions inherited from initialization or depth-specific training. The surviving blocks accept the same residual width, so the transformed graph can usually use ordinary dense kernels and execute fewer block calls. Quality may still require substantial recovery because downstream blocks now receive a different representation trajectory.

0x01F Artifact Kind Determines The Runtime Question

A pruning transformation separates logical zeros from five physical artifacts: dense zeros, explicit masks, packed sparse values and metadata, smaller dense matrices, and a shorter layer graph, each with a different runtime path

A release manifest should name the artifact rather than only its sparsity:

@dataclass
class PruningArtifact:
    family: str                 # dense_zero, masked_dense, csr, nm, sliced_dense, depth
    original_shapes: dict
    transformed_shapes: dict
    removal_unit: str
    grouping_axis: str | None
    density_by_tensor: dict
    values: dict
    masks_or_indices: dict | None
    metadata: dict
    config_patch: dict
    source_checkpoint: str
    score_manifest: str
    recovery_checkpoint: str | None
    required_runtime: str

The fields are conceptual, not a proposed universal file schema. Their purpose is to make invalid comparisons difficult:

  • dense_zero must not claim sparse bytes or skipped arithmetic;
  • csr must count values, indices, pointers, and sparse operator support;
  • nm must identify local grouping, metadata, dtype, shape, and device rules;
  • sliced_dense must carry new shapes and configuration;
  • depth must carry the kept ordered layer map and new partition plan.
ArtifactStored valuesExtra stateExpected operatorWhat must be measured
dense zerosall original entriesnone or audit manifestdense GEMMnormally baseline bytes/arithmetic; verify any compiler specialization
masked denseall entriesbinary maskmasked training or custom fused pathmask overhead and whether zeros are skipped
general sparsenonzerosindices/pointers/layoutCSR/CSC/BSR sparse operatorbreak-even bytes, irregularity, shape and batch performance
N:Mlocally retained valuescompact pattern metadatasemi-structured sparse GEMMeligibility, selected tactic, fallback, dtype/shape support
sliced densesmaller rectangular tensorsindex/config mapordinary smaller dense GEMMnew shapes, hardware utilization, shard balance
depth-prunedsurviving complete blocksordered layer/config mapordinary blocks, fewer callsprefill/decode latency, pipeline and memory changes

0x01G Artifact Failure Lab

FailureWhy the tensor may still look validObservable consequenceRepair
report sparsity as file compressiondense zeros have the expected countcheckpoint bytes do not fallinventory the serialized artifact
expect dense GEMM to skip zerosmultiplication by zero is mathematically harmlesslatency and arithmetic remain denseconvert to a supported sparse artifact and confirm dispatch
add a byte mask to dense values and call it compressedmask correctly reconstructs the functionartifact becomes largeromit pruned values or physically slice structures
use global 50% sparsity as 2:4global count matchesN:M validator/kernel rejects patternenforce the local group constraint on the correct axis
slice only MLP producer rowssaved tensor is smallerconsumer dimension mismatchclose the same channel set across producer and consumer axes
slice gate but not up projectionindividual matrices loadgated product channels no longer correspondshare one ordered retained index set
renumber layers without preserving order mapmodule list has valid lengthcheckpoint/recovery lineage becomes ambiguousrecord source-to-target ordered layer map
count nnz but omit indices and pointersvalues reconstruct correctly with external metadatamemory estimate is too smallcount every decoder-required byte
sparse format is supported but dense tactic is selectedbuild succeedsno runtime speedupinspect selected kernel/tactic and profile target shapes
recovery checkpoint uses another maskmodel loads because shapes matchresults cannot be reproduced from pruning evidencepin mask/structure manifest into recovery lineage

0x01H Loop Boundary

Loop 1 establishes the object being selected and the object being deployed:

removal unit
    -> defines dependency closure

binary or structured decision
    -> defines the pruned mathematical function

artifact conversion
    -> defines values, shapes, metadata, and configuration

runtime operator
    -> determines whether storage or arithmetic can actually be skipped

It has not yet decided which unit is least important. The next loop will derive magnitude, activation, first-order Taylor, movement, curvature, gate, and ablation evidence. It will show on one toy matrix that these scores can select different units even under the same sparsity budget.

0x02 Importance Is A Counterfactual Under A Measurement Contract

Once the removable unit is defined, pruning needs evidence for choosing one unit instead of another. It is tempting to call this evidence an importance score and treat the resulting ranking as a property stored inside the model. That interpretation is too strong.

An importance score is an estimator for a counterfactual such as:

if this scalar, head, channel, or layer were removed,
how much would a chosen loss, output, or task metric change,
under a chosen dataset, reduction, compensation rule, and recovery budget?

Change any noun in that sentence and the ranking can change. Weight magnitude does not inspect data. Activation evidence sees a calibration distribution but not necessarily the task loss. A Taylor score linearizes one objective at the current parameters. Curvature approximates a local loss surface. A learned gate changes the training problem. Ablation measures an intervention directly, but only for the tested unit and context. Movement records a trajectory rather than the final checkpoint alone.

The evidence contract for every score in this article is therefore:

unit and tensor axes
quantity perturbed: parameter, activation, gate, layer, or task metric
objective or reconstruction target
data and reduction over examples/tokens
parameter state and training time
whether surviving weights may compensate
group aggregation and score normalization
local/global/cost-aware budget rule

The score is reproducible only when this contract is reproducible.

Loop 2 accompanies this prose with a synthetic, executable score fixture under docs/numinens-evidence/m6-11-pruning-structural-compression-executable-sparsity. It does not run a pruning paper or claim model quality. It asks a narrower question: if the checkpoint, calibration rows, objective, hook, unit, reduction, dtype, and shard layout are frozen, can an independent program recover the same score tensors and reject a one-field contract mutation? The fixture keeps these objects separate:

score family
    != removal counterfactual
    != tensor axis or structural gate
    != evidence collection hook
    != reduction order
    != ranking pool and budget
    != deployed sparse artifact

That separation is important because two implementations can both say “Taylor pruning” while one gates a complete head and averages per-example absolute derivatives, and the other sums absolute scalar terms after a mean-reduced batch backward. They are different estimators even before either mask is selected.

Let denote all model parameters and let group be controlled by a binary gate . At the current model, . Removing the group means setting . For calibration distribution and loss , the direct loss intervention is

This definition makes several choices explicit:

  • lower loss is assumed better, so a negative means the tested removal improved this measured objective;
  • the other groups remain present;
  • the surviving parameters are not updated;
  • the calibration distribution, token masking, sequence reduction, and loss normalization are part of the result.

For a task metric where larger is better, one could instead use

The two rankings need not agree. Cross-entropy can move slightly while exact match changes discontinuously. A perplexity improvement on one corpus does not prove that safety, another language, or long-generation behavior improved.

Direct ablation is often called an oracle for a greedy one-unit step, but it is not an oracle for the best joint subset. For two groups ,

in general. The groups can substitute for or amplify one another. Evaluating every individual unit costs one or more forward evaluations per candidate; evaluating all subsets is combinatorial. Iterative greedy pruning can recompute ablations after each deletion, but then its data and evaluation cost become part of the pruning algorithm.

Michel et al. used direct head masking to study the effect of individual heads, then used a cheaper gradient proxy for iterative head pruning. Their results are evidence for the tested Transformer, task, and datasets, not a theorem that all heads with small individual ablation effects can be removed jointly.

0x02B First-Order Taylor Evidence Comes From A Gate Perturbation

Direct ablation asks for along gate . Taylor expansion around the current gate gives

Removal uses . Ignoring curvature and higher-order remainder:

Because pruning usually wants the size of the perturbation rather than its signed direction, a common first-order score is

This is the head-mask sensitivity form used by Michel et al. and is closely related to the Taylor pruning criterion of Molchanov et al. The absolute value is inside the expectation. That placement matters:

Positive and negative per-example sensitivities can cancel before the absolute value. Ordinary backpropagation through a mean batch loss followed by an absolute value computes the left-hand style of estimator for that batch, not the per-example-absolute quantity on the right. Microbatching is an approximation; exact per-example gradients require additional machinery such as vectorized gradient computation.

For a scalar weight with gate ,

Hence the instantaneous scalar score is

For one gate shared by a structured group ,

and the chain rule gives

The actual first-order shared-gate score is therefore

Another implementation may aggregate independent scalar saliencies:

These are not interchangeable. The shared form preserves cancellation implied by one coherent group perturbation; the sum-absolute form discards cancellation and tends to favor larger groups unless normalized. The article must name which object was gated before presenting a group score.

At a full-batch stationary point, may be near zero, making signed first-order terms uninformative. Per-example or minibatch absolute sensitivities can remain nonzero because their variance remains nonzero. This is one reason timing and reduction order cannot be omitted.

SNIP applies a related connection-sensitivity derivative at initialization, before ordinary training. Michel-style head sensitivity evaluates a trained model. Movement pruning accumulates a first-order signal during fine-tuning. The algebra can look similar while the parameter state and question are different.

0x02C Magnitude Is A Zeroth-Order Proxy, Not Loss-Free Ground Truth

Magnitude pruning ranks scalar weights by

and removes the smallest. It needs no calibration examples, labels, backward pass, or auxiliary state. That makes it a necessary baseline and an attractive selection rule, but its justification is indirect: a small parameter perturbation is assumed to produce a small functional perturbation.

The assumption can fail under reparameterization. For two consecutive linear maps without an intervening non-homogeneous operation,

for nonzero scalar . The represented function is unchanged while the magnitudes in one matrix grow and those in the other shrink. Ranking raw magnitudes globally across those matrices is therefore not invariant to this equivalent scaling.

For a group, common magnitude summaries include

Raw norms systematically grow with group size under comparable entry distributions. Mean or root-size-normalized forms answer a different question. Comparing one 128-value head slice with one 11,008-value MLP group using raw norm is not a neutral operation.

Magnitude can still be effective empirically, especially when tensors have comparable scales and pruning is local. The claim must remain empirical and regime-specific. Data-free means the score did not inspect calibration data; it does not mean the ranking is independent of architecture, training, dtype, or parameterization.

0x02D Activation Evidence Approximates A Functional Perturbation

Consider one linear output row:

Deleting scalar while holding the other weights fixed creates output perturbation

Its calibration mean-squared size is

Taking the square root yields an activation-weighted magnitude proxy:

The normalization must be named. The formula above uses root mean square over rows. Wanda’s paper-defined score instead uses the input-feature norm:

When every feature is observed on the same valid rows, and RMS differ by one positive constant, so they induce the same within-matrix ranking. They are not numerically identical score tensors. The equivalence can also fail when feature populations differ, when scores from differently sized calibration runs are compared without normalization, or when a distributed implementation silently loses rows. Loop 3 will preserve Wanda’s paper contract as raw -norm evidence; this generic derivation keeps RMS because it directly represents mean-squared output disturbance.

This derivation explains why one input-channel statistic is broadcast across the output rows of . It approximates local output disturbance, not task-loss change, and it assumes no compensation by other weights. Correlated inputs create cross-terms when several weights are removed together. A later section will show how Wanda turns this score into a specific per-output one-shot LLM pruning rule, and how SparseGPT instead reconstructs surviving weights.

An activation-only group score, such as

asks whether the produced feature is numerically active. It does not ask whether the loss is sensitive to that feature. A large activation can be cancelled downstream; a small activation can carry decisive information.

Activation evidence is calibration-dependent:

prompt/domain mix
sequence lengths and token masks
prefill versus generated-token states
number of sampled tokens
whether statistics are RMS, mean absolute value, maximum, or covariance

The statistic and sample population belong in the score manifest.

0x02E Curvature Separates Deletion From Compensated Deletion

For parameter perturbation , a second-order local model is

where and is the Hessian. Near a trained local minimum, the classical Optimal Brain Damage approximation neglects and off-diagonal Hessian terms. Deleting scalar with no coordinated change elsewhere uses

and produces diagonal saliency

The formula is not merely weight squared. Curvature asks how expensive that coordinate direction is in the local loss geometry. A small weight in a sharp direction can outrank a larger weight in a flat direction.

Optimal Brain Surgeon keeps the full quadratic coupling and allows the surviving weights to compensate. It solves

Using a Lagrange multiplier gives

and predicted loss increase

The off-diagonal entries in distribute compensation across other coordinates. OBD and OBS therefore answer different counterfactuals:

OBD diagonal:
    delete this coordinate; ignore cross-coordinate compensation in the score

OBS full inverse curvature:
    delete this coordinate; choose the least-cost local compensation

The original OBS paper’s Figure 1 makes the missing counterfactual visible. A magnitude or diagonal deletion moves toward a coordinate plane while keeping the remaining coordinate fixed; OBS follows the local quadratic surface and changes the surviving coordinate. The figure is source-reported geometry from a two-weight teaching problem, not evidence that a model-scale Transformer Hessian was formed here.

Optimal Brain Surgeon Figure 1 showing magnitude deletion, diagonal OBD deletion, and an OBS path that compensates the surviving weight along a non-diagonal error surface

Source: Hassibi and Stork, Optimal Brain Surgeon, Figure 1. The crop preserves the paper’s own caption; this article uses it only to distinguish uncompensated and compensated deletion.

At Transformer scale, an exact full Hessian and inverse are generally impractical. Implementations use layerwise reconstruction objectives, Gauss-Newton/Fisher-like approximations, blocks, damping, factorizations, or sequential updates. If curvature is indefinite, singular, stale, or estimated from narrow calibration data, the clean quadratic interpretation weakens. Loop 3 will derive the scalable reconstruction path used by SparseGPT rather than pretending it materializes a model-wide exact Hessian.

0x02F Movement Is A Training-Trajectory Score

Movement pruning introduces score and forms a hard top-budget mask

The forward uses . Because hard top selection has zero or undefined ordinary derivatives, the hard variant uses a straight-through estimator for the score path. The approximate derivative is

Gradient descent on the score gives

If a positive weight receives a negative gradient, its next weight update moves it farther positive; , so its score increases. The same holds for a negative weight receiving a positive gradient and moving farther negative. Thus a high accumulated score indicates movement away from zero under the fine-tuning objective, not simply a large final magnitude.

This score cannot be reconstructed from the final checkpoint alone. It depends on optimizer trajectory, batches, learning rate, score initialization, mask schedule, and straight-through approximation. Section 0x08 compares hard and soft movement, learned gates, gradual schedules, and exact-budget enforcement. Here, movement appears only to establish that when the evidence was collected is part of the evidence contract.

0x02G Learned Gates Optimize A Different Training Problem

Instead of computing one fixed score from a checkpoint, a method can attach a gate to every removable group and learn gate parameters jointly with model parameters. A generic stochastic objective is

where can represent one unit, parameter count, FLOPs, bytes, or another resource proxy. The second term is an expected resource penalty, not necessarily an exact deployed budget.

The discrete gate is not ordinarily differentiable. Hard-concrete methods replace it during optimization with a reparameterized stochastic relaxation, then clip or threshold to obtain zeros. Voita et al. applied stochastic head gates and a differentiable relaxation in their NMT head-pruning study. Their observation that specialized heads survived longer is source-scoped evidence about that trained setting, not proof that linguistic specialization is a universal pruning score.

Gate-based selection has several design choices:

gate one scalar, one head, one channel, one layer, or one coupled group
freeze or update base weights
stochastic sample or deterministic expectation in the forward pass
temperature/relaxation schedule
uniform L0 count or hardware/resource-weighted cost
threshold, top-budget projection, or constrained optimization at export

A gate probability of is not a final mask, and a sparsity penalty does not guarantee an exact target width. The export rule can change the ranking learned under the relaxed objective. Section 0x08 derives the relaxation and schedule mechanics in full.

0x02H Global, Local, Normalized, And Cost-Aware Rankings Differ

Even fixed evidence does not determine a mask until a budget rule is chosen. Let be retained importance and the deployment cost of keeping group . A conceptual budget problem is

If all groups have equal cost and additive independent importance, keeping the largest scores solves this approximation. Structured Transformer groups do not generally have equal cost. One residual channel propagates across many matrices; one head changes several projections; one layer removes an entire block. Ranking by is a useful greedy heuristic, not an exact solution to a discrete knapsack problem and not a model-quality theorem.

The ranking scope also matters:

RuleConstraintBenefitFailure mode
tensor-localkeep a fixed fraction in every tensorprevents one tensor from being emptiedignores cross-tensor redundancy and cost
layer-localallocate within one blockpreserves broad depth coveragecan spend equal budget on unequal layers
global raw scorecompare all candidate units directlycan learn nonuniform sparsityscore scales and group sizes may be incomparable
global normalizednormalize within layer/tensor, then comparereduces scale imbalancenormalization removes potentially meaningful absolute sensitivity
cost-awareinclude bytes/FLOPs/latency proxytargets a resourceproxy may not match measured runtime
constrained candidate searchevaluate target shapes or subsetshandles interactions more directlyexpensive and still data/workload dependent

Molchanov et al. and Michel et al. discuss layer-wise normalization because raw Taylor scales can vary across layers. Movement Pruning compares local and global top-budget masks. None of those empirical choices supplies a universal normalization. The score manifest must record the ranking pool, normalization, tie-breaking rule, and exact cost model.

0x02I One Matrix, One Removal Budget, Six Different Answers

Consider a didactic matrix with six scalar candidates:

Let calibration input RMS by column be

and let one illustrative gradient estimate be

Label entries row-major as . For a diagonal-curvature teaching example use

Suppose accumulated movement scores and measured single-entry ablation loss changes are

Under a budget that removes exactly one entry:

EvidenceScore vector for a-fSmallest / removedWhy
magnitude[.05, .30, .20, .10, .40, .25]asmallest absolute weight
activation-weighted[.25, .03, .40, .50, .04, .50]bcolumn 2 is rarely energetic
instant Taylor[.10, .15, .002, .10, .008, .20]csmall current loss sensitivity
OBD diagonal[.25, .225, .10, .005, .08, .3125]dflattest weighted coordinate
direct ablation[.09, .04, .02, .03, -.01, .12]eremoval lowered measured loss
movement[.40, .20, .30, .50, .10, -.20]flowest retained trajectory score

The arrays are a constructed counterexample, not a reported empirical result. Their purpose is to disprove the idea that a sparsity budget uniquely defines the mask. Each ranking is internally consistent with its own evidence, yet all six choose differently.

A two-by-three weight matrix evaluated by magnitude, activation-weighted, Taylor, curvature, direct ablation, and movement evidence, with each score removing a different entry under the same one-weight budget

The disagreement is not a defect to average away blindly. It identifies which assumption needs validation:

magnitude -> parameter scale
activation -> calibration feature energy
Taylor -> local task-loss slope
curvature -> local quadratic geometry and compensation assumption
ablation -> measured one-unit intervention
movement -> fine-tuning trajectory

0x02J Tensor Hooks Must Preserve The Intended Reduction

For a linear weight , activation statistics live on the input axis and scalar magnitude/Taylor statistics live on the full matrix:

class LinearEvidence:
    def __init__(self, weight):
        self.input_sumsq = zeros(weight.shape[1], dtype=float64)
        self.input_count = 0
        self.taylor_abs_sum = zeros_like(weight, dtype=float64)
        self.taylor_examples = 0

    def observe_input(self, x, valid_token_mask=None):
        # Flatten batch/sequence only after excluding padding or invalid tokens.
        rows = select_valid_rows(x, valid_token_mask).to(float64)
        self.input_sumsq += (rows * rows).sum(dim=0).cpu()
        self.input_count += rows.shape[0]

    def observe_microbatch_gradient(self, weight, microbatch_examples):
        # grad is for this microbatch reduction, not a stale accumulated grad.
        g = weight.grad.detach().to(float64)
        self.taylor_abs_sum += (
            (g * weight.detach()).abs().cpu() * microbatch_examples
        )
        self.taylor_examples += microbatch_examples

    def finalize(self, weight):
        rms = sqrt(self.input_sumsq / self.input_count)       # [d_in]
        magnitude = weight.detach().abs().cpu()               # [d_out, d_in]
        activation_weighted = magnitude * rms[None, :]        # broadcast input axis
        taylor_microbatch = self.taylor_abs_sum / self.taylor_examples
        return magnitude, activation_weighted, taylor_microbatch

This trace intentionally says microbatch. If the loss is mean-reduced over 32 examples, taking one absolute value after its backward pass measures the absolute gradient of that 32-example mean. It does not equal the mean of 32 per-example absolute gradients. Smaller microbatches reduce cancellation but increase overhead and still define another estimator.

For a structured group, an explicit gate is safer than guessing how scalar scores should be aggregated:

def gate_sensitivity(model, batch, groups):
    z = ones(len(groups), requires_grad=True)

    def loss_for_one(z_value, sample):
        output = model.forward_with_structural_gates(
            sample.inputs[None, ...], z_value, groups
        )
        return objective(output, sample.targets[None, ...], reduction="sum")

    # Conceptual torch.func-style vectorized gradient: [batch, groups].
    dz_per_example = vmap(grad(loss_for_one), in_dims=(None, 0))(z, batch)
    expected_abs = dz_per_example.abs().mean(dim=0)
    abs_expected = dz_per_example.mean(dim=0).abs()
    return expected_abs, abs_expected

The two returned vectors are both valid statistics, but they are not the same statistic. Code, formula, and prose must agree about which one drives pruning.

Distributed collection adds another reduction boundary. Averaging one local mean per rank is correct only when every rank contributes the same denominator. The replayable contract reduces sufficient statistics instead:

@dataclass
class ScoreSufficientStatistics:
    input_sumsq: Tensor          # [d_in], fp64 teaching accumulator
    valid_input_rows: int
    taylor_abs_sum: Tensor       # [d_out, d_in]
    taylor_signed_sum: Tensor    # [d_out, d_in]
    taylor_examples: int


def merge_score_shards(shards):
    # Conceptually all_reduce SUM on every numerator and denominator.
    input_sumsq = sum(s.input_sumsq for s in shards)
    valid_rows = sum(s.valid_input_rows for s in shards)
    taylor_abs = sum(s.taylor_abs_sum for s in shards)
    taylor_signed = sum(s.taylor_signed_sum for s in shards)
    examples = sum(s.taylor_examples for s in shards)

    return {
        "input_rms": sqrt(input_sumsq / valid_rows),
        "expected_abs_taylor": taylor_abs / examples,
        "abs_expected_taylor": abs(taylor_signed / examples),
    }

This is not merely a distributed-performance choice. For shard sizes one and four, mean(local_mean) weights the one-example shard and four-example shard equally; sum(local_numerator) / sum(local_count) reconstructs the monolithic estimator. The valid-token denominator must replace the example denominator when the objective and hook are token-reduced.

0x02K Evidence Needs A Provenance Manifest

Scores should not be serialized as anonymous tensors:

@dataclass
class ScoreManifest:
    method: str
    removable_unit: str
    tensor_axes: dict
    counterfactual: str
    hook_location: str | None
    hook_quantity: str | None
    checkpoint_hash: str
    checkpoint_step: int | None
    objective: str
    dataset_hash: str | None
    ordered_sample_ids_hash: str | None
    example_count: int | None
    valid_token_count: int | None
    valid_row_policy: str | None
    batch_or_microbatch_size: int | None
    reduction: str
    reduction_order: str
    parameter_state: str       # initialization, pretrained, fine-tuning step, final
    compensation: str          # none, diagonal, inverse-curvature, reconstruction
    group_aggregation: str
    normalization: str
    ranking_scope: str         # tensor, layer, global
    cost_model: str | None
    target_budget: dict
    tie_break: str
    random_seed: int | None
    model_mode: str            # eval/train; records dropout and stochastic layers
    source_dtype: str
    accumulator_dtype: str
    world_size: int
    distributed_reduction: str | None
    trajectory_hash: str | None
    relaxation: dict | None
    score_tensor_hash: str

For magnitude, data fields may be null. For activation/Taylor/ablation, they are correctness state. For movement, the final score tensor is insufficient; the training and mask schedules also belong to lineage. For a learned gate, relaxation, temperature, penalty, and export threshold must be added.

The manifest also closes a subtle provenance gap between where a quantity is observed and what it means:

EvidenceRequired hook or stateAxis before reductionReduction that must remain visible
magnitudecheckpoint parameterfull parameter tensorelementwise absolute value, then named group/ranking reduction
activation-weightedlinear input before the projectioninput-feature axis plus valid calibration rowssum of squares and count, then square root; raw or RMS named
scalar Taylorparameter gradient plus the parameter, or an explicit scalar gatefull parameter tensorper-example/microbatch absolute placement named
structured Taylorone gate at the actual removable group outputgroup gate axissum inside the shared perturbation before absolute value
diagonal curvaturediagonal curvature estimate and matching parameter stateselected parameter coordinateno off-diagonal compensation may be implied
movementscore/weight/gradient trajectory at every scored updatescore tensor and timeordered update accumulation; final checkpoint alone is insufficient
learned stochastic gategate distribution parameters and random statedeclared structural gateexpected active probability is not an exact exported budget
direct ablationcontrolled model evaluation with one frozen interventioncandidate unitbaseline and ablated numerators/counts under the same sample order

The Loop 2 oracle uses an uneven two-shard split deliberately. It proves that the sufficient-statistic merge reproduces the monolithic score, while an unweighted mean of shard means does not. It also computes both and , and both the shared-gate Taylor score and the sum of scalar absolute scores, so none of those pairs can pass as aliases.

0x02L Score Failure Lab

FailureSilent causeObservable consequenceRepair
compute absolute value after averaging examplespositive and negative sensitivities cancelunits look unimportant despite high per-example sensitivitystate the reduction; use per-example or controlled microbatch statistics
compare raw magnitude across rescaled equivalent layersscore is not invariant to parameter scalingglobal mask follows scale conventionprune locally or use validated normalization/functional evidence
rank unequal groups by raw normlarger groups accumulate larger normsmall groups are removed disproportionatelynormalize for group size or use a shared gate
capture activations on unrelated short promptscalibration misses deployment statesselected mask fails on target domain/lengthstratify calibration and record token population
call raw calibration norm an RMS scoremissing normalizationscore magnitudes drift across calibration sizes even if one local ranking is unchangedname raw versus RMS and persist the valid-row count
call activation magnitude task importancedownstream loss is absenthigh-energy but cancelable features are protectedadd gradient/ablation evidence or state local reconstruction target
use $\mathbb E[gw]\mathbb E[gw
sum scalar Taylor values for a shared group without naming aggregationcancellation semantics changegroup score is implementation-dependentgate the group directly or state sum/absolute convention
apply diagonal OBD formula and claim OBS compensationoff-diagonal inverse curvature omittedrecovery effect is overstatedname diagonal deletion versus compensated deletion
compute movement score only at final checkpointtrajectory state is unavailablescore degenerates into another post-hoc proxycollect scores during the specified fine-tuning run
treat learned gate probability as an exact budgetrelaxed expected cost is not exported shapefinal mask misses target width/costproject or search under the exact target and reevaluate
prune many units using stale one-at-a-time ablationsunit interactions accumulatejoint quality collapsesrecompute iteratively or evaluate candidate subsets
rank score only, ignoring group costequal score can buy different savingspruning misses deployment objectivesolve or approximate a stated cost-constrained selection problem
average rank-local score meansunequal shard denominators are hiddenscore changes with world size or sampler partitionall-reduce sufficient-statistic numerators and counts
collect hooks with dropout active but replay in eval modestochastic model state is absent from provenancerepeated calibration produces another rankingfreeze and record model mode and random state

0x02M Loop Boundary

Loop 2 has established how pruning evidence is produced:

exact intervention -> direct but expensive and interaction-sensitive
zeroth order       -> cheap parameter proxy
activation         -> calibration-local functional perturbation
first order        -> objective slope under a named reduction
second order       -> local curvature, with or without compensation
movement/gates     -> training-time evidence with additional state

The next loop will hold the removal unit fixed at scalar weights inside one linear layer and compare three one-shot LLM paths: magnitude, Wanda’s activation-weighted per-output ranking, and SparseGPT’s layerwise reconstruction with compensating updates. That section will derive the shared reconstruction objective and show where the algorithms cease to be equivalent.

0x03 One-Shot Unstructured Pruning Is A Reconstruction Design Space

The word one-shot can suggest a cheap operation: compute one score, write zeros, and stop. That describes magnitude pruning and approximates Wanda’s selection path, but it does not describe SparseGPT. All three methods start from a trained checkpoint and avoid ordinary end-to-end retraining. They differ in the evidence they collect, the scope over which they allocate zeros, and whether retained weights may move to repair the local function.

The owner question for this loop is:

Given calibration inputs to one linear layer, should pruning preserve the original parameters, preserve each output approximately without weight updates, or solve a sparse reconstruction with compensating weight updates?

This section owns scalar unstructured masks, calibration statistics, local reconstruction, and the sequential layerwise compression process. It does not claim that an ordinary dense checkpoint with zeros executes fewer operations. Loop 4 owns N:M layout and sparse-kernel eligibility. Loops 5-7 own physically smaller head, width, and depth artifacts. Section 0x08 owns training-time scores, gates, budgets, and schedules; Loop 10 owns the cross-method recovery comparison.

Loop 3 accompanies this prose with a deterministic small-matrix reconstruction fixture. It does not execute PyTorch, a Transformer checkpoint, the SparseGPT or Wanda repositories, a GPU, or a sparse runtime. It does replay the algebraic contracts that must agree before those larger claims are meaningful:

fixed support least squares
    <-> exact OBS deletion of only the masked coordinates

SparseGPT fixed-mask eager update
    <-> SparseGPT fixed-mask lazy block update

Wanda metric and per-output allocation
    <-> exact mask-only zero artifact

dense upstream hidden states
    != hidden states propagated through the pruned upstream layer

The fixture freezes W, X, orientation, valid calibration rows, objective scale, damping, factorization convention, column order, mask-selection scope, tie policy, update schedule, block sizes, output dtype, and artifact identity. Its claim ceiling remains synthetic teaching evidence: paper quality, framework fidelity, kernel execution, and deployment speed remain unclaimed.

0x03A The Shared Objective Is Sparse Regression On Layer Inputs

Let one linear layer have

Each column of is one calibration activation vector. In a Transformer, batch and valid sequence positions are normally flattened into the sample axis after padding and excluded positions are handled. Let binary mask use for retained entries, and let denote values permitted after compression. The layerwise pruning problem is

This is already more specific than “remove small weights.” It says:

  • the dense layer output on calibration inputs is the teacher target;
  • the mask and possibly changed surviving values define the student layer;
  • error is measured before later nonlinearities repair or amplify it;
  • the sparsity constraint defines where selection is allowed to compete.

Jointly choosing the discrete mask and continuous reconstruction is a sparse regression problem and is NP-hard in general. Practical methods change the problem:

magnitude:
    choose M from abs(W); keep W_c = W

Wanda:
    choose M from abs(W_ij) * input_norm_j; keep W_c = W

SparseGPT:
    choose M adaptively from reconstruction evidence;
    update still-unfrozen entries of W_c to compensate

To expose the geometry, define

Then

The reconstruction decomposes by output row because every row shares the same input Gram matrix but has its own weight vector and mask. This row separability enables parallel arithmetic. It does not imply that every row has the same optimal mask.

For an average-squared objective it is convenient to define

so one row’s reconstruction cost is

Factors of and vary between papers and code. They change reported error scale but not an undamped ranking under one shared factor. Damping and mixed normalization do change the solution and must be recorded.

0x03B A Fixed Mask Is Easy Algebraically But Expensive Row By Row

Suppose the retained input-coordinate set for output row is . Let

be the original dense output and let contain the retained input rows. Once the mask is fixed, the best reconstructed retained row solves

The normal equation is

giving, when the system is invertible,

A damped or pseudoinverse solution is needed when calibration features are rank-deficient. The costly part is not writing this formula. Different rows usually retain different , so they require inverses of different masked Gram matrices. In general,

Masking an inverse is not the inverse of the masked system. Recomputing a -scale inverse independently for every output row would destroy the scalability gained from row separability. SparseGPT’s central systems idea is to share one elimination sequence across rows even though the rows freeze different values to zero.

0x03C OBS Shows What A Compensating Deletion Must Do

For one row, let be the coordinate to remove and let be its basis vector. Under the quadratic reconstruction objective, deletion requires

The constrained optimum derived in 0x02E is

with reconstruction increase

The component of the update is exactly , making the deleted weight zero. Off-diagonal entries of move correlated survivors. Without those entries, deletion changes the local function more.

Consider a two-feature teaching system:

Deleting the first weight without compensation uses

OBS instead gives

and

The removed coordinate is still zero. The lower error comes from changing the surviving coordinate. This is the distinction between mask-only pruning and local reconstruction, not evidence that any arbitrary update improves the full model.

Sequentially applying exact OBS updates can recover the exact optimum for a fixed final mask in this quadratic row problem when the algorithm eliminates only coordinates in that mask and keeps every surviving coordinate available for compensation until all deletions finish. Freezing an unpruned coordinate merely because its column was visited removes that coordinate from later compensation and changes the optimization problem. The direct exact path is still too expensive at LLM dimensions because each row mask induces its own active inverse. SparseGPT changes which coordinates remain updateable so one inverse-Hessian sequence can be shared across rows; it is an efficient ordered partial reconstruction, not an alias for independently solving every row’s fixed-support least-squares system.

0x03D SparseGPT Synchronizes Elimination, Not Row Masks

SparseGPT fixes an input-coordinate order

and defines an active suffix containing coordinate and all later coordinates. After processing , that coordinate is frozen: it is either zero or retained at its current reconstructed value, and later updates may touch only the remaining suffix.

Let

After eliminating the first coordinate of the active system, the next inverse can be obtained by one Schur-complement/Gaussian-elimination step:

Every output row uses this same sequence because depends only on layer inputs. Rows can still choose different weights to zero. At a processed column :

row where M[i,j] = 0:
    set the current reconstructed value to zero
    emit a compensation error into later columns

row where M[i,j] = 1:
    freeze the current reconstructed value
    emit zero pruning error

This is why “shared Hessian” must not be rewritten as “shared mask.” The algorithm synchronizes the elimination order and inverse-curvature state, not the row-level keep/drop decisions.

SparseGPT also selects masks adaptively in column blocks. At the start of a selection block, it ranks current reconstructed weights using predicted pruning error independently within each output row, matching the reference implementation’s topk(..., dim=1) allocation axis. It then performs the block’s updates before selecting the next mask block, allowing earlier compensation to influence later decisions. The paper distinguishes:

  • : columns considered together when allocating the block sparsity;
  • : columns whose updates are accumulated before one lazy matrix update.

They are algorithmic batching controls, not the deployed sparse layout. Loop 4 will treat an N:M group as a separate artifact constraint.

SparseGPT layer reconstruction from calibration activations through a damped input Gram matrix, adaptive block mask selection, column freezing, compensation of future weights, and propagation to the next Transformer layer

SparseGPT Figure 4 showing fixed-mask column elimination with a synchronized sequence of inverse Hessians and adaptive mask selection by iterative blocking

Source figure: Frantar and Alistarh, SparseGPT, Figure 4, extracted from the revision-pinned 2301.00774v3 PDF. The left panel distinguishes the row mask from the shared column-elimination sequence; the right panel shows that mask selection is refreshed by blocks. The figure explains the paper algorithm and does not constitute a reproduced pruning result.

0x03E The Cholesky Code Path Is Not The Raw OBS Formula

The official implementation first accumulates a scaled input Gram matrix, handles input coordinates with zero observed energy, applies diagonal damping, inverts the system, and stores an upper Cholesky factor of that inverse:

The reference code uses a relative damping value

with supplied as percdamp. Damping improves numerical conditioning but changes the reconstruction geometry. It is not a harmless implementation detail.

During ordered elimination, the code uses

For a current weight vector and frozen value (zero for pruned rows, current value for kept rows), it forms

and applies

The mask score used by the implementation is proportional to

for the current selection state. should not be casually labeled the original raw diagonal of . It is the diagonal factor consumed by the ordered Cholesky/elimination procedure. The paper reuses inverse-Hessian notation around this factorized algorithm; code-level explanations should name which object is actually indexed.

A faithful teaching sketch is:

def sparsegpt_linear(weight, calibration_inputs, sparsity,
                     lazy_block=128, mask_block=128, damp_ratio=0.01):
    # weight: [d_out, d_in]
    # X:      [d_in, valid_calibration_tokens]
    X = flatten_valid_tokens(calibration_inputs).T.float()
    H = (2.0 / X.shape[1]) * (X @ X.T)                 # [d_in, d_in]

    dead = diag(H) == 0
    weight[:, dead] = 0
    H[dead, dead] = 1
    H += damp_ratio * mean(diag(H)) * eye(H.shape[0])

    Hinv = inverse_from_cholesky(H)
    R = upper_cholesky(Hinv)                           # R.T @ R = Hinv
    W = weight.float().clone()
    Q = zeros_like(W)

    for block_start in range(0, W.shape[1], lazy_block):
        block_end = min(block_start + lazy_block, W.shape[1])
        block_errors = zeros(W.shape[0], block_end - block_start)

        for j in range(block_start, block_end):
            if starts_new_mask_block(j, mask_block):
                cols = current_mask_block(j, mask_block, W.shape[1])
                score = W[:, cols].square() / diag(R)[cols].square()[None, :]
                mask = lowest_fraction_per_output_row(score, sparsity, dim=1)

            current = W[:, j]
            frozen = current.clone()
            frozen[mask[:, j]] = 0
            Q[:, j] = frozen

            error = (current - frozen) / R[j, j]
            W[:, j:block_end] -= error[:, None] * R[j, j:block_end][None, :]
            block_errors[:, j - block_start] = error

        # Lazy update gives the same accumulated block correction to the tail.
        W[:, block_end:] -= block_errors @ R[block_start:block_end, block_end:]

    return Q

This is mechanism pseudocode, not a drop-in reimplementation. Production code must define exact block boundaries, stable threshold/tie behavior, transposed Conv1D storage, dtype restoration, device placement, and optional joint quantization. The final zero pattern still needs an executable sparse format and kernel before it becomes acceleration.

0x03F Wanda Keeps The Diagonal Perturbation And Drops Reconstruction

Wanda begins from the no-compensation perturbation derived in 0x02D. For one row,

Deleting a set changes the output by

Its squared calibration norm is

Wanda uses the individual diagonal term’s square root:

It ignores the cross-feature terms and performs no compensating update. This makes the method much cheaper than inverse-curvature reconstruction, but also states its approximation clearly: correlated deletion errors are not solved jointly.

The input norm has shape and is broadcast over output rows:

For unstructured sparsity ratio , Wanda sorts each output row and removes

lowest scores in that row. Per-output ranking is part of the method, not a cosmetic implementation choice. It ensures every output retains the same number of incoming connections. A global threshold could spend most zeros in a few output rows; a layer-global magnitude baseline and a row-local Wanda mask therefore differ in both metric and allocation scope.

Consider

Wanda scores are

At one removal per output, both rows remove the second input connection. The first feature’s high calibration energy protects even the first row’s small weight. A global two-entry budget would remove scores and from the first row, leaving the second row dense. Same score tensor, different allocation contract, different mask.

Wanda pruning trace showing calibration inputs reduced to one norm per input feature, broadcast multiplication with a weight matrix, stable sorting within each output row, and zeroing without survivor weight updates

Wanda Figure 1 comparing layer-global magnitude pruning with activation-weighted scores grouped independently within each output row

Source figure: Sun et al., Wanda, Figure 1, extracted from the revision-pinned 2306.11695v3 PDF. It makes two changed objects visible at once: the score uses input-feature activation norms, and the allocation scope changes from one layer-wide pool to independent per-output rows. The shown example is paper-local explanatory evidence, not a universal quality claim.

0x03G The Wanda Hook And Mask Axis Must Agree

The official implementation flattens sequence positions, transposes the input to [d_in, token_positions], and accumulates squared feature norms over calibration examples. Its stored scaler_row is named from an older wrapper, but its length is the input-column count, not the output-row count. The square root is taken before broadcasting into the weight metric.

A clearer equivalent trace is:

class WandaInputNorm:
    def __init__(self, d_in):
        self.sequence_sumsq_mean = zeros(d_in, dtype=float64)
        self.sequence_count = 0

    def add_batch(self, x, valid_token_mask=None):
        # x: [batch, sequence, d_in]
        # Preserve a named population; do not silently include padding.
        for sequence, mask in zip(x, valid_token_mask):
            rows = sequence[mask].to(float64)
            sequence_sumsq = rows.square().sum(dim=0)  # [d_in]
            self.sequence_sumsq_mean = online_mean(
                self.sequence_sumsq_mean,
                self.sequence_count,
                sequence_sumsq,
            )
            self.sequence_count += 1

    def score_and_mask(self, weight, sparsity):
        input_norm = sqrt(self.sequence_sumsq_mean)       # [d_in]
        score = weight.abs() * input_norm[None, :]        # [d_out, d_in]
        k = floor(weight.shape[1] * sparsity)
        prune_idx = stable_argsort(score, dim=1)[:, :k]   # sort each output row
        mask = zeros_like(weight, dtype=bool)
        mask.scatter_(dim=1, index=prune_idx, value=True)
        return score, mask

Averaging per-sequence squared norms differs from one total token norm only by a common factor, whatever the valid lengths: the mean of per-sequence sums is the sum over all valid tokens divided by . It therefore does not change row rankings, and the official WrappedGPT.add_batch keeps the same kind of quantity: the squared-norm total over the positions it receives, divided by the number of calibration sequences. An accumulator that first divided each sequence by its own length would give short sequences’ tokens more weight and could change row rankings when lengths vary; this one does not. Padding inclusion, packed samples, and prompt-domain mixtures change which tokens are measured, not that proportionality. The calibration manifest must say what population produced .

After mask creation, reference Wanda writes exact zeros:

weight[mask] = 0

There is no reconstruction update and no backward pass. Optional later LoRA or full recovery is a different pipeline. Calling a recovered Wanda checkpoint “Wanda with no updates” would erase an important experimental distinction.

0x03H Sequential Layer Processing Changes The Calibration State

Both reference pipelines process Transformer blocks in order to limit memory. The orchestration is more involved than running independent hooks on the original dense model:

1. disable generation KV caching;
2. capture the first block's hidden-state inputs for calibration sequences;
3. retain attention masks, position IDs, and other required block state;
4. for block l:
       run current block on current inputs with linear hooks;
       collect input norms or Gram/Hessian state;
       prune the selected linear modules;
       rerun the now-pruned block;
       use those outputs as block l+1 calibration inputs;
5. restore model configuration and serialize pruning provenance.

The first pass through block measures its dense sublayers under the hidden states produced by already-pruned blocks . The second pass propagates the new block error forward. Thus later layers are calibrated on a progressively compressed upstream model, not necessarily on hidden states cached once from the original dense model.

A state-safe teaching skeleton is:

@no_grad()
def sequential_one_shot(model, calibration_batches, method):
    previous_use_cache = model.config.use_cache
    model.config.use_cache = False
    try:
        inps, attention_mask, position_ids = capture_first_block_inputs(
            model, calibration_batches
        )

        for layer_id, block in enumerate(model.layers):
            block_device = device_for_block(model, layer_id)
            inps = inps.to(block_device)

            collectors = register_linear_input_collectors(block, method)
            dense_outs = run_block_samples(
                block, inps, attention_mask, position_ids
            )
            remove_hooks(collectors)

            for name, linear in ordered_target_linears(block):
                if method == "wanda":
                    score, mask = collectors[name].wanda_mask(linear.weight)
                    linear.weight[mask] = 0
                elif method == "sparsegpt":
                    linear.weight = collectors[name].sparsegpt_reconstruct(
                        linear.weight
                    )

            pruned_outs = run_block_samples(
                block, inps, attention_mask, position_ids
            )
            inps = pruned_outs
            free_layer_collectors(collectors)
    finally:
        model.config.use_cache = previous_use_cache

dense_outs is shown to distinguish the statistics pass, but it need not be retained after pruning if memory is constrained. Real model blocks may require rotary position state, cache-position arguments, cross-attention inputs, or architecture-specific projection orientation. Catching the first block by raising a generic exception is a convenient reference-code technique, not a recommended production control-flow API.

0x03I Calibration Memory Is Part Of Method Cost

For calibration sequences of length , hidden width , and element size , one hidden-state buffer costs

With , , , and FP16 storage:

Separate input and output buffers therefore consume about GiB before model weights, hooks, and solver state. Streaming or smaller calibration batches can lower residency, but then accumulation order and host/device transfer become part of the implementation.

Wanda’s persistent statistic for one linear layer is only

where is the accumulator element size. SparseGPT stores a dense reconstruction matrix:

At FP32 accumulator precision:

input-norm vectordense or factor
409616 KiB64 MiB
1100843 KiBabout 462 MiB

Processing several linears’ collectors simultaneously multiplies this state. SparseGPT also pays approximately to accumulate the Gram matrix, for factorization/elimination state, and for reconstruction updates. Lazy update blocks improve compute-to-memory behavior; they do not make the dense curvature state linear in .

The paper’s calibration choice of 128 C4 segments of length 2048 and its reported runtimes are source-reported experimental settings, not universal defaults. Domain, tokenizer, sequence length, padding, and number of samples remain part of the released artifact’s evidence lineage.

0x03J Reconstruction Evidence Needs Its Own Manifest

A mask file alone cannot replay a one-shot reconstruction. The same mask can produce different survivor values under a different calibration population, damping rule, factorization, elimination order, block schedule, or dtype. A minimal evidence record is therefore method- and action-aware:

@dataclass(frozen=True)
class ReconstructionManifest:
    dense_checkpoint_sha256: str
    calibration_values_sha256: str
    ordered_sample_ids_sha256: str
    weight_shape: tuple[int, int]
    calibration_layout: str          # feature_by_valid_position
    objective: str                   # layer_output_squared_error
    objective_scale: str             # e.g. 2 / valid_positions
    accumulator_dtype: str
    dead_feature_policy: str
    damping_rule: str
    damping_ratio: float
    factorization: str               # upper_cholesky_of_inverse
    column_order: tuple[int, ...]
    selection_metric: str
    allocation_scope: str
    mask_refresh_schedule: str
    tie_policy: str
    survivor_update: str             # none, exact_obs, ordered_partial
    lazy_update_block_size: int
    selection_block_size: int
    output_dtype: str
    expected_mask_sha256: str
    expected_weight_sha256: str
    expected_dense_shape: tuple[int, int]
    runtime_format: str               # dense_tensor_with_exact_zeros

The Loop 3 fixture uses a 2 x 4 weight matrix and four input features over five calibration positions. It records separate masks for a fixed-support comparison, Wanda, and adaptive SparseGPT. Its executable oracle proves:

  1. independently solved fixed-support least squares matches exact sequential OBS deletion to numerical tolerance;
  2. SparseGPT’s ordered partial reconstruction is not silently relabeled as that exact row-specific solve;
  3. adaptive SparseGPT removes the declared count independently per output row inside each selection block;
  4. eager and lazy SparseGPT updates match for the same frozen mask, factor, order, and block boundaries;
  5. the upper-factor diagonal consumed by code is not generally the raw diagonal of the inverse Gram matrix;
  6. Wanda changes no surviving value and its per-output mask is reproduced from the recorded activation population;
  7. propagated hidden states after pruning differ from dense cached hidden states, making sequential layer processing observable; and
  8. every output remains an ordinary dense-shape tensor with exact zeros, so no runtime acceleration claim is admitted.

The oracle also rejects one-field mutations for transposed calibration axes, changed masks, reordered columns, missing damping, changed tie rules, mixed eager/lazy schedules, altered hashes, survivor-update aliasing, and promoted runtime or quality claims. That is evidence for the mechanism contract, not a substitute for evaluating a real model.

0x03K Selection, Reconstruction, And Recovery Are Three Separate Knobs

These methods should not be compared under labels that hide their update budgets:

PathCalibration evidenceMask allocationSurvivor update during pruningLater recovery
magnitude mask-onlynoneconfigured global/layer/tensor scopenoneoptional, separate
Wanda referenceinput feature normsfixed fraction per output rownoneoptional, separate
fixed-mask least squares / AdaPrune familylayer inputs and dense outputsmask selected firstregression or SGD reconstructionoptional
OBC/OBQ-style greedyinverse-curvature stateone decision at a timeexact local compensation under quadratic modeloptional
SparseGPTdamped layer-input curvatureadaptive column blocksordered Cholesky/lazy compensationoptional
recovery fine-tuningtraining data and objectivemask fixed or constrainedmany optimizer stepsthis row is recovery

“No retraining” does not mean “no data,” “no updates,” or “one forward evaluation.” SparseGPT changes retained weights without end-to-end gradient training. Wanda uses calibration data but leaves retained values untouched. Magnitude can be data-free but still needs a declared allocation scope.

The methods also optimize local output fidelity, not task quality directly. A lower on calibration data can correlate with better downstream behavior, but it does not prove equivalent generation, safety, factuality, long-context behavior, or latency. Those are independent evaluation gates.

0x03L Reconstruction Failure Lab

FailureSilent causeObservable consequenceRepair
use as [samples, d_in] in without transposingsample and feature axes swappedHessian shape follows token count or multiplication failsassert X.shape == [d_in, n] before Gram accumulation
include padded tokens in activation normspadding population differs across batchesWanda scores follow sequence padding policymask invalid token rows before accumulation
call Wanda scaler_row an output statisticwrapper name confused with tensor axisnorm is broadcast on wrong axisassert statistic length equals weight.shape[1]
sort Wanda scores globallyallocation scope changedsome output rows lose many more connectionsstable-sort along dim=1 for reference per-output pruning
use raw while copying Cholesky update codeformula object and code object differmask and compensation do not reproduce reference pathname and test the upper factor used by elimination
omit damping on rank-deficient calibration featuresinverse/factorization is unstableNaNs, failed Cholesky, or extreme updatesrecord damping rule and dead-feature policy
reconstruct pruned coordinates again laterfrozen-state invariant brokenzeros silently become nonzerofreeze processed columns and validate final mask
reuse one masked inverse for all row masksinverse of submatrix assumed equal to submatrix of inversecompensation is mathematically wronguse synchronized elimination or solve each fixed mask correctly
select all SparseGPT masks before any compensationadaptive state removedlater blocks ignore earlier updatesselect masks at documented block boundaries
feed dense original hidden states to every later blocksequential propagation omittedcalibration no longer matches progressively pruned modelrerun each pruned block and propagate its outputs
leave use_cache=True during calibrationgeneration state leaks into block traversalexcess memory or architecture-specific cache behaviordisable and restore cache configuration with finally
report one-shot as zero-costfactorization, updates, and two block passes omitteddeployment preparation estimate is wrongpublish calibration time, peak memory, and artifact conversion time
benchmark dense zero tensors as sparse executionartifact remains ordinary denseFLOPs and latency are unchangedconvert to supported format and inspect selected kernels in Loop 4
compare recovered Wanda with unrecovered SparseGPTtraining budgets differmethod conclusion is confoundedseparate selection, reconstruction, and recovery columns
call ordered partial SparseGPT reconstruction the exact row-wise least-squares optimumretained earlier columns were frozen before later deletionsreported optimality exceeds the executed update setcompare against the fixed-support solve and record the updateable suffix at every step
publish only the zero maskdamping, order, factorization, and survivor values are absentthe reconstructed checkpoint cannot be replayedpublish the reconstruction manifest and final weight digest
average per-rank Gram matrices without sample-count weightingcalibration shards have different valid-position countsfactorization depends on world-size partitioningreduce Gram numerators and valid counts, then normalize once

0x03M Loop Boundary

Loop 3 has followed the complete one-shot path:

calibration hidden states
    -> input norms or dense Gram/curvature state

mask allocation
    -> per-output Wanda ranking or adaptive SparseGPT blocks

weight action
    -> exact zeros only, or zeros plus compensating survivor updates

sequential propagation
    -> pruned block outputs become the next block's calibration inputs

artifact
    -> same dense tensor shapes with many zero entries

That last line is the handoff. Loop 4 will constrain zeros into N:M groups, derive value-plus-metadata storage, validate axis and shape eligibility, and prove whether an actual sparse kernel is selected. Loop 3 does not claim latency from an unstructured zero count.

0x04 N:M Sparsity Is A Local Mask, A Packed ABI, And A Kernel Decision

Unstructured pruning asks which scalar weights may become zero. N:M sparsity adds a stricter question: which zeros may coexist inside every small, hardware-visible group? That regularity sacrifices mask freedom in exchange for fixed work and compact location metadata. It still does not make the result executable by itself.

The owner question for this loop is:

How does a row-local N:M selection become a validated value-plus-metadata artifact, enter an eligible sparse matrix multiplication, and demonstrate a measured saving against the same pruned values executed densely?

This section uses N_keep:M to mean that exactly N_keep positions are selected in each logical group of M weight positions. Hardware and runtime documentation often phrases the same 2:4 constraint as “at least two zeros” or “at most two nonzeros.” Those statements are compatible for ordinary nonzero weights, but a compressed format still reserves a fixed number of value slots. A selected slot may numerically contain zero, so selected position count, numerical nonzero count, and stored value count must not be treated as synonyms.

There is also an API naming trap. The official SparseGPT and Wanda reference code accepts arguments named prunen and prunem, where prunen is the number of entries zeroed in each group. This article’s N_keep is the number retained:

For 2:4 the two numbers are both 2, hiding the mismatch. A generalized N:M implementation must declare which convention it uses rather than inheriting meaning from a parameter name.

Loop 4 accompanies the prose with a dependency-free 2 x 8 fixture. It projects two positions in every four-element row/K group, deliberately retains one numerically zero value, encodes the six possible 2:4 patterns by a logical rank, decodes the teaching representation, and checks aligned tensor-parallel K shards. That proves local selection and reference representation parity. It does not import PyTorch, create a CUDA tensor, invoke cuSPARSELt, build a TensorRT engine, observe a sparse kernel, or measure latency.

The fixture keeps the resulting states separate in one immutable record:

@dataclass(frozen=True)
class NMRuntimeManifest:
    logical_operator: str
    stored_weight_shape: tuple[int, int]
    reduction_axis: int
    n_keep: int
    group_size: int
    stable_tie_policy: str
    selected_mask_sha256: str
    logical_values_sha256: str
    logical_pattern_ranks_sha256: str
    original_shape: tuple[int, int]
    storage_orientation: str
    k_shards: tuple[tuple[int, int], ...]
    documentation_snapshot_sha256: tuple[str, ...]
    backend_conversion_executed: bool
    sparse_operator_eligible: bool
    sparse_tactic_selected: bool
    sparse_operator_observed: bool
    speedup_measured: bool

The last five booleans remain false in this article-local oracle. A valid mask and a reversible logical pack cannot be promoted into any of those runtime claims.

0x04A The N:M Projector Solves Many Tiny Constrained Problems

Let

be an nn.Linear weight stored as [out_features, in_features]. Partition each output row along the input/reduction axis into consecutive groups

Let be the estimated cost of deleting position in one group. A mask-only local projection solves

Because every retained position removes its deletion cost from the objective, the solution keeps the largest costs, or equivalently prunes the smallest. This derivation is simple but useful: the local budget is independent of the score definition.

  • magnitude projection can use ;
  • Wanda can rank by , or its square, because squaring nonnegative scores preserves order;
  • a SparseGPT N:M block uses its current reconstruction score involving the Cholesky-factor diagonal and then compensates later weights;
  • a learned or gradient score can use the same projector while changing the evidence and recovery contract.

The projector is therefore not “the pruning method.” It is the budget operator placed after a score or inside a reconstruction process.

A deterministic implementation must also define ties. topk is not a complete artifact specification if equal scores can choose different positions across framework versions or devices. One teaching implementation uses the original input index as a stable secondary key:

def project_nm(score, n_keep, group_size):
    # score: [d_out, d_in], larger means more important to retain
    d_out, d_in = score.shape
    assert 0 < n_keep <= group_size
    assert d_in % group_size == 0

    groups = score.reshape(d_out, d_in // group_size, group_size)
    original_index = arange(group_size).expand_as(groups)

    # Stable descending order: score first, then lower original index.
    order = stable_lexsort(
        primary=-groups,
        secondary=original_index,
        dim=-1,
    )
    keep = order[..., :n_keep]

    mask = zeros_like(groups, dtype=bool)
    mask.scatter_(dim=-1, index=keep, value=True)
    return mask.reshape(d_out, d_in)

The actual implementation may use a stable sort or a documented deterministic topk. The invariant is:

The numerical nonzero validator is weaker:

because a retained value can already equal zero.

0x04B Magnitude, Wanda, And SparseGPT Share Groups But Not Weight Actions

Loop 3’s three one-shot paths remain different after imposing 2:4:

PathLocal selection when a group beginsAction on retained valuesState carried to the next group
magnitude N:Mprune the smallest $W$ values per row/group
Wanda N:Mprune the smallest $W_{ij}|X_j|_2$ per row/group
SparseGPT N:Mprune the smallest current candidates per row/grouplater columns receive reconstruction updatesdamped factor, frozen prefix, accumulated errors

The official SparseGPT inner loop creates a new local mask whenever the current column index is divisible by prunem. It ranks the next group using the current reconstructed weights, marks prunen locations per output row, then performs the same ordered compensation derived in 0x03E. Wanda’s reference path instead computes one score tensor, applies local topk to each row/group, and writes zeros without changing survivors.

A common wrapper can expose the naming difference explicitly:

def nm_mask_from_score(score, n_keep, group_size):
    n_prune = group_size - n_keep
    grouped = score.reshape(score.shape[0], -1, group_size)
    prune = stable_argsort(grouped, dim=-1)[..., :n_prune]
    mask_prune = zeros_like(grouped, dtype=bool)
    mask_prune.scatter_(-1, prune, True)
    return mask_prune.reshape_as(score)


# Reference CLI translation, not a universal naming convention.
prunen = group_size - n_keep
prunem = group_size

For 2:4, a global 50% mask can have lower unconstrained reconstruction error because it can spend zeros unevenly. That is not a fair runtime comparison if the global mask cannot enter the 2:4 operator. Quality should be compared under the same local constraint; runtime should be compared using the same pruned values through dense and sparse execution paths.

0x04C The Reduction Axis Is Part Of The Artifact

For input

nn.Linear computes

The logical reduction dimension is . In the stored W[d_out,d_in] tensor, 2:4 groups therefore run across consecutive input columns within each output row. TensorRT’s current matrix-multiply rule states the same condition in graph terms: when a constant supplies one input, each four-element vector along reduction axis must contain at least two zeros.

These descriptions can look contradictory after a transpose:

logical linear weight:
    W[out, in]          groups along W's last axis

matmul operand:
    W^T[in, out]        the same groups are no longer horizontal on paper

kernel descriptor:
    op(A), op(B)        physical major order and transpose flags decide loading

The solution is not “always group the last stored dimension.” The artifact must record the logical operator, original shape, reduction axis, storage orientation, transpose state, and backend packing version. Group before or after a transpose only when the decoder and kernel descriptor preserve the same logical coordinates.

Tensor parallelism adds another boundary. If output features are sharded, each shard retains complete groups. If the reduction dimension itself is sharded, every shard’s global offset and length must align to :

Otherwise one logical group straddles two devices and neither shard contains enough information to validate or pack it independently. A safe conversion order is:

logical pruning mask
    -> validate full tensor coordinates
    -> choose tensor-parallel placement
    -> assert each K shard begins and ends on group boundaries
    -> pack each physical shard for its target kernel

Padding is also semantic. Padding to a runtime tile multiple can create new all-zero positions, but the manifest must retain the original shape and exclude padded outputs from model semantics. PyTorch’s current prototype does not promise arbitrary automatic padding; it rejects unsupported shapes.

A validator should consume the mask and the physical tensor separately:

def validate_nm(weight, selected_mask, n_keep, group_size, k_offset=0):
    assert weight.ndim == 2
    assert weight.shape == selected_mask.shape
    assert k_offset % group_size == 0
    assert weight.shape[1] % group_size == 0

    mask_groups = selected_mask.reshape(weight.shape[0], -1, group_size)
    value_groups = weight.reshape(weight.shape[0], -1, group_size)

    assert (mask_groups.sum(dim=-1) == n_keep).all()
    assert (value_groups.masked_fill(mask_groups, 0) == 0).all()
    assert (value_groups.ne(0).sum(dim=-1) <= n_keep).all()

The second assertion catches nonzero values outside selected slots. The third checks numerical eligibility. Neither proves that the shape, dtype, device, operator, or backend is supported.

0x04D Recovery Must Preserve The Local Pattern

One-shot projection can damage quality because it removes half the values in every group even where all four matter. Recovery can update selected values, but it must not silently regrow pruned positions.

For fixed mask , a masked gradient step is

where is optimizer state. Masking only the current gradient is insufficient when momentum from before pruning remains in a deleted slot. Weight, gradient, momentum-like state, and any shadow/master copy must respect the same mask.

def enforce_fixed_nm_after_step(parameter, selected_mask, optimizer):
    parameter.mul_(selected_mask)

    state = optimizer.state.get(parameter, {})
    for value in state.values():
        if is_tensor(value) and value.shape == parameter.shape:
            value.mul_(selected_mask)

    validate_nm(parameter, selected_mask, n_keep=2, group_size=4)

Recovery is usually simpler in an ordinary dense tensor carrying exact zeros. After recovery, export should revalidate the pattern and then compress once. Converting to a backend-packed inference tensor inside every forward pass charges preparation cost to every request and can erase the saving. Training directly on a compressed sparse weight also requires explicit sparse-gradient support; current PyTorch semi-structured operator documentation does not promise backward with respect to every sparse operand.

Merges and transforms can invalidate the pattern after recovery:

  • merging a LoRA delta adds values into pruned positions unless the delta or merged result is reprojected;
  • quantization can preserve exact zero only when its reconstruction convention represents zero exactly at those slots;
  • tensor transposition, sharding, or permutation can move group boundaries;
  • optimizer checkpoint restoration can reintroduce stale dense state.

The N:M manifest therefore belongs to the final transformed artifact, not just the checkpoint immediately after pruning.

0x04E Metadata Has An Information Lower Bound And A Runtime Encoding

One group with exactly selected positions among has

possible location patterns. Any lossless pattern code needs at least

bits per group. For 2:4:

That is an information lower bound, not the hardware ABI. Current PyTorch semi-structured documentation budgets 2 metadata bits per stored value. Two stored values therefore use 4 metadata bits per 2:4 group. The extra code space can support a simple fixed decoder and packing across metadata words. The physical code values and swizzle are backend details; consumers should use the backend compressor rather than inventing an enum from the six patterns.

For element width bits and backend metadata bits per group:

With PyTorch’s documented 2:4 ledger, , , and :

Value dtypeDense bits per groupRetained value bitsMetadata bitsSparse/dense
FP16/BF1664324
INT832164

The lower value width makes fixed location metadata a larger fraction of INT8 storage. “Half the values” is therefore not “half the artifact bytes.”

For an FP16 MLP matrix with

the dense payload is exactly 86 MiB. The documented 2:4 representation has:

retained values: 43.000 MiB
pattern metadata: 5.375 MiB
logical compressed payload: 48.375 MiB

Allocator alignment, wrapper objects, workspace, optional packed transpose, serialization headers, and shard padding are outside that logical payload and must be counted separately in a process-level memory measurement.

A 2:4 pruning and execution trace showing row-local score projection, six possible position patterns, retained values plus four metadata bits, reduction-axis and shape validation, backend compression, sparse-kernel admission, dense tactic fallback, and measured runtime proof

NVIDIA conceptual 2:4 representation showing a dense-shape sparse matrix transformed into retained values and compact two-bit indices

Source figure: NVIDIA, “Accelerating Inference with Sparsity Using the NVIDIA Ampere Architecture and TensorRT,” Figure 1, retrieved 2026-08-12. The figure anchors the conceptual values-plus-location split. It is not a byte-level dump of the current PyTorch or cuSPARSELt ABI, and its hardware-performance claims are not imported by this use.

0x04F A Teaching Pack Is Not A Hardware Pack

A backend-independent reference representation can retain values and logical positions:

def logical_pack_2of4(weight, selected_mask):
    validate_nm(weight, selected_mask, n_keep=2, group_size=4)

    groups = weight.reshape(weight.shape[0], -1, 4)
    masks = selected_mask.reshape(weight.shape[0], -1, 4)

    # Human-auditable representation, not cuSPARSELt/PyTorch physical bytes.
    values = groups[masks].reshape(weight.shape[0], -1, 2)
    positions = nonzero_positions_per_group(masks)  # [..., 2], each in [0, 3]
    return values, positions


def logical_decode_2of4(values, positions, original_shape):
    groups = zeros((*values.shape[:-1], 4), dtype=values.dtype)
    groups.scatter_(dim=-1, index=positions, src=values)
    return groups.reshape(original_shape)

This is suitable for parity tests and format explanation. It is not suitable as a production ABI:

  • two integer positions per group would spend more bits than the fixed 4-bit metadata budget;
  • a kernel may pack several group codes into one metadata word;
  • values can be interleaved or swizzled by tile and lane;
  • transpose and major-order choices alter physical traversal;
  • backend versions can change packing while preserving logical semantics.

The converter should keep both a reference decoder and a backend compressor. Parity is checked against the reference; execution uses only the packed object accepted by the target kernel.

0x04G PyTorch Exposes A Narrow Semi-Structured Tensor Contract

As of the current PyTorch 2.13 documentation snapshot, semi-structured sparse tensors are a prototype CUDA-only tensor subclass. The documented conversion requires a contiguous 2D CUDA tensor and currently lists:

Weight dtypeDocumented shape multiple for both dimensionsPatternLogical compression fraction
FP16642:4
BF16642:4
INT81282:4

This table is a versioned API snapshot, not a hardware law. PyTorch exposes CUTLASS- and cuSPARSELt-backed subclasses with backend-specific packed state. The stable documentation lists matrix multiplication, addmm/linear, and transpose-related operations; arbitrary dense operators are not implied.

The user-facing table and one backend implementation are also different evidence surfaces. The pinned PyTorch v2.13.0 source validates CUDA, rank, contiguity, dtype, and backend-specific minimum shapes before dispatching to either a CUTLASS or cuSPARSELt subclass. Its cuSPARSELt subclass stores retained values and metadata in one packed tensor, while its CUTLASS subclass keeps packed values and metadata separately. Those implementation details constrain that exact source revision; they must not be generalized into one timeless semi-structured ABI.

A minimal inference conversion should separate semantic validation, conversion, parity, and timing:

from torch.sparse import to_sparse_semi_structured

@torch.no_grad()
def prepare_sparse_linear(linear, selected_mask, example_inputs):
    W_dense_pruned = linear.weight.detach().contiguous().cuda()
    validate_nm(W_dense_pruned, selected_mask.cuda(), 2, 4)

    W_sparse = to_sparse_semi_structured(W_dense_pruned)

    for x in example_inputs:
        dense_y = torch.nn.functional.linear(x, W_dense_pruned, linear.bias)
        sparse_y = torch.nn.functional.linear(x, W_sparse, linear.bias)
        assert_close(sparse_y, dense_y, contract_for(x.dtype))

    return W_sparse

The comparison target is W_dense_pruned, not the original dense model. That isolates representation and kernel parity from pruning quality. Original-model quality is a separate evaluation.

Conversion must happen outside the steady-state request path. The returned subclass should also be tested through the exact graph/compiler path used in deployment: eager support does not prove that an export, compiler, fusion, or distributed wrapper preserves the sparse operator.

0x04H cuSPARSELt Makes Preparation And Execution Separate Phases

The cuSPARSELt documentation snapshot retrieved for this loop identifies version 0.9.1. Because the stable URL moves, the evidence bundle records the resolved bytes and SHA-256 rather than pretending the alias is immutable. The current workflow exposes the sparse runtime contract more directly:

initialize handle
    -> declare structured and dense matrix descriptors
    -> declare op(A), op(B), layouts, dtypes, compute, epilogue
    -> create algorithm selection and matmul plan
    -> prune or accept a custom-pruned matrix
    -> run the library's pruning-validity check
    -> query compressed size and compress the sparse operand
    -> query/allocate workspace
    -> optionally search/tune an algorithm
    -> repeatedly execute matmul with new dense inputs

The distinction between prune and compress is essential. Pruning writes zeros into a dense-shape tensor. Compression creates the physical operand read by sparse matrix-multiply kernels.

A deployment wrapper should preserve the plan inputs in provenance:

SparseMatmulSignature(
    library="cuSPARSELt",
    library_version=runtime_version(),
    device_capability=current_sm(),
    logical_shape=(M_out, N_out, K_reduce),
    sparse_operand="A",
    op_a="T",
    op_b="N",
    order_a="row",
    order_b="row",
    input_dtype="fp16",
    compute_dtype="fp32",
    alignment_bytes=16,
    epilogue="bias",
    algorithm_id=selected_algorithm,
    workspace_bytes=workspace_size,
)

Do not copy those illustrative fields as universal cuSPARSELt constraints. Current library versions support multiple layouts, dtypes, compute modes, and SM generations. The executable signature must come from the descriptors and plan actually created by the installed version.

Direct cuSPARSELt invocation differs from a graph builder: if plan creation, validation, compression, or execution fails, the call has not produced a sparse result. There is no justification for silently timing an unrelated dense call under the same label.

The pinned CUDALibrarySamples matmul_example.cpp makes the order concrete: descriptor and plan creation precede prune/check and compression; optional algorithm search precedes workspace allocation and repeated matmul. The Loop 4 oracle records that source trace but does not compile or execute it.

0x04I TensorRT Separates Eligibility From Tactic Selection

TensorRT creates a second decision boundary. Its current documentation states that matrix-multiply constant weights are sparse-eligible when every four-element vector on the reduction axis has at least two zeros. The builder must also enable sparse weights and a supported precision path.

Eligibility only adds sparse tactics to the candidate set. TensorRT profiles or otherwise selects tactics for the deployed shape, and a dense tactic may be faster. Verbose build logs distinguish:

eligible sparse layers:
    pattern and graph constraints passed

layers using sparse tactics:
    builder actually selected a sparse implementation

An audit should fail if it records only the first list:

def audit_tensorrt_sparse_build(build_log, expected_layers):
    eligible = parse_sparse_eligible_layers(build_log)
    selected = parse_sparse_selected_layers(build_log)

    assert expected_layers <= eligible
    missing_sparse_tactic = expected_layers - selected
    return {
        "eligible": sorted(eligible),
        "selected_sparse": sorted(selected),
        "dense_fallback": sorted(missing_sparse_tactic),
    }

Polygraphy can inspect whether ONNX weights satisfy the structural pattern. It cannot by itself prove which tactic a later engine build selects or whether that engine improves the target workload.

0x04J The Arithmetic Ceiling Is Not The Latency Result

For input rows, , and , a dense linear layer has the usual approximate arithmetic ledger

An idealized N:M sparse multiply performs useful value multiplications for only the selected fraction:

For 2:4, this is half the dense multiply-add count. Sparse Tensor Core throughput is often reported in effective dense operations, which credits the skipped zero work. That convention is useful for comparing hardware capability but must not be inserted as twice as many physical nonzero multiplications in a software FLOP ledger.

A sparse operator also reads:

  • compressed values and pattern metadata;
  • the dense activation operand;
  • output tiles and any bias/epilogue state;
  • descriptors or workspace amortized by the runtime;
  • possible padding and layout-conversion buffers.

A bounded model is

The compressed weight saving matters more when weight traffic is a meaningful share of the operation. Small matrices, skinny decode shapes, low occupancy, unsupported fusions, conversion in the hot path, and metadata handling can erase the arithmetic advantage.

At model level, let be the fraction of baseline latency spent in operators that are both eligible and actually dispatched sparsely. Let be their measured speedup and be new overhead normalized by baseline latency:

For , , and :

That teaching example explains why a nominal 2x sparse arithmetic capability can become a much smaller end-to-end improvement. It is not a benchmark claim.

0x04K Runtime Proof Needs A Parity Ladder And Workload Shapes

A valid experiment has at least four distinct objects:

  1. original dense model, used for quality comparison;
  2. N:M-pruned weights in ordinary dense storage;
  3. the same pruned values decoded from or executed through the compressed representation;
  4. the deployed graph and selected kernel/tactic.

Those objects induce a stricter evidence ladder:

StateMinimum evidenceLoop 4 synthetic fixture
local pattern validevery declared row/K group has exactly two selected slots and no value outside themproved
logical pack reversibleretained values plus logical pattern ranks decode to the same dense-pruned tensorproved
backend artifact createdofficial converter returns the target backend representation and recorded bytesnot executed
operator eligibleexact dtype, shape, orientation, device, graph, and version contract passesnot claimed
sparse tactic selectedplan, algorithm, or engine-build evidence names the sparse implementationnot claimed
sparse operator observedprofiler trace identifies that implementation in the deployed pathnot claimed
speedup measuredsynchronized paired workload evidence beats the same pruned values run denselynot claimed

This ordering is not ceremonial. Later states consume earlier artifacts but do not follow from them logically. In particular, TensorRT eligibility can be true while tactic selection is false, and tactic selection can be true while end-to-end speedup is false.

The first comparison measures pruning error. The second proves representation parity. The third proves integration. Timing only object 1 against object 3 confounds quality, arithmetic, layout, and potentially dtype.

@torch.no_grad()
def prove_sparse_linear(candidate, workload_cases):
    W0 = candidate.original_dense_weight
    Wp = candidate.pruned_dense_weight
    Ws = candidate.packed_sparse_weight

    validate_nm(Wp, candidate.selected_mask, 2, 4)
    assert packed_nbytes(Ws) == candidate.manifest.packed_nbytes

    records = []
    for case in workload_cases:
        x = case.make_input()  # e.g. prefill tokens or active decode rows

        y_original = linear(x, W0)
        y_pruned_dense = linear(x, Wp)
        y_sparse = linear(x, Ws)

        assert_close(y_sparse, y_pruned_dense, case.numeric_contract)

        dense_profile = profile_synced(lambda: linear(x, Wp), case)
        sparse_profile = profile_synced(lambda: linear(x, Ws), case)

        records.append({
            "case": case.name,
            "pruning_error": error(y_original, y_pruned_dense),
            "representation_error": error(y_pruned_dense, y_sparse),
            "dense_pruned_ms": dense_profile.latency_ms,
            "sparse_ms": sparse_profile.latency_ms,
            "selected_kernel": sparse_profile.kernel_names,
            "workspace_bytes": sparse_profile.workspace_bytes,
        })
    return records

profile_synced must include warmup, device synchronization or CUDA events, enough repetitions, dispersion statistics, and the same stream/graph/fusion conditions. Preparation, compression, engine build, and algorithm search should be reported separately from steady state.

Transformer workload cases must include the real linear shapes:

prefill:
    T = batch * prompt tokens after packing
    large activation matrix, potentially throughput-oriented

decode:
    T = active sequences scheduled in this step
    can be small or dynamic, often latency- and launch-sensitive

tensor parallel:
    local N_o and K after sharding
    collective overlap can hide or expose local GEMM changes

A speedup measured on one large square GEMM does not establish decode latency. Conversely, a sparse kernel that loses at tiny can still improve high-load prefill throughput. The result belongs to the workload tuple, not the word “2:4.”

0x04L Semi-Structured Sparsity Is One Point In A Format Design Space

ArtifactPattern freedomLocation stateTypical execution implication
dense tensor with zerosarbitrary values may be zeronone beyond dense positionsordinary dense GEMM still issues zero work
COO/CSR unstructuredarbitrary nonzeroscoordinates or row pointers plus indicesflexible, but metadata and irregular access are expensive at moderate density
block sparseselected dense blocksblock coordinatesregular work inside blocks; quality depends on block constraint
N:M semi-structuredfixed local nonzero budgetcompact fixed-pattern metadataspecialized sparse MMA path with strict dtype/shape/operator rules
smaller dense matrixwhole rows/columns physically removedchanged shapes/configurationordinary dense kernels on less work; owned by Loops 5-6

For FP16 at 50% density, generic CSR with 32-bit column indices already spends 2 bytes for each retained value and 4 bytes for its column index, before row pointers. That is about 3 bytes per original matrix position, larger than the 2-byte dense tensor. The 2:4 representation avoids that failure by replacing arbitrary coordinates with a tiny fixed-pattern code. Its compression comes from reduced mask entropy, not from zeros alone.

0x04M N:M And Sparse Runtime Failure Lab

FailureSilent causeObservable consequenceRepair
interpret prunen=1, prunem=4 as “keep one of four”API counts removed entries, article counts retained entriesexported density is 75% instead of 25%, or vice versaname n_keep and n_prune separately
apply one global 50% thresholdglobal density mistaken for local structuresome groups contain 3-4 nonzeros and fail validationproject independently inside every row/K group
group along output rowsstored matrix axes confused with logical reduction zero count looks right but kernel grouping is wrongbind groups to logical operator coordinates
transpose after pruning without updating descriptorphysical orientation changespacker interprets different groupsrecord transpose/order and validate decoded coordinates
split a K group across tensor-parallel shardsshard boundary is not divisible by local packers see incomplete groupsalign K offsets/lengths or change placement
allow unstable tiesequal scores select device-dependent positionsartifact hashes differ across runsdefine stable secondary ordering
mask gradients but keep old momentumoptimizer state remains nonzero in deleted slotszeros regrow after recoverymask parameter and same-shaped optimizer state
merge LoRA after N:M projectiondense delta writes into pruned locationslocal pattern becomes invalidmerge before final projection or reproject and reevaluate
serialize dense zeros and call them compressedno values/metadata conversion occurredcheckpoint size and dense GEMM remain unchangedrun target backend compressor and record packed bytes
hand-write a 3-bit pattern code for a 4-bit ABIinformation lower bound confused with hardware encodingdecoder/kernel reads wrong positionsuse official compressor and version its layout
time to_sparse_semi_structured inside forwardone-time conversion placed in hot pathsparse request is slowerconvert at load/build time and time separately
compare sparse weight against original dense weight onlypruning error mixed with representation errorkernel parity cannot be isolatedcompare sparse output with the same dense-pruned values
prove TensorRT eligibility onlycandidate set mistaken for chosen tacticengine may execute dense kernelsparse verbose selected-tactic evidence
benchmark only a large square GEMMdeployment uses skinny or dynamic Transformer shapespaper speedup does not reproducebenchmark prefill/decode and each local shard shape
report effective sparse TOPS as physical FLOPsskipped zero work counted as executed workcompute ledger is doubledstate throughput convention and selected-value work separately

0x04N Loop Boundary

Loop 4 has now followed the complete semi-structured path:

score or reconstruction state
    -> row-local top-N_keep projection on logical K groups

fixed pattern
    -> optional mask-preserving recovery
    -> revalidation after merge, sharding, transpose, and dtype conversion

logical artifact
    -> retained values + compact pattern metadata
    -> reference decode parity

physical artifact
    -> backend descriptors, compression, workspace, and algorithm/tactic

runtime proof
    -> same pruned values through dense and sparse paths
    -> selected sparse kernel evidence
    -> prefill/decode measurements and end-to-end attribution

This closes scalar sparse execution. It does not answer which attention heads or MLP channels can be removed, because those transformations change tensor shapes and dependency graphs rather than packing scalar positions. Loop 5 owns Q/K/V/O head-slice closure and gated-MLP channel surgery.

0x05 Structural Surgery Removes Coupled Head And MLP Slices

An N:M mask preserves every matrix shape. Head and MLP-channel pruning make a different promise: delete a complete computational term, remove every parameter slice that produces or consumes that term, and export smaller dense matrices. Ordinary dense kernels can then execute the reduced shapes without decoding sparse metadata. That apparent simplicity hides a dependency problem.

A head is not one row in one tensor. In classic MHA it occupies matching output blocks in Q, K, and V, then an input block in the output projection. A SwiGLU channel occupies one output row in both gate_proj and up_proj, then one input column in down_proj. Biases, per-channel quantization state, adapters, optimizer tensors, packed layouts, tensor-parallel shards, configuration, and cache metadata can carry the same axis.

The owner question for this loop is:

How does a low-importance gate become a dependency-closed tensor slice, a reloadable smaller checkpoint, and a measured dense execution path without accidentally changing residual width or GQA sharing semantics?

Loop 5 keeps the residual stream at width . It owns the internal attention width , the persistent KV width , and the MLP intermediate width . Removing residual channels would propagate through embeddings, normalization, every block, and the LM head; that global transformation remains the Loop 6 boundary.

The replayable evidence for this loop deliberately stops below a framework or model reproduction. It executes one deterministic row-major fixture with classic MHA, equal-group GQA, an irregular query-only GQA alternative, and SwiGLU. The oracle proves logical-to-stored index maps, gate-zero versus sliced forward parity, packed-layout maps, parameter and KV ledgers, target-TP ownership, and architecture-manifest round trips. It does not claim that a paper checkpoint was pruned, that a production loader accepted heterogeneous shapes, that a GPU kernel ran, or that latency or quality improved.

@dataclass(frozen=True)
class StructuralSurgeryManifest:
    residual_width: int
    head_dim: int
    original_q_heads: int
    original_kv_heads: int
    kept_q_heads_original: tuple[int, ...]
    kept_kv_heads_original: tuple[int, ...]
    q_to_kv_new: tuple[int, ...]
    attention_layout: str
    original_mlp_width: int
    kept_mlp_channels_original: tuple[int, ...]
    mlp_layout: str
    target_tp: int
    kv_placement: str

This manifest distinguishes original coordinates from compacted coordinates. The distinction matters whenever a surviving noncontiguous head owns ALiBi, QK normalization, cache state, quantization metadata, or an adapter slice.

0x05A A Gate Defines The Unit Before A Score Ranks It

For one attention layer, split the output projection into row-vector algebraic blocks and write

where is the value-mixed output of head . Insert one scalar gate per head:

Now has a precise counterfactual: the entire projected contribution of head is absent. With upstream gradient , the chain rule gives

Equivalently, if the gate is applied directly to , this is the inner product of with its own backpropagated gradient. Michel et al. estimate head importance as

The absolute value is inside the data expectation. Moving it outside would let positive and negative samples cancel. The paper computes the score with forward/backward passes, normalizes within each layer by the layer score norm, and uses iterative greedy removal. This is a post-training local sensitivity proxy, not proof that jointly pruning several low-score heads has the sum of their individual effects.

Voita et al. instead learn stochastic head gates with a differentiable relaxation of an penalty. In schematic form:

That objective lets task loss and sparsity pressure co-adapt. A binary exported gate still has to be converted into physical tensor slices; expected gate sparsity alone does not shrink a checkpoint or select a faster kernel.

The source-series figure below records the empirical observation that specialized positional, syntactic, and rare-token heads tended to survive longer in the studied translation encoders. It should not be read as a universal taxonomy for every decoder-only LLM.

A source-series reconstruction of Voita et al.'s head-pruning trajectory, with specialized positional, syntactic, and rare-token heads surviving as the encoder head budget shrinks

Source-local reconstruction from A12, based on Voita et al., Figure 8. The empirical result is architecture-, task-, and training-run-scoped.

The next source-series figure makes the training object explicit: a gate multiplies each head output and the optimization penalizes nonzero gates.

Head gates applied before concatenation and an L0 objective encouraging fewer active heads

Source-local reconstruction from A12, based on Voita et al. The blue annotation explains that each head receives a gate.

Four common selectors answer different questions:

SelectorSignalExtra workWhat it does not prove
direct ablationmeasured one or more evaluations per candidatejoint subset quality
Michel Taylor gate$\mathbb E\partial L/\partial g_h$
learned gatetask loss plus expected active-unit penaltysparsity-aware trainingexact exported budget without projection
grouped weight criterionaggregate magnitude/Taylor terms over all coupled slicesweights only or calibration backwardequivalence between different group sizes/costs

Scores are meaningful only after the unit is defined. Ranking one Q row, one head, one KV-sharing group, and one MLP channel in a single raw list silently compares objects with different parameter, FLOP, cache, and quality costs. A cost-aware target can instead solve a constrained selection problem:

where must name the target resource: parameters, bytes, projection MACs, prefill attention MACs, KV bytes, or measured latency. These budgets are not interchangeable.

0x05B A Zero Head Gate Has An Exact Smaller-Dense Counterpart

Use row-vector algebra for classic MHA:

Let be the kept head set and expand it into a kept channel index:

The structurally reduced matrices are

Under PyTorch nn.Linear, weights are stored as [out_features,in_features]. The same operation therefore becomes:

q_proj.weight: keep rows I
k_proj.weight: keep rows I
v_proj.weight: keep rows I
o_proj.weight: keep columns I

q/k/v bias:    keep entries I
o_proj bias:   unchanged, because residual output width d is unchanged

This is not merely shape bookkeeping. Partitioning by head proves

Therefore, in deterministic evaluation mode, the physically sliced module must match the original module with deleted head gates fixed to zero. That gate-zeroed dense path is the representation-parity oracle. Comparing only to the original unpruned module mixes intentional pruning error with export bugs.

The Q/K/V slices are needed to stop computing the deleted head. Slicing only the corresponding input columns of produces the same gated function but still computes the deleted Q/K/V projections, score matrix, softmax, and value mix. Conversely, deleting Q/K/V blocks without slicing changes the concat width and makes the consumer invalid.

For classic MHA, one full head without projection biases owns

weights. For and :

Q row block: 4096 * 128 =   524,288 weights
K row block: 4096 * 128 =   524,288 weights
V row block: 4096 * 128 =   524,288 weights
O col block: 4096 * 128 =   524,288 weights
total:                     2,097,152 weights = 4 MiB at FP16

For self-attention over token rows, the removed head’s projection work is MACs. Its two core attention products contribute approximately MACs in prefill: one and one probability-value product. The exact kernel ledger also includes softmax, masking, RoPE, reads/writes, launch, and fusion boundaries.

In classic MHA the persistent KV payload for one removed head is

bytes per layer and request, where is bytes per cached element. With and FP16, that is 512 bytes per cached token per layer. This cache saving does not carry over to query-only pruning in GQA or MQA.

The LLM-Pruner paper frames the same problem as discovery of coupled groups. Its Figure 2 separates MLP intermediate groups, attention-head groups, and a global residual-channel group. Loop 5 owns the first two; Loop 6 owns the globally propagating channel-wise group on the right.

LLM-Pruner Figure 2 showing coupled MLP, attention-head, and globally propagating channel groups in LLaMA

Source: Ma, Fang, and Wang, LLM-Pruner, Figure 2. Cropped from the official paper for legibility.

0x05C Slice Modules By Stored Axis, Then Prove The Function

A reference implementation should name output and input slicing separately. It should also preserve device, dtype, bias policy, and requires_grad rather than constructing default CPU/FP32 modules by accident:

def copy_linear_out(linear, keep_out):
    keep_out = as_index(keep_out, device=linear.weight.device)
    new = nn.Linear(
        in_features=linear.in_features,
        out_features=len(keep_out),
        bias=linear.bias is not None,
        device=linear.weight.device,
        dtype=linear.weight.dtype,
    )
    with torch.no_grad():
        new.weight.copy_(linear.weight.index_select(0, keep_out))
        if linear.bias is not None:
            new.bias.copy_(linear.bias.index_select(0, keep_out))
    new.weight.requires_grad_(linear.weight.requires_grad)
    if new.bias is not None:
        new.bias.requires_grad_(linear.bias.requires_grad)
    return new


def copy_linear_in(linear, keep_in):
    keep_in = as_index(keep_in, device=linear.weight.device)
    new = nn.Linear(
        in_features=len(keep_in),
        out_features=linear.out_features,
        bias=linear.bias is not None,
        device=linear.weight.device,
        dtype=linear.weight.dtype,
    )
    with torch.no_grad():
        new.weight.copy_(linear.weight.index_select(1, keep_in))
        if linear.bias is not None:
            new.bias.copy_(linear.bias)  # output axis was not reduced
    new.weight.requires_grad_(linear.weight.requires_grad)
    if new.bias is not None:
        new.bias.requires_grad_(linear.bias.requires_grad)
    return new

Classic separate-projection MHA surgery is then explicit:

def expand_head_indices(keep_heads, head_dim):
    return torch.tensor([
        h * head_dim + offset
        for h in keep_heads
        for offset in range(head_dim)
    ])


def prune_classic_mha_heads(attn, keep_heads):
    assert is_classic_mha(attn)
    keep = expand_head_indices(keep_heads, attn.head_dim)

    attn.q_proj = copy_linear_out(attn.q_proj, keep)
    attn.k_proj = copy_linear_out(attn.k_proj, keep)
    attn.v_proj = copy_linear_out(attn.v_proj, keep)
    attn.o_proj = copy_linear_in(attn.o_proj, keep)

    attn.num_heads = len(keep_heads)
    attn.num_key_value_heads = len(keep_heads)
    attn.inner_dim = len(keep_heads) * attn.head_dim
    return attn

This mirrors the dependency direction in the historical Hugging Face BERT prune_heads implementation: Q/K/V are sliced on linear output, while the attention output dense layer is sliced on linear input. That implementation is useful evidence for the tensor operation, not a universal modern API contract; generic head pruning was removed from the Transformers v5 surface.

At the pinned Transformers v4.0.1 commit, BertAttention.prune_heads first maps original head ids around already_pruned_heads, then applies dim=0 to Q/K/V and dim=1 to the output dense layer before updating both num_attention_heads and all_head_size. The helper constructs new linear modules, copies the selected weights and the axis-appropriate bias, and does not preserve an optimizer object. Those details support this slice direction; they do not establish a GQA, packed-QKV, TP, cache, or exact-resume contract.

Before mutating the model, capture the gate-zeroed oracle. After surgery, test the same input, mask, positions, dtype, and evaluation mode:

with torch.no_grad():
    y_masked = dense_attn(x, head_gate=keep_mask, use_cache=False)
    y_small = sliced_attn(x, use_cache=False)

torch.testing.assert_close(y_small, y_masked, rtol=rtol, atol=atol)

Then repeat with prefill and one-token decode cache paths. A no-cache parity test does not validate KV head count, cache append shape, cache reorder, or position-dependent kernels.

0x05D GQA Makes Query Heads And KV Groups Different Pruning Units

Grouped-query attention has

under the common equal contiguous-group mapping. Every group of query heads reads one shared K/V head. MQA is the limiting case .

Removing one query head requires only:

q_proj: remove one output head block
o_proj: remove the matching input head block
k_proj, v_proj: unchanged
KV cache: unchanged

The parameter saving is , half of a classic MHA head, and there is no persistent KV-cache saving because the shared K/V head remains. The resulting query-to-KV map may become irregular: after deleting one member, an equal group no longer contains exactly contiguous query heads. A runtime that derives the map from integer division cannot represent that arbitrary subset merely by changing num_attention_heads.

There are three different export choices:

  1. Prune a complete GQA group. Remove Q blocks, their O input blocks, and the one shared K and V block. Equal grouping survives.
  2. Prune query heads only. Preserve K/V and store an explicit surviving query-to-KV map. This needs a model and kernel that accept that map.
  3. Repartition or merge. Change which queries share each KV head, then recover quality. This is an architecture conversion, not simple deletion.

A complete equal GQA group owns

weights across Q, K/V, and O. With , , and , this is 5,242,880 weights, or 10 MiB at FP16. It also removes one K/V head from the cache. In MQA, the sole shared K/V head cannot be removed while any query head still needs attention, so query-head pruning saves projection and attention work but not cache width.

def prune_equal_gqa_groups(attn, keep_kv_groups):
    group = attn.num_heads // attn.num_key_value_heads
    assert attn.num_heads % attn.num_key_value_heads == 0

    keep_q_heads = [
        kv * group + local
        for kv in keep_kv_groups
        for local in range(group)
    ]
    q_idx = expand_head_indices(keep_q_heads, attn.head_dim)
    kv_idx = expand_head_indices(keep_kv_groups, attn.head_dim)

    attn.q_proj = copy_linear_out(attn.q_proj, q_idx)
    attn.k_proj = copy_linear_out(attn.k_proj, kv_idx)
    attn.v_proj = copy_linear_out(attn.v_proj, kv_idx)
    attn.o_proj = copy_linear_in(attn.o_proj, q_idx)

    attn.num_heads = len(keep_q_heads)
    attn.num_key_value_heads = len(keep_kv_groups)
    assert attn.num_heads // attn.num_key_value_heads == group
    return attn

Full-head deletion keeps constant, so the within-head Q/K dot-product and RoPE pair layout remain unchanged. Reducing is a different operation: it slices coordinates inside every surviving head, changes RoPE dimensions and possibly scaling, alters attention kernel shapes, and must coordinate Q, K, V, and O at sub-head granularity. Loop 5 records it as an alternative, not as head pruning.

0x05E Packed Projections And Tensor Parallelism Change The Index Map

The equations above assume separate Q, K, V parameters whose output rows are head-major. Production checkpoints may store any of these layouts:

contiguous packed MHA:
    [Q_all_heads | K_all_heads | V_all_heads]

head-interleaved MHA:
    [Q_0 | K_0 | V_0 | Q_1 | K_1 | V_1 | ...]

group-packed GQA:
    [Q_group_0 | K_0 | V_0 | Q_group_1 | K_1 | V_1 | ...]

separate projections:
    q_proj, k_proj, v_proj

The same logical head set maps to different physical rows in each format. A converter must read the model implementation or an explicit layout descriptor; inferring pack order from a tensor shape is impossible because several layouts have the same number of elements.

@dataclass(frozen=True)
class AttentionLayout:
    kind: str                  # separate, qkv_contiguous, head_interleaved, ...
    num_q_heads: int
    num_kv_heads: int
    head_dim: int
    q_to_kv: tuple[int, ...]
    tensor_parallel_size: int


def physical_rows_for_heads(layout, keep_q, keep_kv):
    if layout.kind == "separate":
        return {
            "q": expand_head_indices(keep_q, layout.head_dim),
            "k": expand_head_indices(keep_kv, layout.head_dim),
            "v": expand_head_indices(keep_kv, layout.head_dim),
        }
    if layout.kind == "qkv_contiguous":
        return contiguous_qkv_row_map(layout, keep_q, keep_kv)
    if layout.kind == "group_interleaved":
        return grouped_qkv_row_map(layout, keep_q, keep_kv)
    raise UnsupportedLayout(layout.kind)

Per-head QK normalization, head-specific scaling, ALiBi slopes, relative-bias tables, attention sinks, or other head-axis parameters must follow the same kept-head map. Full-head pruning leaves RMSNorm/LayerNorm on the residual stream unchanged, but a head-local norm is not residual normalization and cannot be ignored.

Tensor parallelism adds ownership constraints. A common placement uses column-parallel Q/K/V projections, whose output channels are partitioned, and a row-parallel O projection, whose input channels use the matching partition. Gated MLPs similarly place gate/up output channels and down input channels on the same rank. The structural keep set must therefore be converted from global indices to each rank’s local indices on all coupled tensors.

The safest export order is:

unsharded logical checkpoint
    -> choose dependency-closed units
    -> slice full tensors
    -> update architecture manifest
    -> repartition the new shapes for target TP degree

Slicing already-sharded tensors is valid only when the converter knows global offsets and can rebalance ownership. Otherwise one rank may retain more heads or MLP channels than another, collective split sizes can disagree, and a locally valid checkpoint can deadlock or fail at all-reduce/all-gather.

Runtime-friendly targets often round kept counts so local dimensions remain aligned:

where is a kernel alignment measured in local channels. These are target runtime constraints, not universal mathematical requirements. LLM-Pruner’s official Llama-3 path, for example, groups K projection output channels in consecutive blocks of head_dim, prunes with dependency groups, then derives each layer’s num_heads and num_key_value_heads from the resulting projection shapes. The code and README also expose the practical consequence of heterogeneous per-layer widths: a conventional scalar config and ordinary from_pretrained() path may no longer reconstruct the model.

That statement is version-specific. At the pinned LLM-Pruner commit, the earlier hf_prune.py block-wise path roots attention groups at q_proj, groups consecutive head_dim rows, and refreshes only num_heads. The later llama3.py path roots them at k_proj and refreshes both query- and KV-head counts from projection shapes. The shared dependency engine propagates root indices through graph mappings and rejects groups that would remove every channel. Neither path is promoted here as a universal irregular-GQA exporter; the fixture therefore treats complete equal GQA groups and query-only irregular maps as different artifact contracts.

0x05F One SwiGLU Channel Is A Three-Slice Computational Term

For a gated LLaMA-style MLP with row-vector input :

with

Write the two input-projection columns as and and row of as . The MLP is a sum of intermediate-channel terms:

Insert a gate on the product:

This expansion proves the dependency group. If channel is deleted, its gate-projection column, up-projection column, and down-projection row must all disappear in algebraic notation. In stored PyTorch nn.Linear weights, that is:

gate_proj.weight [d_ff,d]: keep output rows J
up_proj.weight   [d_ff,d]: keep output rows J
down_proj.weight [d,d_ff]: keep input columns J

gate/up bias [d_ff]:       keep entries J, if present
down bias [d]:             unchanged

The gate-gradient analogue is

where . A grouped Taylor score can estimate this directly with a channel gate or aggregate weight-gradient terms over all three coupled slices. Those estimators are not necessarily identical: placing one shared gate after the elementwise product measures the signed joint contribution of the complete channel, while summing absolute scalar terms discards cancellation within the group.

A classic two-matrix GELU/ReLU FFN owns only the input-projection output row and the output-projection input column. Its per-channel parameter saving is . SwiGLU owns three slices and saves

weights per removed intermediate channel, excluding optional bias. With , one channel is 12,288 weights, or 24 KiB at FP16. Removing 1,024 channels from a MLP produces and removes 12,582,912 weights, exactly 24 MiB of FP16 parameter payload. It also removes projection MACs for token rows, before kernel overhead.

The width 9,984 may be mathematically valid yet poorly aligned for a target kernel or tensor-parallel degree. A deployment target may instead remove a nearby number of channels in aligned groups. That changes the realized budget and must be reflected in the quality evaluation rather than reported as the unrounded score target.

0x05G MLP Surgery Uses One Keep Set Across Every Branch

def prune_swiglu_channels(mlp, keep_channels):
    keep = as_index(keep_channels, device=mlp.gate_proj.weight.device)

    assert mlp.gate_proj.out_features == mlp.up_proj.out_features
    assert mlp.gate_proj.out_features == mlp.down_proj.in_features

    mlp.gate_proj = copy_linear_out(mlp.gate_proj, keep)
    mlp.up_proj = copy_linear_out(mlp.up_proj, keep)
    mlp.down_proj = copy_linear_in(mlp.down_proj, keep)
    mlp.intermediate_size = len(keep)
    return mlp

For a packed gate_up_proj with stored output shape [2*d_ff,d], the keep set must be translated through pack order. Under a simple contiguous [gate_all | up_all] layout:

packed_keep = torch.cat([keep, keep + old_intermediate_size])
mlp.gate_up_proj = copy_linear_out(mlp.gate_up_proj, packed_keep)
mlp.down_proj = copy_linear_in(mlp.down_proj, keep)

An interleaved [gate_0,up_0,gate_1,up_1,...] layout needs a different map. As with packed QKV, shape alone does not reveal the order.

The dependency closure also extends into training and compression state:

State attached to the channel axisRequired action
Adam moments or master weightsslice with the same stored axis if training continues
LoRA on gate/up outputslice LoRA output rows; input side is unchanged
LoRA on down inputslice LoRA input columns; output side is unchanged
per-output quantization scale on gate/upslice scale entries or requantize
per-input/group quantization state on downregenerate groups after slicing
tensor-parallel checkpoint shardsrepartition global keep set and rewrite shard metadata

Merging adapters before surgery followed by slicing the merged dense weights is usually easier to audit. If runtime adapter switching must survive, every adapter needs the same architecture manifest and compatible sliced shapes.

A dependency-closed structural pruning trace showing attention-head gates mapped to Q K V output rows and O input columns, the GQA query-versus-KV-group fork, SwiGLU gate and up rows coupled to down columns, architecture manifest migration, parity checks, and measured smaller-dense execution

0x05H Selection, Surgery, And Recovery Are Different Phases

A robust structural-pruning pipeline separates at least five states:

1. dense trainable model
2. model with explicit head/channel gates for scoring or sparsity training
3. frozen discrete keep sets under an exact architecture/resource budget
4. physically sliced smaller-dense model
5. recovered and deployment-converted model

Collapsing them creates reproducibility gaps. A paper may report gate sparsity from state 2, parameter count from state 4, and quality after state 5. The article or release manifest must say which state produced each number.

Three practical schedules have different tradeoffs:

Post-training score, then surgery

calibration forward/backward
    -> fixed Taylor/magnitude/ablation scores
    -> exact keep-set projection
    -> physical slice
    -> optional recovery

This is inexpensive and close to Michel-style sensitivity pruning, but scores are local to the dense checkpoint. Large one-shot removals can invalidate the ranking because surviving units have not adapted.

Iterative prune and recover

score -> remove a bounded fraction -> short recovery
      -> rescore the changed model -> repeat

This tracks interactions better but increases training and evaluation cost. The ranking, optimizer, calibration data, random seed, and per-step budget all become part of the experiment contract.

Joint stochastic gates

train weights and relaxed gates under task + resource penalty
    -> anneal or harden gates
    -> exact budget projection
    -> physical slice
    -> recovery / final validation

This lets the model redistribute function while sparsity develops, as in Voita-style gates. A penalty coefficient does not guarantee a requested integer head count or aligned d_ff; a final constrained projection remains necessary.

LLM-Pruner instantiates a related discovery-estimation-recovery pipeline. It discovers dependent groups, estimates group importance using magnitude or Taylor-style terms on limited public samples, removes groups, then applies LoRA recovery. Its official implementation exposes block-wise attention/MLP, channel-wise residual-width, and layer-wise modes. Its published quality, timing, and data-volume results are source-reported examples, not defaults for other models or pruning ratios.

Recovery can use full fine-tuning, LoRA, distillation, or a combination:

The student is already structurally smaller. Hidden-state losses are easy at the residual boundary because Loop 5 keeps width unchanged; matching the deleted intermediate head/channel tensors themselves is neither possible nor necessary. M6.10 owns the full distillation target and divergence design.

After the keep set is frozen, removed units should not reappear. Gate training can retain relaxed gate parameters, but the final sliced model has no deleted slots to regrow. If surgery occurs mid-optimization, recreate or explicitly slice optimizer state; attaching an old dense optimizer to new parameter objects is invalid.

0x05I The Architecture Manifest Is Part Of The Artifact

A state dictionary alone does not fully describe a pruned model. The loader must know how each changed tensor maps to logical heads and channels:

{
  "layer": 12,
  "attention": {
    "layout": "separate_q_k_v",
    "head_dim": 128,
    "kept_query_heads_original": [0, 1, 4, 5, 8, 9, 12, 13],
    "kept_kv_heads_original": [0, 1],
    "q_to_kv_new": [0, 0, 0, 0, 1, 1, 1, 1],
    "num_attention_heads": 8,
    "num_key_value_heads": 2
  },
  "mlp": {
    "layout": "separate_gate_up_down",
    "intermediate_size": 9984,
    "kept_channel_hash": "sha256:..."
  }
}

Uniform pruning can update ordinary scalar configuration fields when every layer shares the same new num_attention_heads, num_key_value_heads, and intermediate_size. Heterogeneous pruning needs per-layer metadata or custom module construction. Saving a Python module object, as one reference LLM-Pruner path does for heterogeneous shapes, can preserve structure for one environment but is not a portable substitute for a versioned architecture and state-dict format.

Migration must cover:

model config and per-layer overrides
state-dict tensor shapes and safetensors shard index
packed QKV / gate-up order
original-to-new head and channel maps
RoPE and head-local parameter shapes
KV-cache head count and query-to-KV map
tensor-parallel ownership and collective split sizes
quantization scales, groups, and excluded modules
adapter shapes and adapter registry compatibility
optimizer/scheduler state if training resumes
artifact schema version and source checkpoint hash

A clean release test constructs the pruned architecture from the manifest, loads the state dict with no missing or unexpected parameters, saves it again, reloads in a fresh process, and reproduces logits and cache shapes. Passing an in-memory forward before serialization is insufficient.

The Loop 5 fixture makes the bookkeeping concrete without borrowing a paper result:

Fixture branchOriginal geometryKept original unitsExact structural consequence
classic MHA, , heads 96 to 64 weights; 12 to 8 cached K/V scalars per token
equal GQA, , , KV group and query heads 96 to 48 weights; 8 to 4 cached K/V scalars per token
query-only GQAsame sourcequery heads , both KV heads16 weights removed; K/V cache unchanged; equal integer grouping no longer represents the map
SwiGLU, channels 72 to 48 weights through one shared three-slice keep set

For every branch, the oracle compares the physically reduced computation to the source computation with exactly the deleted terms gated to zero. It also maps the same keep sets through contiguous and interleaved QKV/gate-up layouts. Those equalities prove conversion semantics only; they do not prove that the removed terms were a good quality choice.

0x05J Prove Four Different Things Before Claiming Speed

Structural pruning needs a layered validation ladder.

1. Dependency and shape proof

assert q_proj.out_features == num_q_heads * head_dim
assert k_proj.out_features == num_kv_heads * head_dim
assert v_proj.out_features == num_kv_heads * head_dim
assert o_proj.in_features == num_q_heads * value_head_dim

assert gate_proj.out_features == intermediate_size
assert up_proj.out_features == intermediate_size
assert down_proj.in_features == intermediate_size

Also validate biases, per-head parameters, pack order, TP divisibility, and the query-to-KV map.

2. Representation parity

Compare the sliced model against the same dense model with deleted head/channel gates fixed to zero. Run block-level and full-model checks, then prefill and incremental decode with cache. This isolates conversion correctness from intentional quality loss.

3. Quality and recovery proof

Compare the original model, gate-zeroed model, freshly sliced model, and recovered model on calibration-held-out loss and task suites. Keep source domains separate from evaluation domains. A low calibration loss increase can hide degraded rare capabilities or long-context behavior.

4. Runtime proof

Record actual parameter bytes, local GEMM shapes, kernel names, workspace, prefill latency, decode latency, throughput, KV bytes, and end-to-end serving metrics. Smaller dense dimensions remove arithmetic, but latency can fail to improve when:

  • dimensions lose favorable tile alignment;
  • per-layer heterogeneous shapes prevent graph reuse or fusion;
  • the attention kernel pads to a supported head count;
  • TP imbalance or extra collectives replace local work;
  • decode remains dominated by unrelated weight or KV traffic;
  • launch, sampling, communication, or non-pruned layers dominate.

For MHA with kept fraction , the idealized projection and core attention arithmetic scale approximately with when remains fixed. For GQA query-only pruning, Q/O and query-head attention work scale with , while K/V projection and KV cache do not. For SwiGLU with kept fraction , its three large projection matrices scale with . End-to-end speed still follows the measured eligible fraction:

where is baseline time in successfully reduced operators and includes conversion-induced overhead. Parameter reduction percentage is not a valid substitute for .

0x05K The Main Variants Differ In Selection And Export, Not Just Score Name

Method or systemUnitSelection/trainingPhysical export lessonEvidence boundary
Michel et al.individual attention headpost-training expected absolute gate gradient; iterative pruningpaper studies head removal and speed potential; converter details are architecture-specificNMT and BERT experiments in the paper
Voita et al.attention headstochastic gates with differentiable pressurelearned zero gates still require structural export for smaller dense tensorsNMT models and identified head roles
historical HF BERT pruningBERT headcaller supplies head indicesQ/K/V output rows plus output-dense input columns; updates local head metadataversioned BERT implementation, not current universal API
DepGraphgraph-derived coupled groupdependency discovery plus chosen importance rulestructural coupling can be discovered through graph dependenciesevaluated architectures in the paper
LLM-PrunerLLM MLP/head groups, residual channel groups, or layersdependency discovery, grouped magnitude/Taylor estimation, optional LoRA recoveryheterogeneity and view/concat index mapping complicate portable loadingsource-reported LLaMA/Vicuna/ChatGLM and later code support
this article’s exporterexplicit MHA/GQA/SwiGLU unitselector is pluggable; keep set is projected under runtime constraintsgate-zero parity, versioned manifest, fresh-process reload, and runtime proof are mandatoryrewrite audit framework

Head merging, MHA-to-GQA conversion, and low-rank factorization can reduce similar matrices but are not deletion. Merging changes the surviving function and usually needs alignment or retraining. N:M preserves matrix shapes. Residual-width slicing propagates globally. Layer pruning removes whole ordered blocks. Keeping these artifacts separate makes both quality and runtime claims auditable.

0x05L Structural Surgery Failure Lab

FailureSilent causeObservable consequenceRepair
zero one head gate and report a smaller modelfunctional ablation mistaken for physical exportunchanged checkpoint and dense kernelsslice every producer/consumer axis and recount bytes
slice Q/K/V but not Oconcat consumer retains old input widthshape error or wrong channel interpretationslice matching O input columns
slice only O input columnsdeleted head is hidden after computelittle or no attention-core savingalso remove Q/K/V blocks when execution should shrink
prune one GQA query head and decrement KV headsquery and shared-KV units conflatedwrong cache shape and group mapkeep KV or prune a complete sharing group
preserve counts but not original head mapsurviving noncontiguous heads renumbered silentlywrong per-head bias, scale, or cache assignmentserialize original-to-new maps
infer packed QKV order from shapeseveral layouts have equal shapeQ rows can become K/V rowsuse implementation-specific layout descriptor
prune gate rows but not up rowsSwiGLU branches disagreeelementwise product shape error or semantic mismatchone keep set for gate, up, and down
prune both axes of down_projintermediate width confused with residual widthdownstream residual addition failsLoop 5 changes down input only
leave old optimizer moments attachedparameter objects and state shapes changedload error or corrupt recoveryrecreate or explicitly migrate state
apply dense LoRA after slicingadapter dimensions reference deleted axesmerge/load failuremerge before surgery or slice every adapter consistently
update global config for heterogeneous layersscalar field cannot describe per-layer shapesfresh load constructs wrong modulesuse per-layer manifest/custom architecture or uniform target
keep arbitrary head/channel countsmathematical shape accepted but target kernel/TP is notpadding, fallback, imbalance, or compile failureproject budget onto runtime-supported aligned counts
compare sliced output only to original densepruning damage and conversion error mixedparity bug can hide inside expected quality losscompare first to gate-zeroed dense oracle
validate prefill without cached decodecache metadata and append path untestedgeneration fails after first tokenrun multi-step decode and cache reorder tests
count removed weights as latency reductionremoved operators are not baseline bottlenecknegligible end-to-end gainprofile eligible time and measure target workload
cite one paper’s removable-head ratio as a ruleredundancy is checkpoint/task dependentaggressive quality regressionrescore and evaluate the actual model and domains

0x05M Loop Boundary

Loop 5 has now followed complete internal-width surgery:

define unit with an explicit gate
    -> estimate or learn importance under declared data and reduction order
    -> project onto exact quality/resource/runtime constraints

dependency closure
    -> MHA: Q/K/V output blocks + O input block
    -> GQA: distinguish query-head and shared-KV-group units
    -> SwiGLU: gate/up output rows + down input columns

physical export
    -> map logical units through packed layout and TP ownership
    -> migrate biases, head-local state, quantization, adapters, and config
    -> serialize a versioned per-layer architecture manifest

proof
    -> gate-zeroed dense parity
    -> fresh-process save/load parity
    -> prefill and cached-decode quality
    -> actual smaller-dense kernels and workload latency

The residual stream has remained width throughout. Loop 6 starts where that assumption is removed. It owns globally coupled residual-channel deletion, SliceGPT’s computational-invariance framing, embedding/norm/LM-head closure, and the difference between local intermediate-width surgery and whole-model dense matrix slicing.

The article-local Loop 5 bundle freezes the exact paper and implementation loci, the synthetic surgery manifest, all formula and visual identities, and one-field failure cases. Its claim ceiling remains below framework execution, paper reproduction, GPU/runtime measurement, recovery, deployment, and final publication closure.

0x06 Residual-Width Slicing Changes Every Block Boundary

Loop 5 removed complete terms inside attention and MLP branches while the residual stream kept width . Residual-width pruning removes coordinates from the state that joins those branches. The dependency group is therefore no longer local to one module. It starts at every embedding that creates residual state, crosses every shortcut and normalization boundary, touches the input and output side of every residual branch, and ends at every consumer of the final state.

The owner question is:

How can a pretrained Transformer compute in a lower-dimensional residual space when its useful directions are not aligned with the checkpoint’s coordinate axes, and how do we separate an exact change of basis from the lossy deletion that follows it?

The replayable evidence for this loop uses one deterministic row-major D=4 fixture. It proves full-width rotation parity, physically sliced forward parity against an independently lifted projected-coordinate reference, non-parity against the unsliced model after truncation, original-dimension RMS normalization, global tensor-axis closure, rectangular shortcut shapes, endpoint-dependent parameter ledgers, unchanged K/V payload, and slicing- manifest round trips. It does not run the SliceGPT framework, prune a paper checkpoint, reproduce paper quality, execute a GPU kernel, measure speed, run recovery, or validate deployment.

@dataclass(frozen=True)
class ResidualWidthSlicingManifest:
    source_checkpoint_sha256: str
    model_family: str
    original_hidden_size: int
    norm_reference_dimension: int
    boundary_bases: tuple[str, ...]
    boundary_dimensions: tuple[int, ...]
    attention_input_dimensions: tuple[int, ...]
    attention_output_dimensions: tuple[int, ...]
    mlp_input_dimensions: tuple[int, ...]
    mlp_output_dimensions: tuple[int, ...]
    shortcut_shapes: tuple[tuple[int, int], ...]
    do_slice_head: bool
    head_dimension: int
    endpoint_policy: str
    calibration_manifest_sha256: str
    state_dict_sha256: str

The bases are executable coordinate state, not interchangeable metadata. The widths determine stored tensor shapes; norm_reference_dimension preserves the normalization denominator; and do_slice_head determines whether the final boundary remains narrow or expands back to the original LM-head width.

SliceGPT provides the main worked system for this loop. It rotates each block-boundary signal into a data-derived basis, keeps a leading subspace, and replaces affected matrices with smaller dense matrices. Its paper calls this post-training sparsification, but its executable artifact is not a zero mask or a CSR/N:M tensor. It is a custom Transformer whose residual dimensions, shortcut operators, and dense matrix shapes have changed.

The paper’s first figure makes that artifact distinction visible. Unstructured and 2:4 pruning retain a dense activation width and encode zeros in the weight. Slicing removes a complete activation coordinate and the corresponding matrix axis after a rotation.

SliceGPT Figure 1 comparing unstructured sparsity, 2:4 structured sparsity, and dense row-column slicing after rotation

Source: Ashkboos et al., SliceGPT, Figure 1. Cropped from the official ICLR 2024 paper. The hatched regions are deleted, not stored as sparse zeros.

0x06A A Residual Coordinate Is A Whole-Graph Unit

Consider a LLaMA-style sequential decoder block:

The additions require both operands at each boundary to use the same shape and the same coordinate interpretation. Deleting coordinate from therefore propagates through every producer and consumer of that state.

For one uniform direct-coordinate keep set , dependency closure includes:

token / learned-position / type embeddings:
    keep residual output columns J

attention branch:
    Q/K/V projections keep residual input columns J
    O projection keeps residual output rows J

gated MLP branch:
    gate/up projections keep residual input columns J
    down projection keeps residual output rows J

normalization and residual-local state:
    transform or slice learned affine state consistently
    keep both operands of every residual add in the same basis

output boundary:
    final normalization follows the new state contract
    LM head keeps residual input columns J, unless an explicit final bridge
    expands into a different head space

In PyTorch storage, input axes are linear-weight columns and output axes are linear-weight rows. This is the opposite visual direction for some matrices from row-vector algebra. A valid exporter names both conventions.

This global coupling explains why the channel-wise group on the right side of the LLM-Pruner figure in 0x05B reaches from embedding to LM head. A single ordinary mask must be shared across all residual boundaries because identity shortcuts cannot add two different coordinate systems. SliceGPT relaxes that single-mask restriction by inserting explicit basis-change operators into the shortcuts.

Local MLP-channel pruning and residual-width pruning are therefore different:

TransformationWidth changedCoupled axesAttention/KV consequence
Loop 5 SwiGLU channel deletiongate/up output and down inputnone
Loop 5 head deletion and optionally Q/K/V output and O inputmay reduce KV only when a K/V unit is removed
Loop 6 residual slicingboundary state embeddings, branch inputs/outputs, shortcuts, norms, head boundarydoes not by itself change , , or

0x06B Orthogonal Rotation Is Exact Before Any Coordinate Is Deleted

First remove the learned affine scale from the notation and define a bare row-wise RMS normalization with fixed reference dimension :

Let be orthogonal:

Orthogonality preserves the row norm:

The denominator is therefore unchanged and

or equivalently

This commutation relation is the computational invariance. It is exact up to floating-point evaluation order. It does not say that deleting coordinates is exact.

For row-vector algebra, a branch with input linear and output linear can compute in the rotated residual basis by using

The internal branch preactivation is unchanged:

while the branch output is expressed in the new residual basis:

The residual sum then obeys

The same basis must reach the boundaries:

in row-vector algebra, where and . Then and the logits remain unchanged.

Biases follow the axis they inhabit. An output bias in residual coordinates rotates with the output; an internal input-linear bias is unchanged because the internal preactivation is unchanged. In PyTorch storage, the official code implements the same algebra as:

# nn.Linear stores [out_features, in_features]
input_linear.weight  = input_linear.weight @ Q
output_linear.weight = Q.T @ output_linear.weight
output_linear.bias   = Q.T @ output_linear.bias

embedding.weight     = embedding.weight @ Q
lm_head.weight       = lm_head.weight @ Q

The last line is consistent because lm_head.weight is stored as and is transposed by the linear operation at runtime.

0x06C Learned Normalization State Does Not Commute For Free

A standard LLaMA RMSNorm is not only . It applies a learned elementwise scale :

For a general dense ,

Leaving the learned scale in place and rotating only neighboring weights would therefore break the claimed invariance. SliceGPT folds each norm’s scale into the subsequent input linears, clones the LM-head weight when it had shared storage with the embedding, and replaces affine normalization modules with an unparameterized RMSN.

LayerNorm adds mean subtraction and often an affine shift. Define

Then subtracts each row mean. The paper rewrites LayerNorm-connected models into an RMSNorm-connected form by absorbing:

LayerNorm scale gamma:
    into the following branch input matrices

mean-subtraction matrix M:
    into preceding branch output matrices and embeddings

final normalization scale:
    into the LM head

LayerNorm bias / downstream affine terms:
    into compatible adjacent biases where the architecture permits

This is an architecture conversion with an exact pre-slice parity obligation, not permission to replace arbitrary LayerNorms with RMSNorms. Post-LN ordering, parallel branches, fused biases, tied weights, and model-specific norm implementations all affect the legal fusion. The official repository requires a ModelAdapter and LayerAdapter for each supported family rather than assuming one universal rewrite.

There is another subtle implementation contract. The official replacement module stores the original hidden size as mean_dim and computes

variance = x.pow(2).sum(-1, keepdim=True) / original_hidden_size

even after has only coordinates. Replacing it with an ordinary RMSNorm(d'), which divides by , changes the scale by approximately when epsilon is negligible. The normalization denominator is part of the sliced artifact.

0x06D Different Boundary Bases Turn Identity Shortcuts Into Matrices

One global orthogonal can rotate the entire residual stream without adding runtime operations: every identity shortcut still maps to . But the activation covariance can have a different principal basis at each boundary. SliceGPT therefore uses before a residual branch and after it.

The branch output can be rotated into , but the residual arrives in basis . Before addition it must be mapped by

because

After slicing, let and select retained coordinates in the two rotated bases. The executable shortcut is rectangular:

It maps a stored state into the next retained basis. Before truncation, is an exact change of basis. After truncation, cannot reconstruct components that already discarded. It is part of the lossy compressed graph, not a proof that the sliced graph equals the original.

A sequential LLaMA block has two residual additions and therefore two such shortcut matrices: one around attention and one around the MLP. A parallel attention/MLP block can share one output boundary and uses a different adapter path. The official CompressedLlamaDecoderLayer makes this runtime state explicit:

rotated_residual = residual @ self.attn_shortcut_Q
hidden_states = rotated_residual + attention_output

rotated_residual = hidden_states @ self.mlp_shortcut_Q
hidden_states = rotated_residual + mlp_output

These are dense matrix multiplications. They cannot generally be folded into a branch weight because the shortcut bypasses the branch nonlinearity.

SliceGPT Figure 4 showing different PCA rotations, sliced input and output matrices, and dense rotation matrices inserted into residual shortcuts

Source: Ashkboos et al., SliceGPT, Figure 4. Cropped from the official paper. Hatched matrix regions are removed; remains executable shortcut work.

0x06E PCA Selects Directions, Not Original Semantic Channels

Directly deleting low-norm coordinate columns assumes that useful residual directions already align with the checkpoint axes. SliceGPT instead collects boundary activations from calibration sequences and forms

Let

Keeping the first eigenvectors gives

where . For the stated squared reconstruction objective, the retained subspace is optimal and the calibration reconstruction error is

That theorem concerns reconstruction of the sampled boundary activations. It does not guarantee language-model loss, downstream accuracy, rare-domain behavior, or long-context quality.

A two-dimensional toy example shows why rotation matters. Suppose

Its eigenvectors are and with eigenvalues and . Keeping one principal coordinate preserves of the sampled squared energy and incurs reconstruction error . Deleting either original coordinate instead discards an axis with diagonal energy . PCA first aligns the high-energy diagonal direction with a coordinate that can be physically kept.

The official implementation is specifically an uncentered Gram/eigensystem calculation, despite using the name PCA:

H = sum_over_batches_and_tokens(X.T @ X)
H += 0.01 * mean(diag(H)) * I
eigvals, Q = torch.linalg.eigh(H.double())
Q = Q[:, argsort(eigvals, descending=True)]

Padding rows are zeroed before accumulation. Adding a scalar multiple of the identity leaves exact eigenvectors unchanged while improving numerical conditioning; finite-precision and repeated-eigenvalue behavior can still vary. The paper reports that eigenvector precision affected large-model accuracy and used double precision for its main PCA calculation. That is a source-reported numerical finding, not a universal requirement for every eigensolver and model.

Calibration choices are part of the method. The paper studies sample count and sequence length and uses larger settings in its main experiments. The current official experiment script defaults to 128 samples and sequence length 2,048. Those are code defaults, not proof that 128 samples cover the deployment distribution.

The Loop 6 fixture makes the exactness boundary numeric. It uses three distinct orthogonal 4 x 4 bases and keeps three coordinates at each compressed boundary. The full-width transformed network matches its original reference. The sliced graph matches the same graph with each discarded projector written explicitly, but intentionally differs from the full-width reference:

ContractStored widthsExpected relation
full rotation4 -> 4 -> 4transformed logits equal original logits
fully narrow slicing3 -> 3 -> 3physical tensors equal projected-coordinate reference; original parity is false
expanded-head slicing3 -> 3 -> 4final shortcut is rectangular and the original-width head remains; original parity is still false
wrong sliced normmean_dim=3 instead of original D=4logits differ from the valid sliced graph

For the fixture’s two-branch, vocabulary-six ledger, the unsliced core has 88 weights. The fully narrow artifact has 66 core weights plus 18 shortcut weights, for 84 total. The expanded-head artifact has 74 core weights plus 21 shortcut weights, for 95 total. The tiny expanded case is deliberately larger than its baseline: reducing a width is not sufficient evidence of reducing the serialized model once bridges and endpoint policy are counted.

0x06F Rotation And Slicing Must Advance Through The Actual Model

Each later basis is estimated from activations produced by the already transformed path. A practical sequential pass is:

inputs = capture_first_layer_inputs(calibration_batches)

# Embedding -> first attention boundary
_, Q = pca_gram(inputs, attention_masks)
rotate_and_slice_embeddings(Q, d_embed)

for layer in layers:
    layer.attn_shortcut = previous_Q.T @ Q
    rotate_attention_input_columns(layer, Q)
    slice_attention_input_columns(layer, d_attn_in)

    attn_to_mlp_states = run_to_mlp_boundary(layer, inputs)
    _, Q_next = pca_gram(attn_to_mlp_states, attention_masks)
    rotate_attention_output_rows(layer, Q_next)
    slice_attention_output_rows(layer, d_attn_out)
    slice_shortcut_both_axes(layer.attn_shortcut, d_attn_in, d_attn_out)

    layer.mlp_shortcut = Q_next.T
    rotate_and_slice_mlp_inputs(layer, Q_next, d_mlp_in)

    outputs = run_complete_compressed_layer(layer, inputs)
    _, Q_out = pca_gram(outputs, attention_masks)
    rotate_and_slice_mlp_output(layer, Q_out, d_mlp_out)
    finish_rectangular_shortcut(layer.mlp_shortcut, Q_out, d_mlp_in, d_mlp_out)

    inputs = outputs
    Q = Q_out

This is a mechanism sketch, not drop-in code. The official implementation also moves layers between CPU and the selected device, updates the layer’s forward arguments, distinguishes sequential and parallel blocks, supports constant and configured per-boundary schedules, and optionally changes the orientation inside the retained subspace.

Its final_orientation="random" option right-multiplies by an orthogonal matrix that acts only inside the retained leading block. This changes the coordinates within the same retained subspace, not which subspace survives. final_orientation="pca" leaves principal axes explicit. The choice can alter finite-precision and downstream compression behavior even though ideal subspace reconstruction is unchanged.

At the pinned Microsoft TransformerCompression commit 6b12cdee6ad51791d7c776b3a046bc408b9e77e9, the prose maps to concrete files:

FileExact implementation contract used here
src/slicegpt/rotate.pysequential/parallel activation propagation, Q estimation, input/output slicing, rectangular shortcut construction, optional head slicing, and PCA damping/eigendecomposition
src/slicegpt/layernorm_fusion.pycompressed-layer replacement, tied-head clone, embedding mean subtraction, affine norm fusion, and replacement by bare RMSN
src/slicegpt/modules.pyRMSN.mean_dim remains the original hidden size even when the stored last axis is narrower
src/slicegpt/slicing_scheduler.pyper-boundary dimensions are recorded; do_slice_head=False is the default and returns the final boundary to the original hidden size
src/slicegpt/model_adapter.pycompressed layers register shortcut parameters and SlicingConfig serializes every boundary dimension and endpoint policy
src/slicegpt/adapters/llama_adapter.pyLLaMA executes residual @ attn_shortcut_Q and residual @ mlp_shortcut_Q before the two additions
src/slicegpt/hf_utils.pyfresh loading constructs compressed modules, fuses norms, allocates shortcut state, restores JSON config, slices the uninitialized graph, then loads weights
experiments/run_slicegpt.pythe experiment path replaces/fuses modules, creates a constant scheduler, rotates/slices, and saves both state dictionary and JSON config

That trace is version-specific. It verifies what this repository revision does; it does not establish one universal SliceGPT ABI or prove compatibility with a newer Transformers release, tensor-parallel engine, compiler, or quantized loader.

0x06G Every Stored Axis Has A Different Slice Direction

For a sequential decoder with boundary dimensions before a branch and after it, the stored PyTorch axes are:

ParameterDense stored shapeRotateSliceResult
token embedding[V,D]right by columns[V,d_0]
learned additive embedding[N,D] or equivalentright by columns[N,d_0]
Q/K/V weight[d_{q/kv},D]right by input columns[d_{q/kv},d_a]
O weight[D,d_q]left by output rows[d_b,d_q]
gate/up weight[d_{ff},D]right by input columns[d_{ff},d_b]
down weight[D,d_{ff}]left by output rows[d_c,d_{ff}]
shortcut[D,D] conceptuallyrows and columns[d_a,d_b]
LM head weight[V,D]right by input columns when enabled[V,d_L]

The attention inner outputs and need not equal the new residual width. Residual slicing changes the input dimension of Q/K/V and the output dimension of O. It need not delete heads or coordinates inside a head. Similarly, it changes gate/up input columns and down output rows, not the MLP intermediate width .

Two small helpers express the physical operation:

def rotate_and_slice_input(linear, Q, keep_dim):
    # stored [out, residual_in]
    W = linear.weight.double() @ Q.double()
    linear.weight = nn.Parameter(W[:, :keep_dim].to(linear.weight.dtype))
    linear.in_features = keep_dim


def rotate_and_slice_output(linear, Q, keep_dim):
    # stored [residual_out, inner]
    W = Q.double().T @ linear.weight.double()
    linear.weight = nn.Parameter(W[:keep_dim, :].to(linear.weight.dtype))
    if linear.bias is not None:
        b = Q.double().T @ linear.bias.double()
        linear.bias = nn.Parameter(b[:keep_dim].to(linear.bias.dtype))
    linear.out_features = keep_dim

These helpers are valid only after the architecture adapter has identified which linears enter and leave each residual boundary. Cross-attention, parallel residual branches, mixture-of-experts shared/routed paths, adapters, multimodal projectors, and recurrent state connectors add more consumers to the closure.

A global residual-width slicing trace showing the distinction between exact orthogonal rotation and lossy PCA truncation, every embedding attention MLP normalization shortcut and output-head dependency, rectangular inter-boundary shortcut matrices, and the runtime quantities that do and do not shrink

0x06H Embeddings, Tied Weights, And The Final Head Need An Explicit Policy

Every tensor added into the first residual state must use the same retained basis. Token embeddings are not enough when a model also has learned position, segment, modality, prompt, or vision-projector outputs. A state can have the right width and still use the wrong basis.

Input embedding and output head weights are often tied. Under one shared full rotation and one shared slice, both stored [V,D] tensors can transform as and remain tied. Per-boundary SliceGPT generally uses different first and last bases, and it may choose different endpoint widths. The official fusion code clones the LM-head weight before modifying embeddings, explicitly breaking shared parameter storage so the two boundaries can evolve independently.

The current official main experiment creates ConstSlicingScheduler(new_embedding_dimension) with do_slice_head=False. The scheduler therefore keeps the final head dimension at the original hidden size: the final layer’s output path and shortcut return to that width, and the LM head is rotated but not sliced. Head slicing is supported as an option, not the default path. Consequently, the phrase “SliceGPT reduces embedding dimension” does not imply that every released/configured path slices the LM head input identically.

An artifact must declare one of these endpoint contracts:

fully narrow endpoint:
    input embedding [V,d0]
    final state [B,S,dL]
    LM head [V,dL]
    tied weights possible only if endpoint basis/width policy permits

expanded head endpoint:
    narrow internal state
    final branch/shortcut maps back to original D
    original-width LM head remains

external bridge:
    narrow Transformer state
    explicit learned/fixed bridge into a different head or projector width

The tokenizer and vocabulary axis do not change. Slicing an embedding column is not vocabulary pruning.

0x06I Residual Slicing Does Not Automatically Shrink KV Cache

Suppose GQA uses

Residual slicing from to changes:

q_proj: [d_q,D]   -> [d_q,d']
k_proj: [d_kv,D]  -> [d_kv,d']
v_proj: [d_kv,D]  -> [d_kv,d']
o_proj: [D,d_q]   -> [d',d_q]

The produced K/V vectors still have width . Therefore

is unchanged unless head count or head width is also reduced. QK score and probability-value attention MACs are also unchanged because their head-space dimensions are unchanged. Residual slicing reduces Q/K/V projection input work, O projection output work, residual activation traffic, MLP residual-axis work, and possibly embeddings/head work.

This makes Loop 5 and Loop 6 complementary. A compound exporter may first choose head/KV groups and MLP channels, then rotate and slice residual widths, but the transforms do not commute blindly. The dependency map, calibration activations, quantization scales, adapters, and recovery all need to be recomputed for the chosen order.

0x06J Parameter And MAC Ledgers Must Include Shortcut Work

Ignoring biases and normalization, one LLaMA-style layer has residual-axis linear parameters

The first two terms count Q/O and K/V; the third counts gate, up, and down. If only residual dimensions become while head and MLP inner dimensions stay fixed:

For sequential blocks with two distinct sliced shortcut maps of shape , add approximately

parameters and the same number of MACs per token row. Variable widths instead add for each boundary map.

For a toy LLaMA-like layer with , , , and :

baseline residual-axis core:
    4 * 4096^2 + 3 * 4096 * 11008
    = 202,375,168 weights

sliced core before shortcuts:
    0.75 * baseline
    = 151,781,376 weights

two 3072 x 3072 shortcuts:
    18,874,368 weights

sliced core plus shortcuts:
    170,655,744 weights

net reduction in this ledger:
    31,719,424 weights, about 15.7%, not 25%

Embedding tables can contribute a large additional saving: values per sliced table. A sliced LM head contributes another unless weights remain tied or the head endpoint expands. Conversely, custom shortcut state and untied endpoint weights can add payload. The width ratio is not the same as whole-model parameter sparsity; the official script separately measures the final parameter count for this reason.

The MAC ledger follows the same linear reductions for branch projections and adds shortcut GEMMs. It must then account for unchanged attention-core work, unchanged KV cache, normalization, RoPE, sampling, collectives, and kernel alignment. Smaller dense dimensions make ordinary dense-kernel acceleration possible, but do not guarantee it.

0x06K Uniform And Variable Schedules Trade Simplicity For Fit

The current official code supports several shape policies:

PolicyBasisWidth scheduleShortcut consequenceDeployment consequence
one global coordinate maskoriginal axesusually constantidentity shortcut survivessimplest loader; weakest alignment to each boundary distribution
one global orthogonal basisone constantidentity shortcut survivesno shortcut GEMM, but one basis must fit all layers
SliceGPT constant schedulerdifferent common retained width internallydense square shortcutsregular GEMM shapes; custom compressed layer
SliceGPT configured/function schedulerdifferent boundary-dependent widthsdense rectangular shortcutsbetter local fit is possible; heterogeneous shapes complicate kernels and sharding
global residual-channel dependency pruningoriginal learned axesselected coordinatesidentity shortcut survives if one map is globalno rotation overhead; selection/recovery bears alignment burden
low-rank factorizationlearned/fitted factorsfactor rank per matrixresidual width may remain two GEMMs and intermediate factor activations rather than one smaller dense model

Per-layer spectrum decay does not automatically prove that heterogeneous widths are better. The SliceGPT appendix reports model-dependent results when varying slice level by layer. Width schedules need held-out quality and runtime evaluation rather than an eigenvalue-only decision.

0x06L Calibration, Recovery, And Deployment Are Separate Costs

SliceGPT is post-training in the sense that rotation and initial slicing do not require updating the base weights by gradient descent. It still needs calibration examples, layer executions, activation Gram accumulation, an eigendecomposition at each boundary, model conversion, and evaluation. The paper also studies optional recovery fine-tuning; the official repository provides a LoRA recovery path.

Keep these phases separate:

calibration and basis discovery
    -> dataset, sample count, sequence lengths, padding mask, precision

exact architecture conversion
    -> norm fusion, model adapter replacement, full rotations, parity

lossy slicing
    -> width schedule, endpoint policy, rectangular shortcuts

optional recovery
    -> task data, LoRA/full parameters, objective, optimizer, merge policy

deployment conversion
    -> dtype/quantization, sharding, compile, kernels, cache, manifest

Quantization after rotation sees different weight and activation distributions; pre-existing group scales are no longer valid. Quantizing first and then performing dense rotations generally requires dequantization or a compatible quantized transform. Tensor parallel shards must be regenerated from global sliced tensors because both residual-axis ownership and shortcut dimensions change. LoRA attached before rotation must be merged or transformed on the same axes; adding LoRA after slicing is simpler.

The paper’s throughput, GPU-count, and quality results are source-reported for its models, hardware, calibration, and evaluation setup. They demonstrate that smaller dense execution can produce real gains; they are not a portable latency multiplier for another runtime.

0x06M The Artifact Requires A Compressed Architecture, Not Just New Weights

The official pipeline performs three architectural stages:

replace_layers(model_adapter)
    -> install compressed layer classes with shortcut matrices

fuse_modules(model_adapter)
    -> absorb affine norms / mean subtraction and install bare RMSN

rotate_and_slice(model_adapter, calibration_loader, scheduler)
    -> estimate bases, rotate weights, slice axes, attach shortcut state

It saves the sliced state_dict together with a JSON SlicingConfig containing embedding, attention-input/output, MLP-input/output, and head dimensions. A loader must construct the custom compressed modules before loading that state. Merely changing a stock Hugging Face hidden_size is insufficient because:

  • head-space widths can remain at their original values;
  • residual widths can differ by boundary;
  • shortcut matrices are new executable parameters;
  • normalization uses an original-dimension denominator;
  • the final head may intentionally retain original width;
  • standard tied-weight and cache assumptions may no longer hold.

A release manifest should include model-family adapter version, source checkpoint hash, calibration contract, basis/scheduler policy, every boundary dimension, endpoint policy, norm conversion, shortcut shapes, original and final parameter counts, dtype, sharding, and recovery lineage.

0x06N Validation Must Stop At Each Exactness Boundary

The safest validation ladder is:

1. Norm-fusion parity

Compare the original model with the model after affine/mean folding and custom layer replacement, before rotation or slicing. Check intermediate boundaries, logits, and cached decode.

2. Full-rotation parity

Apply square matrices and unsliced shortcuts. Compare against the fused model. This tests orientation, stored axes, biases, parallel/sequential branch handling, and the final head.

3. Slicing quality, not parity

After applying , exact equality to the dense model is no longer expected. Measure calibration reconstruction, held-out language-model loss, task suites, rare/long-context behavior, and generation. A parity assertion at this stage would be the wrong contract.

4. Fresh-process artifact proof

Reconstruct modules from the slicing config, load with no missing or unexpected state, and reproduce sliced-model logits and cache behavior. Test endpoint weight tying, generation, serialization, and multiple sequence lengths.

5. Runtime proof

Record final parameter bytes, shortcut bytes/MACs, activation width, actual GEMM shapes, selected kernels, sharding, peak memory, prefill, decode, throughput, KV bytes, and end-to-end latency. Attribute the gain to narrower dense work rather than to the nominal slicing percentage.

0x06O Residual-Width Failure Lab

FailureSilent causeObservable consequenceRepair
delete residual coordinate in only one layeridentity shortcuts globally couple the stateadd/next-layer shape failure or coordinate driftclose the dependency graph or insert an explicit compatible bridge
call the orthogonal rotation lossyexact basis change and truncation conflatedinability to localize conversion bugsprove full-rotation parity before applying
call PCA truncation exactinvariance theorem applied after deleting coordinatesunacknowledged quality losstreat slicing as approximation and evaluate it
rotate linears but leave learned RMSNorm scalediagonal affine state does not commute with dense logits drift before slicingfold norm state into adjacent modules and parity-test
replace sliced norm with ordinary RMSNorm(d')denominator changes from original to systematic activation rescalingserialize and preserve normalization reference dimension
use different per boundary but retain identity shortcutresidual and branch outputs use different basessemantically invalid additionexecute on the shortcut
omit shortcut parameters from parameter/FLOP countbridge work treated as freeoverstated compression and speedcount every square/rectangular shortcut
slice Q/K/V output rows as residual pruninghead coordinates confused with residual inputaccidental head-width/head-count changeslice Q/K/V input columns; leave head outputs unless separately pruned
claim residual slicing reduces KV cacheK/V output dimension stayed fixedcache bytes do not movecombine with explicit KV-head/width pruning if desired
rotate token embeddings but not learned position/type embeddingsadditive sources use different baseswrong initial residual statetransform every residual-state producer
preserve tied storage across different endpoint basesinput and output endpoints require incompatible transformsone boundary silently becomes wrongenforce shared endpoint policy or untie explicitly
report width sparsity as parameter sparsity ignores embeddings, head, shortcuts, and unsliced axesmisleading artifact sizerecount the serialized model
compute bases from padded tokenspadding distribution enters unstable or domain-specific componentsmask padded rows before accumulation
use one calibration domain and claim universal subspace qualityprincipal directions are data-dependentrare/domain performance regressiondeclare data contract and evaluate held-out domains
reuse quantization scales after rotationbasis change changes tensor distributions and groupspoor quality or invalid group metadatarecalibrate/requantize after final slicing
mutate already-sharded tensors without global mapsresidual axes cross TP ownershipcollective mismatch or rank imbalancetransform globally, then repartition and validate collectives
load sliced weights into a stock modelcustom shortcuts and dimensions are absentmissing keys, shape errors, or wrong forwardconstruct versioned compressed modules from manifest first
infer speed from the paper’s GPU resultsource setup treated as universaldeployment misses targetbenchmark the actual runtime, hardware, batch, and sequence mix

0x06P Loop Boundary

Loop 6 has now followed the residual coordinate from source to sink:

global dependency closure
    -> all embeddings and residual-state producers
    -> Q/K/V and gate/up residual input axes
    -> O and down residual output axes
    -> norm state, shortcuts, final norm, and output boundary

exact conversion
    -> fold noncommuting normalization state
    -> rotate each boundary with orthogonal Q
    -> prove full-width logits and cache parity

lossy compression
    -> estimate uncentered activation Gram matrices
    -> retain a PCA subspace with explicit D
    -> slice linears and rectangular shortcut matrices

artifact and runtime proof
    -> custom compressed layer + slicing manifest
    -> fresh-process reload and held-out quality
    -> count shortcut work and unchanged KV/attention work
    -> measure smaller-dense execution

Loop 7 changes a different axis: depth. It owns deletion of whole ordered Transformer blocks, LayerDrop’s training-time subnetworks, post-training block importance methods such as ShortGPT, depth-versus-width comparisons, and the cache/config/runtime consequences of a shorter graph. Residual-width slicing does not imply that any layer is removable.

0x07 Layer Pruning Removes An Ordered Function, Not A Tensor Axis

Width pruning asks which coordinates inside a block can disappear. Depth pruning asks whether the complete transformation performed by a block can disappear. This sounds coarser and therefore simpler: every surviving block can retain its original residual width, attention heads, MLP width, and dense kernel shapes. But the removed object is not one contiguous matrix slice. It is one function in an ordered composition, together with its parameters, cache slot, checkpoint namespace, pipeline placement, and recovery role.

The owner question is:

When can a complete Transformer block be replaced by the identity, how do training-time LayerDrop and post-training scores provide evidence for that counterfactual, and what must change before the result is a deployable shorter model rather than a Python list with missing entries?

This loop uses four primary paths for distinct purposes:

LayerDrop
    -> train one model to tolerate many missing-layer subnetworks

ShortGPT
    -> rank post-training blocks by hidden-state angular change

Shortened LLaMA
    -> compare PPL, Taylor, magnitude, depth/width runtime, and recovery

Minitron
    -> search depth and width jointly, then rerank candidates after recovery

None supplies a universal layer order. Their scores, calibration data, training state, budgets, and exported runtimes are different contracts.

The replayable evidence for this loop uses one deterministic six-block, three-coordinate row-major fixture. It imports no framework and executes no paper checkpoint. Its narrower job is to prove complete-gate versus physical deletion parity, enumerate a LayerDrop depth distribution, expose score and subset interactions, distinguish three runtime artifacts, compact every layer-indexed namespace, and demonstrate that a recovery-aware candidate order can differ from a zero-shot order. Paper quality, reported rankings, training, recovery, GPU speed, production loading, and deployment remain outside that synthetic evidence ceiling.

0x07A The Residual Connection Makes Deletion Shape-Valid, Not Harmless

Let one complete decoder block be . For the sequential pre-norm block used throughout M3.7,

Both residual additions return a tensor in . Define the complete block delta

A scalar gate on the complete block is then

The two endpoints have exact meanings:

This is the correct functional counterfactual for deleting the whole block. Gating only attention, only the MLP, or only one output projection asks a different question. Setting every linear weight to zero can approximate the identity in a bias-free LLaMA block, but it is a fragile oracle when biases, adapters, residual scales, stateful modules, or nonstandard branches exist. The complete-delta gate names the intended function directly.

If the original model has blocks and the retained original indices are

the shortened network is

Order is preserved, but adjacency changes. If block 7 is removed, block 8 now receives the representation produced by block 6. The residual path makes that composition type-correct because all boundaries retain width ; it does not make on the relevant data.

The first converter oracle is therefore:

def whole_block_gate(block, hidden, keep: bool, **kwargs):
    if keep:
        return block(hidden, **kwargs)
    return hidden  # exact identity counterfactual for this block

# Before rebuilding the model, compare these two paths.
y_gated = dense_model(x, block_keep_mask=keep_mask)
y_short = physically_shortened_model(x)
assert_close(y_short, y_gated)

The shortened model is not expected to match the original full model. It must first match the dense model under the same fixed block mask. That test separates converter errors from the quality loss caused by the deletion decision.

Four-stage depth-pruning diagram showing a complete-block identity gate, LayerDrop and post-training ranking evidence, physical layer-list deletion and renumbering, and checkpoint cache pipeline and runtime ledgers

0x07B Parameters, Block Calls, And KV Slots Move Together But Not Equally

Let contain embeddings, the final norm, output head, and any other state not owned by the repeated blocks. If block has parameters, then

For uniform blocks with parameters,

The block-stack parameter ratio is , but the whole-model ratio is

which is larger than whenever . Reporting “25% of layers removed” as “25% of model parameters removed” silently ignores the endpoint state.

The same additive rule applies to block work, but prefill and decode have different per-block shapes:

For a standard layer-local KV cache,

Uniform deletion therefore reduces the layer-stack KV payload in proportion to . This differs from Loop 6: residual-width slicing can make Q/K/V input projections narrower while leaving and persistent KV width unchanged. Depth deletion removes the complete K/V cache slot for every deleted block.

That theoretical saving requires the runtime to construct only cache entries. A model that merely skips the attention/MLP body while retaining an -slot cache schema has a different artifact and may retain some projection or cache work. The ShortGPT implementation comparison in 0x07G makes this distinction concrete.

0x07C LayerDrop Trains A Distribution Of Missing-Layer Graphs

LayerDrop starts before post-training ranking. It constrains a structured random mask to a complete group of weights, then specializes that group to Transformer layers. In the complete-delta notation above, training samples

and optimizes an expectation over subnetworks:

The useful training signal is not “layer is unimportant.” It is that surviving blocks repeatedly see predecessor representations generated under many missing-layer patterns. They learn a model that is more robust to those changed adjacencies than a model trained only on the full path.

If each of layer groups is independently retained with probability , the active-depth random variable

has

To center training on a target retained depth , the paper derives

This aligns the expected number of active groups with . It neither guarantees exactly active layers on every training example nor identifies which fixed layers should be exported. Those are selection questions. The paper also reports that larger train-time drop rates help more aggressive test-time depth reduction in its studied models; that is source-reported evidence, not a universal schedule.

LayerDrop Figure 1 contrasting separate training for each depth with one full model trained to support several fixed shallower subnetworks

Source: Fan, Grave, and Joulin, Reducing Transformer Depth on Demand with Structured Dropout, Figure 1. Cropped from the official paper. The figure expresses the training objective’s purpose; a deployment still exports and validates one fixed selected subnetwork.

LayerDrop compares three inference-time selectors:

SelectorEvidenceCostMain risk
every other layerbalanced depth spacingnegligiblespacing is a heuristic, not task-specific evidence
search on validationevaluate candidate layer subsetscombinatorial or search-dependentexpensive and can overfit the validation set
data-driven fixed top-learn per-layer drop-rate scores under an average-rate constraintextra training and scoringlearned rule still requires a fixed export decision

The paper found every-other selection difficult to beat in its experiments and found consecutive first-half or last-half deletion harmful. These observations fit an ordered-composition view: early blocks process raw embedding state, late blocks prepare the final prediction state, and a long missing interval creates a larger adjacency shift than balanced omissions. They do not prove that every-other deletion is optimal for current decoder-only LLMs.

The Loop 7 fixture makes the expectation-versus-export distinction executable. For and , it enumerates all binary masks rather than sampling them. Their probability mass is exactly , while

E[K]   = 4 * (1 - 0.25)       = 3
Var[K] = 4 * 0.25 * (1-0.25) = 0.75

The fixed export used elsewhere in the fixture is a separate six-to-four decision, keep_old = [0, 2, 3, 5]. Matching an expected active depth during training does not create that ordered keep set.

LayerDrop is therefore a training-time robustness prior. It can be paired with balanced selection, validation search, learned fixed scores, or another post-training criterion. “Trained with LayerDrop” and “selected these layers” must remain two separate lines in an audit manifest.

0x07D Four Post-Training Scores Approximate Four Different Effects

For a pretrained checkpoint not prepared with LayerDrop, depth selection often uses calibration data. The following scores are not interchangeable.

One-Block Perplexity Ablation

Let calibration tokens be , and let denote the model with block replaced by identity. Its average next-token negative log-likelihood is

The corresponding perplexity is

Since the exponential is monotone, ranking by or is equivalent on the same calibration set. A low value means that this one-block ablation was less damaging. It is direct evidence for the model’s prediction objective, but it requires roughly ablated evaluations for blocks.

It is still local. In general,

because after removing , block receives a different representation. A one-shot ranking must be followed by joint-subset validation; iterative rescoring is an alternative with higher calibration cost.

Gate Taylor Evidence

The exact whole-block counterfactual can also be relaxed to . A first-order expansion around the full model gives

An absolute first-order block score is therefore

This directly differentiates the identity-versus-block decision. Shortened LLaMA instead aggregates parameter-level Taylor products inside each block:

The exact reductions over elements and matrices matter. Sum of absolute terms, absolute of a signed sum, mean, maximum, and product do not produce the same ranking. The official Shortened LLaMA code accumulates parameter-times-gradient state, takes absolute values and configurable norms at output-neuron level, then exposes configurable weight-level and block-level reductions. “Taylor score” is therefore incomplete without those choices.

Parameter Magnitude

A data-free block magnitude score aggregates surviving block weights:

It is cheap, but it measures parameter coordinates rather than the complete function delta . LayerNorm/RMSNorm, residual scaling, matrix shape, and parameterization can make equal norms produce different hidden-state effects. Shortened LLaMA’s Mag+ and Taylor+ variants preserve the first four and last two blocks by excluding them from candidate deletion, because the raw metrics could rank boundary blocks as removable even when ablation quality said otherwise. That “plus” rule is a source-specific structural prior, not part of Taylor mathematics.

Block Influence

ShortGPT defines Block Influence for hidden row as cosine distance between the block input and output:

Lower means the block changes representation direction less on the calibration rows. All layer-boundary hidden states can be collected in one forward pass, making BI much cheaper than one-block PPL evaluations.

The geometric limitation follows from decomposing the block delta into parallel and orthogonal parts. Let

Then

For a small orthogonal change,

This explains what BI favors: low angular deflection relative to the residual state. If for any positive , BI is zero even though the norm changed. Conversely, a small orthogonal rotation can produce a nonzero BI without immediately proving a large next-token loss. BI is a useful proxy, not an identity test or a loss theorem.

The deterministic fixture applies all four score definitions to the same six blocks, calibration rows, objective, and full-model state. Lower-is-more- removable rankings are:

Score contractFixture ranking, lowest first
one-block objective loss1, 4, 0, 5, 2, 3
absolute complete-gate Taylor2, 5, 0, 3, 4, 1
parameter L1 magnitude0, 3, 2, 5, 1, 4
row-wise block influence0, 4, 1, 5, 3, 2

These are synthetic values, not paper-ranking reproductions. Their semantic purpose is that one shared input does not collapse four estimands into one order. The same fixture removes blocks 1 and 4 together: its actual pair loss is 0.013617906608, while adding their two isolated loss changes predicts 0.010770016105. That exact mismatch is the subset-interaction warning above.

0x07E ShortGPT Turns Hidden-State Geometry Into A Deletion Order

The ShortGPT paper applies the following pipeline:

unlabelled calibration text (PG19 in the paper)
    -> run the full model with all hidden boundaries returned
    -> compute one BI aggregate for every adjacent boundary pair
    -> sort block indices by ascending BI
    -> delete the requested number of lowest-BI blocks
    -> evaluate the joint shortened model

The paper’s Figure 3 compares BI with the PPL obtained by separately removing each layer. It is evidence from two source checkpoints, not a guarantee that BI and PPL correlate for every model or domain.

ShortGPT Figure 3 plotting block-influence score and one-block-removal perplexity over depth for Llama 2 7B and Baichuan 2 7B

Source: Men et al., ShortGPT: Layers in Large Language Models are More Redundant Than You Expect, Figure 3. Cropped from the official paper. Both curves are source-reported calibration evidence.

The official code makes several implementation choices that should be visible in a reproduction:

# Conceptually equivalent, memory-linear BI accumulation.
def accumulate_bi(x_in, x_out, valid_tokens, total, count):
    # x_*: [batch, sequence, hidden]
    x = x_in.reshape(-1, x_in.shape[-1]).float()
    y = x_out.reshape(-1, x_out.shape[-1]).float()
    valid = valid_tokens.reshape(-1).bool()

    cosine = torch.nn.functional.cosine_similarity(x[valid], y[valid], dim=-1)
    total += (1.0 - cosine).sum(dtype=torch.float64)
    count += valid.sum()
    return total, count

bi = total / count.clamp_min(1)

The reference metrics.py flattens hidden rows, computes the full matrix input @ output.T, and takes only its diagonal. A row-wise dot product computes the same intended diagonal without materializing the quadratic token-by-token matrix. The reference accumulation uses a sum and replaces NaN similarity with 0.5; it also does not apply the tokenizer padding mask inside BI after the model forward. A robust reproduction should define zero-norm behavior, apply a valid-token mask, state sum-versus-mean reduction, and accumulate in adequate precision.

The official main path deletes entries from the layer container in reverse index order and then rewrites each surviving attention module’s layer_idx. Reverse deletion prevents earlier list indices from shifting before later deletions. The minimal mutation is:

drop = sorted(selected_original_indices, reverse=True)
for old_idx in drop:
    del model.model.layers[old_idx]

for new_idx, layer in enumerate(model.model.layers):
    layer.self_attn.layer_idx = new_idx

That is enough to change one in-memory Python graph, but not enough to define a portable release. A complete exporter must also set the configured layer count, preserve an original-to-new index map, migrate all layer-indexed state, save with the architecture metadata, and load the result in a fresh process.

At immutable ShortGPT commit 9a2ee8dfe9e28d0bee430e83c140b787c02ddd6c, the evidence path is split across four files: metrics.py:4-22 defines BI, shortgpt.py:26-47 ranks and deletes, run_shortgpt.py:9-38 fixes PG19 calibration and rewrites layer_idx, and shortgpt-gen/llama/shortgpt_llama.py:89-148 implements the non-equivalent phase-specific cache path described in 0x07G. Those line contracts are version-specific. They explain that repository revision; they are not generic Transformers APIs.

0x07F Shortened LLaMA Makes Runtime And Recovery Part Of The Choice

Shortened LLaMA focuses on a practical contrast. Width pruning keeps the number of block invocations but changes projection dimensions. Depth pruning keeps each surviving block’s native dense shapes but removes complete calls. On the small-batch GPU regimes studied in the paper, some narrowed shapes were less hardware-friendly while complete block deletion produced clearer latency gains. That is a measured result for the paper’s models, shapes, hardware, batch sizes, and software, not a theorem that depth always beats width.

Shortened LLaMA Figure 3 contrasting complete Transformer-block deletion with narrower attention and FFN projections

Source: Kim et al., Shortened LLaMA: Depth Pruning for Large Language Models with Comparison of Retraining Methods, Figure 3. Cropped from the official paper. The article keeps whole-block depth deletion separate from the head/channel surgery derived in Loop 5.

The paper studies four evidence families: magnitude, parameter Taylor, boundary-preserving Mag+/Taylor+, and one-block PPL. The official repository turns PPL scoring into an explicit -model loop:

for original block index l:
    construct model with only block l removed
    run next-token loss on calibration samples
    store PPL_l and parameter count
sort original block indices by ascending PPL_l

Its exporter does not mutate the old ModuleList in place. It copies the configuration, decrements num_hidden_layers, constructs a new shorter model, and copies each surviving original block into a consecutive new index:

old 0 -> new 0
old 1 -> new 1
old 3 -> new 2       # old 2 removed
old 5 -> new 3       # old 4 removed
...

This is a stronger artifact boundary than deleting Python list entries alone: the saved configuration and state-dictionary namespaces agree on the new depth. It still needs modern cache, sharding, tied-state, adapter, and runtime tests when ported beyond the repository’s model/version assumptions.

The official Shortened LLaMA repository is pinned at 4031a9212aa0dbeec8f2ad504af9346c758b0147. Its code keeps the contracts separate: anal_block_sensitivity_ppl.py:73-167 constructs and evaluates one single-block deletion at a time; anal_block_sensitivity_taylor.py:94-189 and anal_block_sensitivity_magnitude.py:78-156 define different reductions; utils.py:114-171 rebuilds the shorter configuration and copies old -> new state; block_prune.py:63-107 applies the selected order and evaluates the artifact; and lora_retrain.py:108-234 trains, saves, and can merge adapter recovery. A ranking script, a converter, and a recovery script are therefore distinct executable stages, not one undifferentiated method.

Shortened LLaMA also makes recovery severity explicit:

Recovery pathUpdated stateCost characterPaper-scoped lesson
noneno surviving parameterscalibration/evaluation onlyisolates immediate deletion damage
LoRAlow-rank adapter state on surviving projectionsrelatively light task/instruction recoverycompetitive in some moderate-pruning settings studied
continued pretrainingall surviving base parameterslarge corpus and optimizer statesubstantially stronger for aggressive depth reduction in the paper
continued pretraining then LoRAfull-model recovery followed by task adaptationcombines both costsseparates language recovery from later instruction adaptation

The paper also compares removing complete blocks against removing attention or FFN modules separately and reports whole-block pruning as favorable in its studied regimes. That result is source-scoped. The mechanisms differ: module-level deletion can preserve more depth but creates mixed block types; whole-block deletion removes both branches and produces a regular shorter stack. Runtime regularity is part of the unit decision, not an afterthought.

0x07G “ShortGPT Runtime” Names Two Non-Equivalent Graphs In The Official Repository

The official ShortGPT repository contains a direct-deletion path and a separate shortgpt-gen experiment. They should not be described as one implementation.

Path One: Physical Depth Deletion

The main shortgpt path removes selected modules from model.layers. Once the configuration and serialization are repaired as described above, the runtime has block calls and cache layers. Both prefill and one-token decode use the shorter composition.

prefill:
    execute kept blocks only

decode:
    execute kept blocks only

KV schema:
    one cache slot per kept block

Path Two: Prefill-Only Full-Block Skip With Cache Projection

The repository’s custom LLaMA generation model keeps the original layer list. For a selected layer and a multi-token input, it skips the full block update but, when cache is enabled, still applies input normalization, K projection, V projection, reshape/transposition, RoPE metadata, and cache update. For a one-token input, its condition is false and the complete layer executes.

for layer_id, block in enumerate(model.layers):
    if q_len > 1 and layer_id in prefill_skip_set:
        if use_cache:
            x_norm = block.input_layernorm(hidden)
            k = block.self_attn.k_proj(x_norm)
            v = block.self_attn.v_proj(x_norm)
            cache.update(k, v, layer_id, rope_metadata)
        # hidden remains unchanged
    else:
        hidden = block(hidden, cache=cache, ...)

Its contract is therefore:

prefill:
    skip attention output + MLP for selected layers
    retain K/V projection and cache-write work for those layers

decode:
    execute all original layers

KV schema:
    retain original per-layer slots

This can be a valid experimental latency tradeoff, but it is not a physically shorter model and it does not have the depth-pruned KV equation from 0x07B. It also means the prefill and decode functions differ: decode revisits full blocks whose prefill cache was built from a hidden state that bypassed those blocks. Any article or benchmark must name which path was measured.

This distinction generalizes. A deployment can implement:

static physical deletion
fixed identity gates in a full graph
prefill-only skip with cache repair
decode-only skip
input-dependent routing

Only the first is the shorter static artifact owned by this section. Fixed gates are an oracle or transitional implementation. Phase-specific skipping is a runtime policy. Input-dependent routing belongs to M6.8.

0x07H Minitron Searches Architecture Tuples, Then Tests Them After Recovery

Minitron expands the decision from “which layers?” to “which architecture near a target parameter count?” A candidate can contain a depth , embedding width , MLP width , attention-head count , and query-group count :

Let be the exact parameter count after applying all coupled tensor transformations. A simple feasibility band around target is

Enumerating only feasible tuples is important because one deleted block and one reduced MLP multiple do not remove equal parameter counts. It also prevents a quality comparison from quietly comparing different budgets.

For depth, the paper reuses one-block PPL and ShortGPT-style BI. For width, it uses forward-activation statistics over heads, neurons, and embedding channels. It then trims the selected architecture, performs lightweight retraining on every feasible candidate, reranks candidates, and spends the full retraining budget only on the selected architecture.

Minitron Figure 3 showing search over MLP ratio, attention heads, embedding width, and depth, followed by lightweight and full retraining

Source: Muralidharan et al., Compact Language Models via Pruning and Knowledge Distillation, Figure 3. Cropped from the official paper. RT in the figure means retraining.

The lightweight stage is not cosmetic. Minitron reports that candidate order can change during the first few hundred recovery steps in its studied search, including a width-versus-combined-depth/width ranking flip around 200 steps. The exact step and token counts are source-reported. The transferable lesson is that zero-shot post-prune loss may rank initialization damage, while the release objective cares about recoverable quality under a declared training budget.

Formally, a zero-shot selector chooses

where is the immediately trimmed initialization. A recovery-aware selector instead approximates

for a fixed short recovery budget . These objectives need not have the same minimizer.

Minitron also compares one-shot and iterative importance estimation. Its reported recommendation favors single-shot importance for the model scales and budgets studied, and its paper recommends width over depth for the examined models up to 15B. Those are empirical design choices, not contradictions of Shortened LLaMA. The papers optimize different model families, workloads, hardware regimes, recovery budgets, and search axes. The correct conclusion is to include target runtime and recovery in the candidate objective.

The current NVIDIA NeMo pruning guide, last updated April 13, 2026, exposes three separate deployment operations:

target_num_layers
    -> BI-calibrated depth pruning

drop_layers
    -> caller-specified one-based layer indices

target hidden / FFN / head / query-group dimensions
    -> width pruning, optionally combined with depth

It also documents current API limits such as tp_size=1 for calibrated pruning and recommends distillation after pruning. These are versioned NeMo tool contracts, not properties of Minitron mathematics. The NVlabs Minitron repository primarily publishes papers, model links, and deployment entry points; the current executable pruning path is in NVIDIA’s NeMo/Model Optimizer stack. Paper method, released checkpoint, and current tooling must be cited as three different sources.

For auditability, the current guide was retrieved on August 12, 2026 as a 57,283-byte HTML snapshot with SHA-256 6af5228ff904b83a8c98df92f25a3346230442bbba81e1a435eb7d521275aa1e. That moving documentation record supports the current command/API boundary; it does not replace the immutable Minitron v2 paper. Likewise, the pinned NVlabs Minitron commit fed740c91886155e1f272159ef662536bb673593 contains a project README and released-model/deployment links, not the full paper pipeline implementation. The article therefore does not pretend that cloning that repository reproduces architecture search or distillation.

0x07I The Paper Variants Differ Across Five Contracts

PathPreparation or evidenceSelection objectImmediate artifactRecovery contractRuntime claim boundary
LayerDroprandom structured layer masks during trainingevery-other, validation search, or learned fixed top-fixed shallower subnetwork selected from trained full modelmain pruning comparisons often use none; appendices study fine-tuningolder Transformer tasks and implementations in the paper
ShortGPT main pathcalibration hidden states and BIlowest-BI individual blockslayer entries deleted from in-memory stackno mandatory recovery in core methodportable release needs config/cache/export work beyond minimal code
ShortGPT generation pathsame chosen indicesfixed prefill skip setfull layer stack with phase-specific branchnot a depth-pruned checkpointselected prefill blocks still project/cache K/V; decode executes all layers
Shortened LLaMAmagnitude, Taylor, boundary-preserving variants, or one-block PPLone-shot complete blocks, with optional protected endpointsnewly constructed shorter model and copied survivor weightsnone, LoRA, CPT, or CPT then LoRAdepth-vs-width timings are hardware, shape, batch, and software scoped
MinitronBI/PPL for depth plus activation evidence for widthfeasible architecture tuple under parameter budgettrimmed depth/width candidatelightweight candidate recovery then full KD/retrainingpaper family creation plus current NeMo versioned tooling

Several alternatives remain valid:

  • balanced fixed deletion avoids calibration scoring but encodes a spacing prior;
  • contiguous-block deletion reduces the search description but creates a large representational jump and was harmful in LayerDrop’s experiments;
  • one-shot ranking is cheap but ignores subset interaction;
  • iterative rescoring observes changed adjacencies but costs repeated calibration and can depend on the deletion path;
  • joint subset search can optimize the actual set but is combinatorial;
  • learned gates can optimize depth under a resource loss and belong to Loop 8 when their schedules and exact-budget projection are derived;
  • module-level depth pruning can remove attention or MLP independently but produces heterogeneous blocks and a different cache/runtime contract;
  • layer merging or collapse transfers deleted transformations into survivors and is not pure deletion.

The selection report must state which alternative was used and why its cost is included or excluded.

0x07J Export Reconstructs A New Layer Namespace

A portable export needs enough immutable state to replay the conversion rather than infer it from tensor-key gaps:

@dataclass(frozen=True)
class DepthPruningManifest:
    source_checkpoint_sha256: str
    source_depth: int
    target_depth: int
    keep_old: tuple[int, ...]
    old_to_new: tuple[tuple[int, int], ...]
    selection_method: str
    calibration_sha256: str
    score_reduction: str
    artifact_class: str
    recovery_recipe: str
    converter_version: str

    def validate(self):
        assert self.keep_old == tuple(sorted(set(self.keep_old)))
        assert self.target_depth == len(self.keep_old)
        assert tuple(old for old, _ in self.old_to_new) == self.keep_old
        assert tuple(new for _, new in self.old_to_new) == tuple(range(self.target_depth))
        assert self.artifact_class in {
            "physical_shorter_graph",
            "fixed_identity_gate_graph",
            "phase_specific_skip_graph",
        }

artifact_class is required because the same keep indices can describe different parameter residency, cache schemas, prefill work, and decode work.

For original blocks 0..N-1, define sorted keep indices and an explicit map:

keep_old = sorted(set(keep_old))
assert keep_old and keep_old[0] >= 0 and keep_old[-1] < old_depth

old_to_new = {old: new for new, old in enumerate(keep_old)}
new_to_old = list(keep_old)

A safer generic export constructs a new model from the new configuration and copies state by semantic ownership rather than relying on list mutation:

new_cfg = deepcopy(old_model.config)
new_cfg.num_hidden_layers = len(keep_old)
new_model = ModelClass(new_cfg)

copy_non_layer_state(new_model, old_model)
for new_idx, old_idx in enumerate(keep_old):
    copy_block_state(
        dst=new_model.layers[new_idx],
        src=old_model.layers[old_idx],
    )
    set_runtime_layer_id(new_model.layers[new_idx], new_idx)

manifest = {
    "source_depth": old_depth,
    "target_depth": len(keep_old),
    "new_to_old_layer": keep_old,
    "selection_method": selection_method,
    "calibration_digest": calibration_digest,
    "recovery_recipe": recovery_recipe,
}
save_model_config_state_and_manifest(new_model, manifest)

The migration closure includes:

StateRequired action
model configurationupdate depth and any architecture-specific layer metadata
state-dictionary keysrename each surviving layers.old.* namespace to layers.new.*
cache layer IDsallocate only kept slots and use new consecutive runtime IDs
adaptersmerge before deletion, delete removed-layer adapters, or rename survivor keys
quantization statediscard removed-layer scales and rename surviving per-layer metadata
optimizer/master weightsrebuild or map survivor parameter objects; never load deleted state by position
tied/shared statepreserve aliases rather than duplicating tensors during copy
tensor parallelismsurviving block tensor shapes can keep the same TP scheme, but shards and keys must be regenerated if serialization is layer-indexed
pipeline parallelismrepartition blocks; avoid empty or severely imbalanced stages
compiled/CUDA graphsinvalidate and recapture for the shorter call graph and cache schema
telemetrypreserve original-layer provenance while reporting new runtime layer IDs

The Loop 7 fixture compacts keep_old = [0, 2, 3, 5] as old_to_new = {0:0, 2:1, 3:2, 5:3}. It applies the same map to model-state prefixes, adapter prefixes, quantization prefixes, and cache slots. The resulting artifact ledger keeps three cases separate:

Synthetic artifactStored layersPrefill full-block callsPrefill K/V writesDecode full-block callsCache slotsParameters
physical shorter graph44444190
fixed identity gates in full graph64446235
ShortGPT-style prefill skip64666235

The parameter fixture uses P_fixed = 100 and per-block counts [20, 21, 22, 23, 24, 25]; its values are pedagogical rather than model measurements. The call/cache differences, however, encode the graph contracts from 0x07B and 0x07G exactly.

Token positions do not require a layer-offset correction. RoPE, ALiBi, and causal-mask positions index sequence tokens, not graph depth. What changes is the semantic “depth” at which a surviving block now operates. Learned layer-specific biases, per-depth scalars, adapters, cache IDs, and diagnostics must follow the original-to-new map.

Pipeline balance deserves an explicit equation. If stage receives kept layer set , its work is approximately

End-to-end pipeline time is constrained by the slowest stage and bubbles:

Deleting layers without repartitioning can leave one stage nearly empty while another remains the bottleneck. A lower total block count does not guarantee a proportional distributed speedup.

0x07K Recovery Must Match Deletion Severity And Release Objective

The immediate shortened model has two kinds of shift:

capacity shift
    -> fewer nonlinear transformations and fewer parameters

adjacency shift
    -> surviving block k receives a representation from an earlier original
       block than it saw during full-model training

LayerDrop attacks adjacency shift during original training. Post-training methods instead rely on low-sensitivity selection and optional recovery. Recovery choices change the claimed method cost:

no update
    -> cleanest test of score + deletion, weakest correction ability

LoRA / adapters
    -> low trainable state; base survivors remain frozen

continued pretraining
    -> all survivor weights can absorb changed adjacency and lost capacity

knowledge distillation
    -> teacher targets can constrain logits and selected intermediate states

architecture-search warmup
    -> short equal-budget recovery estimates which candidate is recoverable

M6.10 owns KL, hidden-state, relation, and on-policy distillation derivations. Depth pruning adds one mapping problem before using them. If student block originated from teacher block , an original-index mapping is

A hidden loss can compare survivor-aligned states,

or use uniformly spaced/learned teacher targets instead. These mappings answer different questions. Original-index alignment preserves lineage; uniform mapping spreads supervision across teacher depth; learned mapping adds state and optimization. The map must be declared rather than inferred from the new consecutive student index.

Recovery data must also be separated from calibration data. Ten or a thousand short calibration examples may be sufficient to estimate a ranking but are not automatically sufficient to restore language modeling, long-context, multilingual, code, or safety behavior. Evaluation must include domains not used to select the blocks.

0x07L Evaluation Must Compare Architecture, Selection, And Training Budget

A fair depth-pruning experiment needs more than original-versus-pruned quality:

ComparisonQuestion isolated
original full modeltotal quality and runtime change
same target depth trained from scratchvalue of inheriting survivor weights
balanced/every-other deletionvalue of the learned or calibrated selector
random subsets with several seedsrobustness of the selected order
width-pruned model at matched parametersdepth-versus-width artifact and runtime
same target architecture with equal recovery tokens/FLOPsvalue of initialization and objective rather than training budget
no-recovery versus LoRA/CPT/KDwhere quality restoration comes from

The runtime report should include:

parameter bytes and serialized bytes
resident weight and runtime-buffer memory
KV bytes per token and actual number of cache layers
prefill latency at declared prompt lengths and batch sizes
decode inter-token latency at declared cache lengths and batch sizes
throughput under scheduler and concurrency targets
pipeline/tensor-parallel placement and communication
compile/capture warmup and graph reuse
quality before recovery and after each recovery phase
calibration, search, recovery, and conversion cost

Depth pruning can preserve familiar GEMM dimensions, which may avoid some width-pruning alignment failures. It also reduces sequential block calls and layer-stack cache. But fixed embeddings/head work, host scheduling, sampling, communication, pipeline bubbles, and memory allocation remain. The measured speedup is workload-specific.

0x07M Layer-Pruning Failure Lab

FailureSilent causeObservable consequenceRepair
delete a residual block and claim exact equivalence to the original modelshape-valid identity confused with learned block functionunexplained quality loss called a converter bugcompare physical export first to the same gate-zeroed dense model
gate attention or MLP and call it whole-layer deletionremoval unit changedscore and runtime do not match declared methodgate complete or name the submodule unit
rank with one-block PPL and assume scores addchanged adjacencies create interactionjointly deleted subset degrades unexpectedlyvalidate the subset; optionally rescore after each removal
treat BI as output equalitycosine ignores positive rescaling and downstream losslow-BI layer can still affect logitscombine with ablation/held-out evaluation and state the geometric proxy
compute BI through a full token-token Gram matrixonly diagonal row pairs are neededquadratic temporary memoryuse row-wise cosine and a valid-token mask
include padding rows or silently replace zero-norm rowscalibration reduction undefineddata/batch formatting changes layer ordermask invalid rows and declare numerical fallback
train with LayerDrop and assume it selected the export subsetrobustness prior confused with inference selectorarbitrary fixed graphstate every-other/search/data-driven selection separately
remove a contiguous first or last region without validationendpoints and long adjacency jump ignoredsevere task or LM degradationprotect boundaries only as an explicit prior and validate alternatives
delete ModuleList entries but leave old config depthruntime graph and serialized architecture disagreefresh load expects missing tensors or reconstructs old depthrebuild from updated config and test fresh-process load
forget layer_idx and cache renumberingcache keys use original positionswrong cache writes, empty slots, or decode failureserialize old/new maps and allocate consecutive runtime slots
call prefill-only skipping a shorter modeloriginal layers still execute at decode and may still write K/Vtheoretical depth/KV saving does not appearname phase-specific runtime and measure each phase separately
reuse optimizer/LoRA/quant keys by new ordinal positionsurvivor identity changed after compactionstate from old layer 3 lands on old layer 4migrate by original identity, then rename explicitly
keep old pipeline partitiontotal depth changed but stage assignment did notempty stages or a new stragglerrepartition by measured per-layer cost and recapture graphs
compare architecture candidates only immediately after trimmingranking measures initial damageselected candidate loses after equal recoveryuse fixed-budget lightweight recovery before final selection
claim depth beats width from one paperhardware, batch, shape, and recovery regime omittednon-portable deployment choicebenchmark matched artifacts in the actual runtime
count as whole-model compressionembeddings and head are fixedparameter and byte saving overstateduse
claim layer deletion changes RoPE positionsgraph depth confused with token positionunnecessary or wrong positional remappingkeep token positions; migrate only layer-indexed state

0x07N Loop Boundary

Loop 7 has now followed depth pruning end to end:

define complete-block identity counterfactual
    -> B_l^z(h) = h + z_l(B_l(h)-h)

prepare or rank
    -> LayerDrop missing-layer training
    -> BI hidden-direction proxy
    -> one-block PPL ablation
    -> Taylor/magnitude proxies
    -> recovery-aware architecture search

select and validate
    -> preserve original order
    -> distinguish one-shot, iterative, balanced, and joint search
    -> test the complete selected subset

export
    -> construct shorter config and ModuleList
    -> copy survivors through an original-to-new map
    -> migrate cache, adapters, quantization, optimizer, TP/PP, and graphs

prove
    -> gate-zero parity
    -> fresh-process load and cached decode
    -> held-out quality before and after declared recovery
    -> prefill/decode/cache/distributed runtime measurement

Loop 8 returns to training-time selection, but at a different granularity. It owns relaxed and hard-concrete gates, movement scores, pruning schedules, resource penalties, exact-budget projection, mask freezing, and regrowth. The depth gate derived here supplies one possible structural unit; Loop 8 explains how such gates are optimized rather than assuming a post-training rank.

0x08 Training-Time Pruning Learns A Decision Process, Not Just A Score

The previous loops mostly began from a trained checkpoint, estimated which units could disappear, and then converted that decision into an artifact. Training-time pruning changes the order. A score or gate participates in the optimization itself, the active subnetwork changes while ordinary weights are still learning, and the eventual export decision may differ from the relaxed state used during training.

The owner question is therefore larger than “which score is highest?”:

How can binary keep/remove decisions receive useful gradients, how should the allowed budget change over training, how does a target parameter count differ from a target architecture, and which state must be frozen or converted before the result is a runnable pruned model?

This loop follows two methods deeply because they answer different versions of that question:

Movement Pruning
    -> learn one score per removable weight during task fine-tuning
    -> use a straight-through binary mask
    -> control a keep fraction with a warmup / cubic prune / cooldown schedule
    -> produce unstructured zeros unless a later representation changes

Sheared LLaMA
    -> learn hard-concrete gates over layers, residual coordinates,
       attention heads, and MLP intermediate coordinates
    -> constrain each gate family toward a pre-specified target shape
    -> deterministically project and physically slice a compact dense model
    -> continue pretraining with dynamically adjusted domain proportions

They should not be blended into a generic learned-mask method. Movement Pruning was evaluated primarily in a BERT transfer regime and its original unit is an individual weight. Sheared LLaMA starts from a decoder-only source LLM and searches a smaller structured target architecture. Their masks, schedules, optimizers, recovery budgets, and runtime artifacts differ.

The implementation statements below are revision-scoped. The Movement path is read against huggingface/block_movement_pruning commit c16cecafb9db2636250487064e00896a77772a3a: the schedule and cached global threshold live in masked_run_glue.py, STE decisions in binarizer.py, dense masked execution in masked_nn.py, and zero-valued export in bertarize.py. The Sheared path is read against princeton-nlp/LLM-Shearing commit b87218b566c73f6b246aaad3e4c42883f65a48a6: l0_module.py owns gate and constraint state, train.py owns minimizing versus maximizing optimizer groups, composer_llama.py and post_pruning_processing.py own structural conversion, and the callbacks and launch scripts own data-mixture and stage state. These are source traces, not claims that either repository was executed here.

Training-time pruning pipeline separating Movement Pruning score learning and cubic keep scheduling from Sheared LLaMA hard-concrete target-shape constraints, deterministic structural conversion, continued pretraining, and runtime proof

0x08A Four States Must Remain Separate

A training-time pruning implementation usually contains at least four kinds of state:

StateExampleWhat it provesWhat it does not prove
latent decision statemovement score or hard-concrete logit the optimizer can rank or relax removal decisionsan exact deployed budget
forward statebinary , sampled , or soft deterministic gatewhich paths contribute to this forward passa smaller checkpoint or faster operator
optimizer statemoments for , , , and constraint multipliershow the decision can continue movingthat a frozen mask cannot regrow through stale state
exported statezeroed dense weights, packed sparsity, sliced tensors, shorter layer listwhat the loader reconstructsthat the target runtime selects a faster kernel

These states can disagree without a software error. A hard-concrete sample can be fractional during training, a deterministic evaluator can force an exact number of zeros, and the checkpoint can still contain the full source weights plus every gate. Conversely, a movement mask can have an exact top- count while its dense F.linear call executes the original matrix shape.

A minimal manifest therefore records all four:

decision_state:
  family: movement_score | hard_concrete_loga
  granularity: scalar | block | head | mlp_channel | hidden | layer
forward_mask:
  mode: sampled | local_topk | global_threshold | deterministic_exact_count
  schedule_step: 1840
  achieved_count_by_group: {...}
optimizer_state:
  weight_group: present
  gate_group: present
  multiplier_group: present | absent
  frozen_at_step: null
export_state:
  form: dense_zero | packed_sparse | physically_sliced_dense
  source_to_target_indices: {...}
  target_config: {...}

The persisted contract should be typed rather than inferred from a collection of tensor names:

@dataclass(frozen=True)
class TrainingPruningManifest:
    method: str
    source_checkpoint_sha256: str
    decision_state_kind: str
    decision_granularity: tuple[str, ...]
    schedule_kind: str
    schedule_state: dict[str, float | int]
    optimizer_group_roles: tuple[str, ...]
    current_mask_sha256: str
    frozen_indices_sha256: str | None
    target_config_sha256: str | None
    data_mixture_sha256: str
    used_sample_ids_sha256: str
    export_form: str | None

schedule_state and data_mixture_sha256 are resumable state, not descriptive metadata. Two checkpoints with equal weights but different cached thresholds, multiplier signs, gate RNG state, domain proportions, or used-sample cursors do not continue the same pruning process.

0x08B Why Movement Scores Measure A Trajectory

Consider one masked linear output coordinate:

where is a trainable score matrix and is the fraction kept. The forward mask is discrete: the largest scores survive. Ordinary differentiation through that ordering operation gives zero almost everywhere, so hard Movement Pruning uses a straight-through estimator (STE): during the backward pass, the discrete mask is treated approximately as if its derivative with respect to the score were one.

Let

The STE score derivative is then

For comparison, the ordinary masked-weight derivative is

Define the gradient that the unmasked connection would have received as

The score update becomes

Unrolling score updates gives

This is the important derivation. A movement score is not , and it is not simply . It accumulates a signed, data-dependent, optimizer-dependent first-order signal along the fine-tuning trajectory.

Under gradient descent, the sign cases are:

Current weightDense gradientWeight update directionScore updateInterpretation
more positivenegativeincreasesmoves away from zero
more negativenegativeincreasesmoves away from zero
less positivepositivedecreasesmoves toward zero
less negativepositivedecreasesmoves toward zero

This interpretation assumes the simple SGD sign relation for exposition. Momentum, adaptive preconditioning, decoupled weight decay, clipping, mixed precision, and gradient accumulation change the realized weight trajectory. The score remains a training-process quantity; it cannot be reconstructed from the final checkpoint weights alone.

Movement Pruning Figure 1 comparing magnitude pruning against movement-based selection during MNLI fine-tuning

Source: Sanh, Wolf, and Rush, Movement Pruning: Adaptive Sparsity by Fine-Tuning, Figure 1. Cropped from the official paper. The scatter is source-reported for the studied BERT/MNLI setting; it is not a universal distribution of LLM weight motion.

The conceptual contrast is now precise:

magnitude score
    snapshot: |W_T|
    evidence: current parameter-space distance from zero

movement score
    trajectory: S_0 - eta_S * sum_t g_dense_t * W_t
    evidence: signed first-order pressure accumulated during adaptation

0x08C The Full Movement Schedule: Warmup, Cubic Pruning, Cooldown

The paper inherits gradual pruning from Zhu and Gupta. The notation is easy to misread because some pruning literature schedules sparsity, while the official Movement implementation passes the scheduled value to top- as the fraction retained. This article uses for keep fraction and for sparsity.

Let:

t_0       first pruning step after the initial warmup
t_1       end of the pruning transition and start of cooldown
kappa_i   initial keep fraction
kappa_f   final keep fraction

The complete schedule is

The corresponding sparsity is

Each interval has a distinct job:

initial warmup
    train W and S before forcing difficult competition

cubic pruning phase
    reduce the retained budget while scores and weights co-adapt

final cooldown
    hold the final mask budget while the surviving subnetwork adapts

For a toy schedule with , , , and :

StepNormalized pruning progressKeep fraction Sparsity
1000.001.00000.0000
3000.250.47970.5203
5000.500.21250.7875
7000.750.11410.8859
9001.000.10000.9000

The cubic curve changes the budget rapidly early in the pruning interval and approaches the final budget gently. It is not interchangeable with a linear schedule, and the cooldown is not ordinary learning-rate warmup under a new name. The Movement paper reports that a final cooldown helps in its studied high-sparsity setting; that is source-scoped empirical evidence.

A renderer-safe implementation is:

def movement_keep_fraction(step, warmup_end, prune_end,
                           initial_keep=1.0, final_keep=0.1):
    if step <= warmup_end:
        return initial_keep
    if step >= prune_end:
        return final_keep

    p = (step - warmup_end) / (prune_end - warmup_end)
    return final_keep + (initial_keep - final_keep) * (1.0 - p) ** 3

The official repository expresses and the cooldown length as multiples of its optimizer warmup-step argument, then returns both the threshold and a regularization coefficient. That is historical implementation behavior, not a mathematical requirement of Movement Pruning. A reproduction must log the actual step boundaries rather than record only “cubic schedule.”

0x08D Local Top-K, Global Top-K, And Exact-Budget Timing

For score group with entries, local hard movement keeps

entries inside every group. If every matrix is a group, each matrix receives the same density even if their score distributions differ.

Global top- concatenates score groups:

Global selection allows nonuniform layer density. It also introduces a distributed systems question: which process owns the concatenated scores, how often is the threshold recomputed, how are ties resolved, and is the threshold broadcast before the next forward?

The official Movement training script recomputes a global numeric threshold only every configured global_topk_frequency_compute steps and reuses the cached threshold between updates. Therefore:

at threshold-recompute step
    target count can be projected from the current global score order

between recomputations
    scores continue to move under a fixed numeric threshold
    achieved count can drift away from the nominal top-k count

This distinction is absent under a freshly recomputed local top- mask. “Uses global top-k” does not by itself prove that every training step has the exact global budget.

def global_threshold(score_tensors, keep_fraction):
    flat = torch.cat([s.reshape(-1) for s in score_tensors])
    keep = int(keep_fraction * flat.numel())
    # Stable tie policy should be explicit in a production implementation.
    return torch.topk(flat, k=keep, largest=True).values[-1]

if step % threshold_refresh == 0:
    tau = global_threshold(all_mask_scores, keep_fraction)
mask = score >= tau
log_achieved_count(mask)  # required even when tau is cached

The Movement paper reports local and global selection as similar in most of its studied regimes, with a more visible global advantage at extreme sparsity. That result does not establish local or global selection as universally preferable for decoder LLMs.

0x08E Hard Movement, Soft Movement, And Hard Concrete Solve Different Budget Problems

The Movement paper compares three trainable decision mechanisms:

MethodForward decisionGradient pathBudget controlExport implication
hard MovementSTE through top-direct count when top- is recomputedbinary zero mask
soft MovementSTE through thresholdindirect through and fixed binary zero mask
/ hard concretesampled stretched-concrete gatereparameterized relaxed gradientindirect expected , unless followed by projection/constraintsstochastic training gate, deterministic test/export gate

Soft Movement adds

to the task objective. Increasing encourages fewer scores to remain above the threshold, but it does not analytically guarantee a requested integer count. The penalty coefficient is a control parameter, not the final sparsity itself.

Hard concrete instead samples a relaxed variable, stretches it beyond , and clips it. It supplies a pathwise gradient and a closed-form expected nonzero probability. It is not “Movement with a smoother top-k”: its decision distribution and budget semantics are different.

The official block_movement_pruning repository also supports score tensors at a block granularity and expands each block decision over several scalar weights. That is an implementation extension. The classic paper’s central Movement derivation and reported transfer experiments use unstructured weight masks; block support should not be retroactively described as the original paper’s only unit.

0x08F A Code-Level Movement Step

The official implementation uses separate optimizer groups for ordinary model parameters and mask_scores. Its masked linear path computes a mask, expands block masks if configured, multiplies the dense weight, and calls an ordinary dense linear operator.

def hard_movement_step(model, batch, step, optimizer, schedule):
    keep = schedule(step)

    for layer in model.masked_linears():
        layer.mask = straight_through_topk(layer.mask_scores, keep)

    loss = model(batch).loss       # F.linear(x, mask * weight, bias)
    loss.backward()
    optimizer.step()               # distinct LR groups for W and S
    optimizer.zero_grad(set_to_none=True)

At tensor level, one route is:

x                    [B, T, d_in]
W                    [d_out, d_in]
S                    [d_out, d_in] or block-grid shape
M = topk_STE(S)       same shape as S
expand(M)             [d_out, d_in] when block scores are used
W_masked = M * W      [d_out, d_in]
y = linear(x,W_masked)[B, T, d_out]

The backward state needs a more careful trace:

active weight
    task gradient reaches W
    score gradient also reaches S through STE

masked weight
    ordinary task-gradient path to W is multiplied by M=0
    score gradient can still reach S through STE
    S may cross the selection boundary and reactivate the connection

optimizer caveat
    stale momentum, master weights, or decoupled decay may still alter stored W
    even when its current task gradient is zero

Thus Movement supports score-based regrowth, not necessarily continuous task-gradient training of the masked weight value. When a connection returns, its stored may be stale relative to continuously active neighbors. The checkpoint must preserve both score and optimizer state if training is to resume with the same dynamics.

optimizer_groups = [
    {"params": ordinary_weights, "lr": weight_lr, "weight_decay": wd},
    {"params": mask_scores, "lr": score_lr, "weight_decay": 0.0},
]

checkpoint = {
    "model": model.state_dict(),       # includes W and S while pruning
    "optimizer": optimizer.state_dict(),
    "step": step,
    "keep_schedule": schedule.state_dict(),
    "global_threshold": cached_tau,
}

The paper’s task fine-tuning setup also freezes embeddings and prunes the fully connected encoder weights in the studied BERT variants. Distillation is an optional additional signal in those experiments; Movement’s score derivation does not itself require a teacher.

0x08G Freeze, Regrow, And Export Are Separate Policies

During the pruning transition, allowing scores to move lets a previously masked connection re-enter. At some point a deployment pipeline usually freezes the topology. That decision must name which state stops changing:

Policy updatesscore/gate updatesmask changesUse
dynamic movementyes for active weightsyesyesjoint selection and adaptation
mask-frozen recoveryyes for retained weightsnonostabilize final subnetwork
full freezenononoconversion/evaluation oracle
regrowth-enabled sparse trainingmethod-specificyesyesrecover from earlier choices

Freezing only the visible mask tensor is insufficient if the next forward recomputes it from still-trainable scores. Freezing scores is insufficient if the optimizer can still apply stale state. A robust transition removes gate parameters from the optimizer, clears incompatible moments, persists the chosen indices, and asserts that repeated forwards reproduce the same mask.

def freeze_movement_topology(model, keep_fraction):
    manifest = {}
    for name, layer in model.named_masked_linears():
        mask = exact_topk_mask(layer.mask_scores.detach(), keep_fraction)
        layer.register_buffer("frozen_mask", mask)
        layer.use_frozen_mask = True
        layer.mask_scores.requires_grad_(False)
        manifest[name] = torch.nonzero(mask, as_tuple=False).cpu()
    rebuild_optimizer_without_mask_scores(model)
    return manifest

The export then chooses a representation:

dense zero export
    save W_export = M * W at original shape
    easiest parity path; ordinary dense kernels usually do original work

general sparse export
    pack retained values and row/column metadata
    needs compatible sparse operators and shape/workload proof

N:M projection
    re-project within each hardware-defined local group
    changes the learned global/local mask unless the training unit already matched N:M

structured slicing
    impossible for arbitrary scalar Movement masks without a new grouping rule

The official Movement repository explicitly uses dense PyTorch linear calls for its masked model and reports no significant inference speedup from that path. Its zeroed checkpoint and sparse-storage examples should be read as artifact demonstrations, not proof that a standard dense GEMM skips zeros.

0x08H Sheared LLaMA Targets A Shape, Not Merely A Sparsity Ratio

Movement asks which scalar weights survive a budget. Sheared LLaMA starts from a source Transformer with

for layer count, residual width, attention-head count, and MLP intermediate width, then declares a target

That target is stronger than a total parameter count. Two models can contain the same number of parameters while one has an irregular per-layer shape and the other has regular dense GEMMs compatible with an existing runtime.

The paper learns four principal gate families:

GateShapeScopePhysical dependency closure
global layer setcomplete attention+MLP blocks and layer-indexed state
shared across the networkembeddings, norms, every residual producer/consumer, output head
per layerlocalQ/K/V head outputs and O input slices
per layerlocalgated/up rows and down columns

The paper’s conceptual layer gate controls complete blocks. The official code also contains attention-layer and MLP-layer mask helpers, while the released pruning script selects [head, intermediate, layer, hidden]. The exact enabled gate list is configuration state, not something a reader should infer from the paper figure alone.

Sheared LLaMA Figure 2 showing global hidden and layer masks plus per-layer head and intermediate masks producing a pre-specified dense target shape

Source: Xia et al., Sheared LLaMA: Accelerating Language Model Pre-training via Structured Pruning, Figure 2. Cropped from the official paper. The diagram establishes the method’s target-shape and gate-family contract.

The paper’s source and target examples make the shape constraint concrete:

ModelLayersHiddenIntermediateHeadsHead dimension
LLaMA2-7B source3240961100832128
Sheared-LLaMA-1.3B target242048550416128
Sheared-LLaMA-2.7B target322560691220128

These are source-reported configurations, not generally optimal Transformer ratios. The target inherits the source head dimension in this construction, so rather than choosing head count independently.

0x08I Hard Concrete Creates A Differentiable Gate With Atoms At Zero And One

For one gate logit , draw

The binary-concrete relaxation is

Stretch it from to with :

Then clip:

The stretch matters. Values below zero collapse exactly to the atom at zero; values above one collapse to the atom at one; values in between remain differentiable relaxed gates. The reparameterized sample lets gradients reach through before clipping.

The probability that the gate is nonzero is

Derivation: iff , which is equivalent to . Applying the logistic inverse to that threshold and then the binary-concrete CDF yields the closed form above. This expected open probability lets the training objective estimate counts without sampling every possible discrete architecture.

The Sheared LLaMA paper reports

The historical official code uses bounds but sets temperature to . This is a paper/code difference that a reproduction manifest must resolve explicitly. The code also initializes gate logits around 5, clamps them to , samples stochastic gates while training, and uses a deterministic path in evaluation.

def hard_concrete(log_alpha, beta, gamma=-0.1, zeta=1.1):
    u = torch.rand_like(log_alpha).clamp_(1e-6, 1 - 1e-6)
    logistic_noise = torch.log(u) - torch.log1p(-u)
    s = torch.sigmoid((logistic_noise + log_alpha) / beta)
    return (s * (zeta - gamma) + gamma).clamp_(0.0, 1.0)

def expected_open(log_alpha, beta, gamma=-0.1, zeta=1.1):
    return torch.sigmoid(log_alpha - beta * math.log(-gamma / zeta))

0x08J From Expected Gates To An Exact Target Architecture

An penalty controls the expected number of active gates indirectly. It does not say that every layer has exactly heads or intermediate coordinates. Sheared LLaMA instead constrains each gate family to match the declared target shape.

For heads in layer , define the count residual

The paper writes a linear-plus-quadratic constrained term:

Analogous residuals enforce:

The joint objective is a min-max problem:

The model weights and gate logits minimize language-model loss plus constraint pressure. The multipliers ascend so persistent count error becomes more expensive. In the official optimizer builder, ordinary parameters use the model learning rate, gate parameters use lag_lr, and multiplier parameters use negative lag_lr; a minimizing optimizer thereby performs ascent on the multipliers.

groups = [
    {"params": theta,       "lr": model_lr},
    {"params": gate_logits, "lr": lag_lr},
    {"params": multipliers, "lr": -lag_lr},  # ascent
]

for batch in train_loader:
    z = {name: gate.sample() for name, gate in gates.items()}
    logits = model(batch.input_ids, gates=z)
    lm_loss = cross_entropy(logits, batch.labels)

    constraint_loss = 0.0
    for family in target_families:
        expected_count = expected_open_count(gates[family])
        target_count = scheduled_target_count(family, step)
        error = expected_count - target_count
        constraint_loss += lambda1[family] * error
        constraint_loss += lambda2[family] * error.square()

    (lm_loss + constraint_loss).backward()
    optimizer.step()
    clamp_gate_logits_()

This objective still operates on expected or relaxed state during training. Exact export requires a deterministic projection.

0x08K Sheared’s Schedule Is Not Movement’s Cubic Schedule

The official Sheared LLaMA module linearly warms the target sparsity for each gate family:

The corresponding target count is

This schedule changes the constraint target used by the Lagrangian. It does not itself select the bottom gates, and it is separate from the ordinary model learning-rate warmup.

ScheduleControlled quantityCurveDecision rule
Movementretained fraction constant / cubic / constanttop- score mask or threshold
Shearedtarget sparsity/count residuallinear warmup then fixedhard-concrete gates under constrained objective
ordinary optimizerlearning rate for weights/gatesimplementation-specificupdate magnitude, not architecture count

The released pruning script for the source-reported 7B-to-1.3B/2.7B runs uses 3,200 pruning batches, 10% ordinary LR warmup, and a 640-batch Lagrangian warmup. Those values describe that experiment and code release; they are not generic defaults for all target shapes.

0x08L Deterministic Projection And Physical Conversion

At evaluation, the official gate module computes a soft deterministic score from each logit. When target dimensions are configured, it then sets exactly the lowest

scores to zero within each relevant row or global gate vector. Surviving scores are not necessarily one.

def deterministic_exact_target(log_alpha, target_count, beta):
    score = torch.sigmoid(0.8 * log_alpha / beta)  # official-code policy
    zero_count = score.numel() - target_count
    if zero_count:
        prune = torch.topk(score, k=zero_count, largest=False).indices
        score[prune] = 0.0
    return score

That means export has two operations, not one:

  1. absorb any surviving fractional gate value into the adjacent weight;
  2. physically remove zero-gated rows, columns, heads, channels, and layers.

For an intermediate gate in a gated MLP, for example:

where contains nonzero gate positions. The exact location at which a soft gate is absorbed depends on where it was applied in the forward graph. A converter cannot multiply every coupled matrix by the same vector without checking the algebra.

The historical official converter performs the dependency closure in code:

hidden gate
    multiply/slice token embeddings
    slice final norm and output-head input
    slice Q/K/V input axes and O output axis
    slice MLP input/output residual axes

head gate
    multiply selected head output where required
    slice Q/K/V output coordinates
    slice O input coordinates

intermediate gate
    absorb gate into the MLP path
    slice gate/up output rows
    slice down input columns

layer gate
    remove complete block state
    renumber surviving layer keys and runtime layer indices

The official post-pruning script loads the source-sized checkpoint with its gate module, requests deterministic masks, calls physical prune_params, removes the module, and saves the converted model. Before conversion, the pruning checkpoint can still contain the full source parameters plus mask state. Its file size is not the target model size.

A stronger conversion oracle is:

z_eval = gates.deterministic_exact_target()
y_masked = source_model(x, gates=z_eval)

target_model, index_map = physically_slice(source_model, z_eval)
y_sliced = target_model(x)

assert target_model.config == declared_target_config
assert_close(y_sliced, y_masked, atol=conversion_tolerance)

save(target_model, index_map, declared_target_config)
fresh = load_in_new_process(export_dir)
assert_close(fresh(x), y_sliced, atol=reload_tolerance)

The fresh target configuration is executable state. A state dictionary with smaller tensors cannot teach an ordinary source-model constructor which layers, heads, widths, or intermediate dimensions it should instantiate.

0x08M Dynamic Batch Loading Changes The Evidence Stream

After pruning, continued pretraining is not an incidental cleanup step in Sheared LLaMA. The paper treats it as the second major stage and changes the domain mixture according to validation deficits.

For domains , let be the current sampling proportion, the current validation loss, and a target reference loss. The paper’s Algorithm 1 defines

then performs exponential reweighting:

A domain farther above its reference loss receives more probability. A domain already at or below the reference receives no positive deficit under the paper algorithm.

def paper_dynamic_mixture(current, val_loss, reference):
    deficit = torch.clamp(val_loss - reference, min=0.0)
    logits = torch.log(current) + deficit
    return torch.softmax(logits, dim=0)

The official callback differs in two visible ways:

def released_callback_mixture(current, val_loss, reference, floor=1e-4):
    diff = val_loss - reference          # no max(..., 0) clamp
    updated = torch.softmax(torch.log(current) + diff, dim=0)
    return (1.0 - floor) * updated + floor / updated.numel()

Therefore domains below reference can be downweighted relative to others, and a small uniform floor prevents complete disappearance. This is a meaningful paper/code distinction, not formatting noise. A reproduction should identify which update it runs.

The callback is applied during both pruning and continued pretraining. The CPT script starts from the final pruning-stage proportions rather than resetting to the original corpus mixture. Resume state also has to preserve used sample IDs; otherwise a resumed data stream can replay examples while appearing to resume the same optimizer step.

Sheared LLaMA Figure 6 showing dynamic domain weights over continued pretraining and cumulative source-reported domain usage

Source: Xia et al., Sheared LLaMA, Figure 6. Cropped from the official paper. The trajectories and percentages are source-reported for the paper’s RedPajama-domain experiment, not a recommended universal mixture.

Dynamic loading and mask learning are separate mechanisms:

gate optimizer
    changes which model substructures are likely to survive

dynamic loader
    changes which domains generate the next gradients

interaction
    changed data proportions change both ordinary weight gradients
    and the evidence received by pruning gates

The default reference losses in the paper are predicted per domain using a scaling-law fit:

The authors also study source-model validation losses as references. Both are source-scoped policy choices. The scaling-law fit is not ground truth for a hypothetical target architecture, especially when fitted from few model sizes.

0x08N The Full Sheared Training And Release State Machine

The complete method is easier to audit as a state machine:

# Stage 0: declare source and target architecture.
target = Shape(layers=L_T, hidden=d_T, heads=H_T, intermediate=m_T)
gates = make_gate_families(source_shape, target)

# Stage 1: targeted structured pruning.
for step, batch in pruning_stream:
    z = sample_hard_concrete(gates)
    lm_loss = source_model(batch, gates=z).loss
    lag_loss = target_shape_constraints(gates, target, step)
    backward_and_minmax_step(lm_loss + lag_loss)

    if step % eval_interval == 0:
        domain_losses = evaluate_each_domain(source_model, gates)
        pruning_stream.update_mixture(domain_losses, reference_losses)

# Stage 2: deterministic exact-count projection and conversion.
z_export = deterministic_target_masks(gates, target)
compact = absorb_soft_gates_and_slice(source_model, z_export, target)
remove_gate_modules(compact)
fresh_load_and_parity_check(compact)

# Stage 3: continued pretraining of the compact model.
for step, batch in continued_pretraining_stream:
    loss = compact(batch).loss
    loss.backward()
    optimizer.step()
    if step % eval_interval == 0:
        continued_pretraining_stream.update_mixture(
            evaluate_each_domain(compact), reference_losses
        )

# Stage 4: deployment proof.
measure_quality_before_and_after_cpt()
measure_prefill_decode_memory_and_throughput(compact)
ship_target_config_index_map_and_runtime_manifest()

The paper’s source-reported cost split is intentionally asymmetric:

StageTraining budgetWeight LRGate/multiplier LRToken batchStepsReported throughput
targeted pruning0.4B tokens1.0131K3,20015K tokens/s
CPT, 1.3B target50B tokensnone1M51,200145K tokens/s
CPT, 2.7B target50B tokensnone1M51,20077K tokens/s

The paper reports pruning as roughly five times slower than ordinary LM training in its implementation because it carries source-model computation, stochastic gates, constraint state, and extra optimization. It allocates a small pruning budget because masks converge quickly, then spends most tokens recovering the compact model. These numbers are historical source-reported measurements on the paper’s hardware/software stack, not a capacity-planning estimate for a current cluster.

The accounting lesson is general:

Reporting only the 0.4B pruning stage would hide the dominant 50B-token recovery budget. Reporting only final inference speed would hide the source model and gate-state training cost.

0x08O Movement Pruning And Sheared LLaMA By Contract

ContractMovement PruningSheared LLaMA
owner problemtask-adaptive sparse weight selectionsource LLM to pre-specified compact dense architecture
original removal unitscalar fully connected weightlayer, global residual coordinate, local head, local MLP coordinate
latent statescore hard-concrete per structure
gradient estimatorSTE through top-/thresholdreparameterized hard-concrete sample
budgetscheduled keep fraction or indirect threshold penaltyper-family target-shape constraints
schedulewarmup, cubic keep transition, cooldownlinear target-sparsity warmup plus ordinary LR schedule
exactness pointexact count when top- is freshly projecteddeterministic lowest-score projection to target counts
regrowthscores can cross selection boundarygate probabilities can move before deterministic export
training modelmasked source-shaped modelgated source-shaped model
initial artifactdense weights multiplied by binary maskfull source checkpoint plus gates before conversion
release conversionzero/pack according to sparse runtimeabsorb residual gate values and slice coupled axes/layers
recoverycontinued task fine-tuning; optional distillation in paperdedicated continued pretraining stage
data adaptationtask dataset in paperdynamic per-domain loading during pruning and CPT
default runtime shapeoriginal dense shape unless packed operator changes itsmaller regular dense target shape after conversion
evidence boundaryBERT transfer tasks and studied sparsity regimesLLaMA2-7B to paper target shapes and training stack

The common principle is only that removal decisions are learned jointly with model adaptation. Everything after that statement must remain method-specific.

The Loop 8 dependency-free fixture makes the distinctions executable. It does not train either paper model; it evaluates the exact equations on small fixed vectors and rejects one-field contract substitutions:

ContractDeterministic fixture resultWhat the result prevents
Movement keep schedulesteps 0,2,4,6,8,10 retain 1,1,0.472222,0.277778,0.25,0.25replacing warmup/cubic/cooldown with one linear ramp
masked backwarda masked weight has zero ordinary task gradient while its score receives a nonzero STE gradientclaiming score regrowth means masked weights train normally
local versus global top-ktwo local groups retain [2,2]; global selection retains [4,0]calling equal density and global competition equivalent
cached global thresholdexact count 4 at refresh drifts to 6; fresh top-k returns 4claiming cached-threshold steps meet an exact integer budget
hard-concrete stateexpected open count differs from deterministic export count; projection retains exactly 3 soft survivorstreating relaxed expectation as the exported architecture
multiplier optimizera minimizing optimizer with negative multiplier LR matches explicit ascentsilently descending every Sheared parameter group
conversionfive gated MLP coordinates slice to three with masked-versus-sliced maximum error below 1e-10dropping surviving gate scales or slicing only one coupled tensor
dynamic loadingclamped paper deficits and released raw-difference-plus-floor updates produce different normalized mixturesclaiming paper and released callback equations are identical
lifecycle accounting0.4B + 50B = 50.4B source-reported training tokens before conversion/evaluation costsreporting only the mask-learning stage as compression cost

The fixture also round-trips domain proportions, used-domain IDs, and the evaluation cursor. That is a serialization invariant only. It does not prove the historical distributed loader, optimizer, model, or recovery run.

0x08P Training-Time Pruning Failure Lab

FailureSilent causeObservable consequenceRepair
call the final weight displacementscore trajectory confused with movement ranking cannot be reproducedpersist score state and derive under the actual optimizer
schedule “90%” without saying keep or sparsitytop- threshold semantics invertedretain 90% when intending to retain 10%log both and
omit warmup/cooldown boundariescubic phase treated as complete schedulereproduction prunes at different optimizer stateserialize exact , total steps, and LR schedule
cache a global threshold and claim exact top- every stepscores move between threshold refreshesachieved count driftslog achieved count and recompute/project at required cadence
assume masked keeps learning normallySTE score gradient confused with weight gradientregrown values are staledistinguish task gradient to from score gradient to
freeze the visible mask but retain score optimizer statemask is recomputed on next forwardtopology changes during recoveryfreeze scores, remove optimizer group, persist exact indices
load a movement checkpoint and expect sparse speedupdense linear(mask * W) remainslatency unchanged or worseconvert to supported packed format and prove kernel dispatch
call hard Movement, soft Movement, and equivalentcount projection and relaxed penalties conflatedfinal sparsity differs from requeststate decision, gradient, and budget contracts separately
use total target parameters for Sheared LLaMAarchitecture geometry omittedirregular or unsupported runtime shapesconstrain layers, hidden width, heads, and MLP width explicitly
treat expected open count as exact export countrelaxed objective confused with discrete artifactwrong target dimensionsrun deterministic exact-count projection and validate config
discard nonbinary surviving gates before slicingrelaxed scale not absorbedconverter parity failsmultiply surviving scale into the correct adjacent weight first
slice only one tensor for a hidden/head/MLP gatedependency closure ignoredshape mismatch or changed functionreuse Loops 5-7 coupled-axis closure and parity test
ship source-sized checkpoint plus gates as the targettraining state confused with artifactfile and loader still instantiate source modelphysically slice, remove gate module, and save target config
optimize all Sheared parameter groups with positive LRmax variables accidentally descendconstraints fail to enforce targetuse explicit ascent or audited negative-LR multiplier group
cite paper while running code silentlypaper/code drift hiddengate distribution differspin and record the chosen implementation contract
implement paper dynamic loading but claim code parityclamp and floor differences omitteddomain proportions divergetest paper and released callback equations separately
reset data proportions at CPTpruning-stage loader state discardedrecovery data trajectory changesinitialize CPT from final pruning proportions when reproducing paper
resume without sample-ID stateoptimizer resumes but data stream repeatstoken accounting and validation trajectory driftcheckpoint loader proportions and used-domain IDs
report only pruning tokensCPT dominates but is hiddencompression cost understatedreport prune, convert, CPT, evaluation, and deployment costs
transfer source-reported throughput to new hardwareimplementation and cluster scope omittedinvalid capacity forecastrerun end-to-end training and serving measurements

0x08Q Loop Boundary

Loop 8 has now followed training-time pruning from differentiable decision to release candidate:

Movement Pruning
    -> derive STE score gradient
    -> accumulate signed first-order trajectory
    -> warmup / cubic keep schedule / cooldown
    -> distinguish local, global, cached-threshold, and penalty budgets
    -> preserve score and optimizer state while regrowth is allowed
    -> freeze exact indices and choose a real sparse representation

Sheared LLaMA
    -> declare target layers, hidden width, heads, and MLP width
    -> sample hard-concrete structured gates
    -> minimize model/gate state and maximize constraint multipliers
    -> linearly warm target sparsity by gate family
    -> deterministically project exact target counts
    -> absorb soft survivors and slice every coupled tensor
    -> continue pretraining with checkpointed dynamic domain loading

release proof
    -> masked-versus-converted parity
    -> fresh-process target-config load
    -> quality before/after recovery
    -> measured artifact bytes, memory, prefill, decode, and throughput

Section 0x09 compares the complete LLM pruning pipelines by these contracts. It does not rank methods by one benchmark table. It owns the cross-paper matrix for LLM-Pruner, Sheared LLaMA, SliceGPT, Shortened LLaMA, ShortGPT, and Minitron: removal unit, evidence, target control, training/calibration data, conversion, recovery, artifact, and runtime claim.

0x09 A Pruning Paper Is A Pipeline Contract, Not A Method Label

The six LLM methods developed in Loops 5-8 are often placed in one benchmark table because every final model has fewer parameters. That shared endpoint is not enough to make the methods interchangeable. They begin from different source checkpoints, remove different graph objects, spend radically different amounts of data and training compute, export different runtime graphs, and measure speed under different workloads.

Owner question: what must be recorded, and what must be held constant, before two pruning papers can answer the same engineering question?

The answer is a pipeline contract. A score is one field in that contract, not the method itself. For this comparison, define

where:

FieldQuestion it fixes
Which exact source checkpoint and architecture are compressed?
What graph object can be removed: block, head, MLP coordinate, residual direction, or a coupled group?
Which examples, sequence lengths, masks, and reductions create pruning evidence?
Is evidence magnitude, Taylor, ablation PPL, hidden geometry, activation energy, or a learned gate?
Is the target a removed-parameter ratio, exact depth, exact per-axis shape, slicing schedule, or architecture search space?
When and how is a discrete topology frozen?
How are gates, indices, rotations, and source tensors converted into an executable graph?
Is recovery absent, LoRA, full continued pretraining, distillation, or a staged combination?
What is actually released: mutated source object, custom compressed module, or fresh standard target configuration?
Which quality suite, hardware, runtime, dtype, batch, sequence shape, and serving phase support the claim?

Two papers are directly comparable only on fields that either match or are deliberately controlled. A matched final parameter count does not repair a mismatch in source model, recovery budget, or runtime workload.

The comparison record is therefore executable state, not an informal row in a spreadsheet:

@dataclass(frozen=True)
class PaperPipelineRecord:
    method_id: str
    paper_revision: str
    source_checkpoint: str
    source_geometry: dict[str, int]
    removable_unit: str
    evidence_manifest: dict[str, object]
    score_or_search: str
    target_contract: dict[str, object]
    freeze_rule: str
    conversion_contract: str
    immediate_artifact: str
    recovery_contract: dict[str, object]
    release_artifact: str
    evaluation_manifest: dict[str, object]
    workload_manifest: dict[str, object] | None

@dataclass(frozen=True)
class VectorBudget:
    evidence_tokens: int
    evidence_forwards: int
    evidence_backwards: int
    search_candidates: int
    recovery_tokens: int
    teacher_forwards: int
    trainable_parameters: int
    optimizer_state_bytes: int
    wall_time_seconds: float | None
    hardware_manifest: str | None

The nullable fields matter. An unavailable runtime measurement is None, not zero. A source-reported three-hour recovery run is not silently converted into a portable compute budget when sequence count, token count, kernel stack, and hardware utilization are missing. The record also separates the immediate post-conversion artifact from the recovered release, because those checkpoints answer different causal questions.

The comparison source surface is immutable. LLM-Pruner is consumed from the Loop 5 2305.11627v3 paper and commit-pinned code records; SliceGPT from the Loop 6 2401.15024v2 paper and 6b12cdee... code records; ShortGPT, Shortened LLaMA, and Minitron from the Loop 7 revision/code records; and Sheared LLaMA from the Loop 8 2310.06694v2 paper and b87218b... implementation records. Loop 9 compares those frozen contracts. It does not replace them with current repository heads, a new framework execution, or memory-based summaries of the papers.

0x09A Normalize Every Paper Into The Same State Machine

The common state machine is longer than score -> prune -> benchmark:

source checkpoint
    -> evidence acquisition
    -> candidate selection or topology search
    -> discrete topology freeze
    -> physical graph conversion
    -> optional recovery
    -> release packaging
    -> quality and runtime validation

Each arrow can change the model. Evidence acquisition may run only forwards, may require gradients, or may jointly train gates and weights. Topology search may choose coordinates inside a fixed target shape or compare several target architectures. Conversion may simply renumber surviving blocks, may slice a dependency closure, or may introduce executable shortcut matrices. Recovery may consume no data, 50K instruction examples, billions of language-modeling tokens, or teacher forwards over a large training run.

Paper-by-contract comparison of LLM-Pruner, Sheared LLaMA, SliceGPT, ShortGPT, Shortened LLaMA, and Minitron across evidence, selection, graph conversion, recovery, released artifact, and runtime proof

A reported number must attach to one state. For example, quality after pruning can mean the newly sliced graph before any update, the same graph after LoRA, or a compact model after 50B continued-pretraining tokens. Calling all three results pruned quality hides the causal difference.

0x09B The Six Complete Pipelines

The following table records the main paper path rather than every ablation. All data volumes, runtimes, and model names are source-reported and scoped to the corresponding paper.

Paper pathEvidence and target decisionPhysical conversionRecovery and released stateEvidence boundary
LLM-Prunerdiscover coupled dependency groups; use 10 calibration samples of length 128 from BookCorpus for LLaMA/Vicuna or DailyDialog for ChatGLM; rank grouped structures with magnitude/Taylor variants under an approximate removed-parameter ratioprune dependency-closed head/MLP or residual-channel groups; resulting widths can be heterogeneous; official paths include Python-object serialization and later architecture-specific supportoptional LoRA on about 50K cleaned Alpaca examples for two epochs; paper reports about three hours on one GPU; merge adapters into the structurally smaller modelLLaMA-7B/13B, Vicuna-7B, ChatGLM-6B; paper Table 3 times a 64-token inference input on one A5000
Sheared LLaMAjointly train hard-concrete layer, hidden, head, and MLP gates on RedPajama under exact target-family constraints; paper allocates 0.4B pruning tokens and chooses 1.3B/2.7B target shapesdeterministically retain exact counts, absorb surviving gate scales, slice every coupled tensor, remove gate modules, renumber layers, and load a fresh target configurationfull continued pretraining for 50B tokens with dynamic domain loading; released artifact is a regular smaller dense model at the declared target shapeLLaMA2-7B source; paper compares targeted and nonuniform pruning generation speed on one A100 80GB and reports downstream results for its data/training stack
SliceGPTcollect boundary activations, form uncentered Gram matrices, compute PCA bases, and apply a constant or configured residual-width slicing schedule; main experiments use WikiText-2 or Alpaca calibration, commonly 1,024 sequences of length 2,048 for LLaMA2fuse normalization state, rotate each boundary, delete low-energy directions, and install dense square or rectangular residual shortcut transforms; custom compressed modules and a slicing config are executable architecture stateslicing can be evaluated without weight updates; optional LoRA recovery uses about 1K WikiText-2 or 5K Alpaca sequences; released state includes custom layer and shortcut parametersOPT, LLaMA2, and Phi-2; paper reports batch-1, length-128 generation timing on A100/RTX6000 and max-throughput sweeps on H100, with explicit runtime caveats
ShortGPTrun an unlabeled PG19 calibration stream, collect block input/output hidden states, rank complete blocks by Block Influence, and remove the lowest scores to a requested depth ratiodirect path deletes entries from the layer stack; a distinct repository generation path performs phase-dependent skipping and is not the same shortened graphno recovery is required by the core method; a separate paper experiment replaces removed blocks with lightweight gated MLPs and trains for 50B tokens, changing both architecture and budgetLLaMA2, Baichuan2, RWKV, and Mamba experiments; GPTQ throughput table and official phase-specific path must not be treated as one universal runtime artifact
Shortened LLaMAuse 10 BookCorpus calibration samples; rank whole blocks with magnitude, Taylor, protected-boundary variants, or one-block PPL; remove complete blocks once to meet a target parameter sizeconstruct a fresh shorter configuration, copy surviving blocks, and remap old layer indices to consecutive target indicescompare no update, LoRA on 50K Alpaca examples, full CPT on SlimPajama, and CPT then LoRA; severe pruning conclusions depend strongly on the selected recovery pathLLaMA-7B and Vicuna-7B/13B; paper timing uses 12 input and 128 output tokens, batch 1, default Transformers, on H100 80GB and RTX3090 24GB
Minitronuse 1,024 calibration samples; rank width axes by activation evidence and depth by PPL or BI; enumerate feasible depth/width architecture tuples under a parameter budget; apply about 1.8B tokens of equal lightweight recovery to each candidate before final rankingphysically trim embedding, attention, MLP, and optional depth axes; an iterative family path prunes the already recovered 8B model to build the 4B modelfull retraining is distillation-centered; the paper reports 94B training tokens for released Minitron models and recommends logits-only or added hidden/embedding targets according to depth changeNemotron-4 15B source and Nemotron/Minitron family; paper reports quality and training evidence, but no direct end-to-end latency or throughput table for the released models

This matrix prevents six common substitutions:

ten calibration examples       != 50K recovery examples
50K instruction examples       != 50B pretraining tokens
one fixed target shape         != architecture search
deleting layer objects         != phase-specific layer skipping
custom dense shortcuts         != stock smaller Transformer
matched parameter count        != matched runtime graph

0x09C A Compression Ratio Does Not Identify The Compressed Graph

Let the source architecture geometry be

where contains per-layer overrides, endpoint policy, packed layouts, and custom shortcut state. The removed-parameter ratio is only

It does not reveal which coordinate of changed. For a decoder with fixed embedding/head state and approximately uniform blocks,

The four attention terms correspond to Q, K, V, and O under a GQA-style layout; model-specific biases, norms, tied endpoints, and packings must be counted separately. The equation exposes why equal can create unlike systems:

  • reducing removes complete sequential block calls and one persistent K/V cache layer per removed block while preserving surviving GEMM shapes;
  • reducing shrinks three MLP matrix axes but leaves attention, layer count, and K/V layer count unchanged;
  • reducing can shrink Q/O and query attention work without changing GQA K/V width;
  • reducing shared residual width changes embeddings, projections, MLPs, endpoints, and possibly shortcut matrices with linear and quadratic terms;
  • a hybrid target changes several terms and may preserve runtime alignment better than an arbitrary one-axis count.

Therefore a fair report includes the geometry delta, not only 20% pruned:

{
  "source": {"layers": 32, "hidden": 4096, "q_heads": 32, "kv_heads": 32, "mlp": 11008},
  "target": {"layers": 24, "hidden": 4096, "q_heads": 32, "kv_heads": 32, "mlp": 11008},
  "parameter_reduction": "measured from serialized tensors",
  "custom_shortcuts": false,
  "heterogeneous_layers": false
}

Replacing the target with hidden=3072, heterogeneous per-layer heads/MLPs, or per-boundary SliceGPT widths can reach a similar parameter count while changing kernel alignment, graph count, cache state, and loader requirements.

0x09D Coordinate Selection And Architecture Selection Are Different Searches

The methods solve three distinct search problems.

Fixed architecture, select surviving coordinates

Sheared LLaMA starts from a target tuple such as and learns which source structures fill it. SliceGPT receives a width schedule and chooses which rotated directions survive at each boundary. Shortened LLaMA receives a target depth or parameter size and chooses the surviving original blocks.

The target geometry is fixed before the final coordinate selection:

Fixed unit family, choose a subset under an approximate budget

LLM-Pruner and ShortGPT rank dependency groups or blocks and remove low-scored units. Group sizes need not be equal, so a nominal parameter ratio can be an approximate stopping rule rather than an exact architecture equation.

Search architecture tuples, then select coordinates

Minitron enumerates feasible geometries first. Each candidate needs its own trimmed coordinates and equal lightweight recovery before comparison:

Ranking unrecovered candidates instead would answer a different question: which topology suffers the smallest immediate damage. Minitron’s source- reported experiments show that candidate order can change during lightweight retraining, so post-pruning loss is not a reliable substitute for recovered architecture quality in that pipeline.

0x09E Recovery Is Part Of The Method Cost And Sometimes Part Of Selection

Use a full compression cost ledger:

For architecture search with candidates,

For distillation, training tokens alone also omits teacher work:

The papers intentionally occupy different cost regimes:

PipelineEvidence/search updateMain recovery path in reported strong resultAccounting caveat
LLM-Prunergradients on 10 short public examplesLoRA, about 50K Alpaca examples, two epochsthree hours is a source-reported single-GPU experiment, not a hardware-independent budget
Sheared LLaMA0.4B tokens of gated source-model pruning50B tokens of full CPT with dynamic loadingpruning is reported around 5x slower than ordinary LM training; the 0.4B stage is not total cost
SliceGPTactivation collection and eigendecomposition; no base-weight updateoptional LoRA on about 1K or 5K sequencespaper could not find beneficial RFT settings for OPT; slicing and RFT costs vary by model
ShortGPTforward hidden-state collectionnone in core path; separate 50B-token MLP-replacement experimentreplacement means the recovered artifact is no longer pure block deletion
Shortened LLaMAforward or backward evidence on 10 examplesLoRA for moderate pruning; CPT or CPT then LoRA for severe pruningpaper reports CPT as far more expensive than LoRA and uses eight H100 GPUs for the studied runs
Minitron1,024-example evidence plus about 1.8B tokens per searched candidatelarge KD/retraining program; released tables report 94B training tokenscandidate warmups, family iterations, teacher forwards, and final training must not be collapsed into one token count

An equal-recovery comparison should snapshot every artifact immediately after conversion, then apply the same trainable-parameter policy, data, tokens, optimizer, and objective. A paper-faithful comparison is also valuable, but it answers which complete published pipeline performs best under each paper’s chosen cost, not which pruning decision is intrinsically best.

0x09F Runtime Evidence Is A Workload-Bound Contract

Define the deployment workload as

where is hardware, is runtime/version, is batch or active sequence count, and are prompt and generation lengths, is concurrency/scheduler policy, and is the quality checkpoint being timed. A speedup ratio is meaningful only relative to a baseline measured under the same .

PipelineRuntime evidence actually suppliedWhat it does not establish
LLM-Prunersource-reported parameter, MAC, memory, and latency table for a 64-token input on one A5000prefill/decode split, continuous batching, portable heterogeneous loading, or current-kernel speed
Sheared LLaMAsource-reported targeted-versus-nonuniform pruning generation throughput on one A100 80GBuniversal advantage of one shape, or a matched modern serving-stack result
SliceGPTbatch-1 length-128 generation timing on A100/RTX6000 plus H100 maximum-throughput sweepssame speedup at another batch/cache length; paper notes its HF test lacked continuous batching and optimized sharding
ShortGPTsource-reported GPTQ model throughput and separate official direct-delete/prefill-skip pathsthat every reported number measures one physically shortened, full-generation graph
Shortened LLaMA12-token prompt, 128 generated tokens, batch 1 on H100/RTX3090 with default Transformersdepth superiority at larger batches, different kernels, or matched quality/recovery budgets
Minitronno direct end-to-end latency or throughput table in the inspected paperdeployment speed or memory gain for a particular runtime; these require separate measurement

Cross-paper speed ranking is therefore invalid. A new evaluation must export all candidates into the same runtime, dtype, sharding, and quality state, then measure at least:

artifact and resident bytes
fresh-load success and custom operator requirements
prefill latency by prompt length
decode inter-token latency by cache length
throughput by concurrency and scheduler policy
persistent KV bytes per token
peak temporary memory and workspace
selected kernels, graph breaks, and TP/PP imbalance

Depth pruning has a plausible advantage when sequential block calls and layer-local K/V state dominate. Width pruning has a plausible advantage when large dense projections dominate and the new dimensions remain kernel- friendly. Residual slicing also changes activation traffic but may execute shortcut transforms. These are hypotheses from graph shape, not conclusions until measured under .

0x09G The Paper Result And The Exported Product Need Separate Manifests

Use two manifests rather than one overloaded pruning_config.json.

The method manifest reproduces the decision:

{
  "source_checkpoint": "immutable-id",
  "unit_family": "block|head|mlp|residual|hybrid",
  "evidence": {"dataset": "...", "samples": 0, "sequence_length": 0},
  "score": {"name": "...", "reduction": "...", "implementation": "..."},
  "budget": {"kind": "target-shape|parameter|depth|schedule", "value": "..."},
  "selected_original_indices": {},
  "search_and_recovery_cost": {}
}

The artifact manifest proves what is shipped:

{
  "architecture": {},
  "old_to_new_indices": {},
  "custom_modules": [],
  "shortcut_shapes": [],
  "cache_schema": {},
  "dtype_and_quantization": {},
  "sharding": {},
  "recovery_lineage": {},
  "fresh_load_hash": "...",
  "runtime_measurements": []
}

This split handles all six methods. LLM-Pruner needs dependency groups and heterogeneous shape maps. Sheared LLaMA needs target-family gates and the final standard target config. SliceGPT needs bases, per-boundary widths, norm policy, and shortcut state. ShortGPT and Shortened LLaMA need old-to-new block maps. Minitron needs the candidate search record, selected architecture tuple, family parent, and distillation lineage.

0x09H A Fair Reproduction Needs Two Experimental Tracks

One track cannot isolate both paper fidelity and algorithmic causality.

Track A: paper-faithful pipeline

reproduce each source checkpoint, evidence data, selection rule,
conversion, recovery recipe, evaluation suite, and reported workload

This tests whether the published complete pipeline reproduces. It preserves the paper’s choices but does not make the methods directly comparable.

Track B: controlled deployment comparison

for target in matched_target_set:
    candidates = {}
    for method in applicable_methods(target):
        immediate = convert_and_fresh_reload(
            select(source, evidence_manifest, target, method)
        )
        candidates[(method, "no_recovery")] = evaluate(immediate)

        recovered = recover(
            immediate,
            data=shared_recovery_data,
            tokens=shared_token_budget,
            trainable_policy=shared_policy,
            objective=shared_objective,
        )
        candidates[(method, "equal_recovery")] = evaluate(recovered)

    benchmark_same_runtime_workloads(candidates)

The matched target set should expose architecture effects instead of hiding them:

same exact parameter bytes
same depth, where methods permit it
same width/head/MLP tuple, where methods permit it
same measured prefill FLOPs or MACs
same measured decode memory traffic
same deployment memory ceiling
same held-out quality floor

Not every method applies to every target. SliceGPT chooses rotated residual directions and cannot be reduced to ordinary head selection. ShortGPT does not search MLP width. Sheared LLaMA and Minitron can target hybrid shapes but pay training/search costs that a no-update baseline does not. Mark a cell not applicable; do not force an invalid adapter that changes the method.

The Loop 9 dependency-free fixture makes these comparison rules executable:

CheckExact fixture resultMeaning
equal parameter count, different graphdepth target and MLP-width target both contain 60 parameters in the toy ledger, but execute 3 versus 4 sequential blocks and retain 3 versus 4 K/V layer slotsparameter equality is not graph or runtime equality
paper-faithful pairwise matchall 15 unordered pairs among the six paper records have at least one uncontrolled comparison coordinatethe source-reported rows cannot be ranked as one controlled experiment
controlled no-recovery trackllm_pruner, slicegpt, shortgpt, and shortened_llama are admitted only after source, target, evidence, immediate-artifact, evaluation, and workload controls are suppliedapplicability and control are separate predicates
depth-only targetshortgpt, shortened_llama, sheared_llama, and minitron are applicable; slicegpt is notunsupported method-target cells remain not_applicable
residual-width targetslicegpt, sheared_llama, and minitron are applicablea method is not stretched into a different removal operator to complete a table
recovery budget50,000 instruction examples, 50,000,000,000 LM tokens, and 94,000,000,000 KD tokens occupy different vector-budget coordinatesscalar labels such as small recovery are not admissible
workload identitychanging only hardware, runtime revision, dtype, batch, prompt length, output length, or phase rejects direct speed comparisona published speed ratio remains local to its workload tuple
claim ceilingpaper reproduction, quality ordering, speed ordering, framework execution, GPU execution, and deployment are all falsethe fixture audits contracts; it does not reproduce experiments

This fixture does not invent a universal scalar cost. A weighted sum is meaningful only after the weights and units are fixed by a product constraint. Before that decision, the budget remains a vector and dominance is coordinate-wise:

Most paper-faithful pipelines are incomparable under this partial order: one may spend more evidence backward passes but fewer recovery tokens, while another pays for teacher forwards or multiple architecture candidates. Calling either pipeline cheaper without declaring the objective weights is a policy choice disguised as measurement.

0x09I What The Papers Jointly Establish, And What They Do Not

Together, the papers support several source-scoped engineering conclusions:

  • structurally smaller dense graphs can produce real inference gains without general sparse kernels;
  • the removal axis changes the runtime result even at similar parameter count;
  • limited calibration can identify useful post-training subsets, but recovery becomes decisive as structural damage increases;
  • selecting a standard target shape improves portability, while heterogeneous or custom graphs may preserve more local fit at higher integration cost;
  • architecture rankings can change after equal lightweight recovery;
  • creating a model family through pruning plus large-scale training is a different product objective from emergency post-training compression.

They do not jointly establish that one score, removal unit, or pipeline is best across architectures and workloads. In particular:

ShortGPT quality table cannot prove depth always beats SliceGPT width.
Shortened LLaMA latency cannot prove depth wins at every batch size.
Sheared LLaMA's 50B-token result cannot be compared to LLM-Pruner's
  pruning-only or 50K-example LoRA row as if recovery were matched.
Minitron's released family quality cannot isolate pruning from distillation.
SliceGPT's source-reported speedup cannot be transferred to a runtime that
  handles its shortcut graph or sharding differently.

The useful choice is conditional:

Product constraintPipeline family worth testing firstReason to test, not a guarantee
minimal post-training data and a quick smaller graphLLM-Pruner, ShortGPT, or SliceGPT without recoveryevidence and conversion can be relatively small compared with pretraining-scale recovery
regular target architecture with a declared shapeSheared LLaMA or a uniform Shortened/Minitron targettarget geometry and loader contract are explicit before release
low-batch decode dominated by repeated block and weight accessdepth-pruned candidateremoves whole sequential calls and cache layers while retaining familiar matrix alignment
projection/activation width dominates and custom modules are acceptableSliceGPT or internal-width candidatereduces dense dimensions and activation traffic, subject to shortcut and kernel cost
reusable family of several model sizesiterative Minitron-style prune and distillamortizes one large source across recovered descendants, but includes teacher and search cost

0x09J Paper-Comparison Failure Lab

FailureHidden mismatchFalse conclusionRepair
compare every row called 20% pruningdepth, residual width, heads, and MLPs alter different graph termsscore method caused runtime differencereport full source/target geometry and physical artifact
compare quality after LLM-Pruner LoRA to Sheared pruning-onlyrecovery state differsselector superioritysnapshot immediate artifacts and match recovery policy
treat 50K examples and 50B tokens as comparable data budgetssample length, epochs, objective, and optimizer work omittedone pipeline is intrinsically more data-efficientreport tokens, forward/backward FLOPs, teacher work, and trainable state
call SliceGPT a stock smaller Transformershortcut matrices, norm reference width, and custom dimensions omittedordinary loader/runtime support assumedserialize custom architecture state and fresh-load it
call ShortGPT’s phase skip a shortened checkpointprefill and decode execute different graphslayer and KV savings overclaimeddistinguish direct deletion, prefill skip, and post-trained replacement artifacts
compare Minitron candidate loss before recoveryarchitecture order can flip during equal warmupwrong target tuple selectedrecover every feasible candidate under the same light budget
attribute Minitron release quality only to pruningdistillation and large retraining program omittedimmediate trim appears stronger than it isreport no-recovery, light-recovery, and final-recovery checkpoints
transfer Shortened depth timings to a server workloadbatch 1 and default Transformers treated as universaldepth always beats widthrerun matched prefill/decode/concurrency workloads
rank paper speedups directlyhardware, dtype, baseline, model, sequence, and runtime differlargest published ratio is best methodcompare only within one workload tuple
omit failed or unavailable method-target cellsmethods forced outside their defined unit familyartificial completenessdeclare not applicable and explain the contract mismatch
save only target weightsdecision, original indices, recovery, and custom graph state lostartifact cannot be audited or resumedpublish separate method and artifact manifests
cite a fresh-load pass as runtime proofserialization correctness confused with performancedeployable means fastprofile actual kernels, cache, memory, scheduler, and end-to-end latency

0x09K Loop Boundary

Loop 9 has converted six paper narratives into one auditable schema:

paper claim
    -> exact source and evidence contract
    -> removal unit and target-control contract
    -> topology freeze and physical conversion
    -> immediate artifact checkpoint
    -> paper-faithful or equal-budget recovery
    -> fresh-load release manifest
    -> workload-bound quality and runtime evidence

The comparison deliberately stops before deriving every recovery objective. Loop 10 owns reconstruction, LoRA, continued pretraining, and knowledge- distillation recovery as cross-method contracts. It will account for which parameters train, which teacher states are required, how student and teacher layers align after structural deletion, and when recovery cost dominates the compression search itself.

0x0A Recovery Optimizes The Surviving Graph, Not The Deleted Model

Pruning changes the hypothesis class before recovery begins. A removed block cannot receive an optimizer update. A deleted residual coordinate is absent from every downstream projection. A removed K/V head has no cache slot to repair. Recovery can move the surviving graph to a better point, but it cannot pretend that the old graph still exists.

Owner question: after physical pruning, which state is trainable, which errors are reachable by that state, and what data, optimizer, teacher, and release costs are required to obtain the reported recovered checkpoint?

The common recovery contract is

where is the frozen target graph, is the immediate converted artifact, is the trainable parameter space, is the recovery stream, is the objective, is optimizer and schedule state, is optional teacher state, and is the finalization operation. LoRA recovery is incomplete without all eight fields.

The serialized recovery identity must make those fields concrete:

@dataclass(frozen=True)
class RecoveryManifest:
    immediate_artifact_hash: str
    target_topology_hash: str
    keep_map_hash: str
    support_or_layout_hash: str | None
    trainable_parameter_names: tuple[str, ...]
    objective_and_coefficients: dict[str, float]
    recovery_data_manifest: str
    evaluation_data_manifest: str
    optimizer_and_scheduler: dict[str, object]
    adapter_or_projector_shapes: dict[str, tuple[int, ...]]
    teacher_checkpoint: str | None
    teacher_taps_and_layer_map: dict[str, object] | None
    loader_cursor_and_sample_ids: dict[str, object]
    finalization: str

None is different from an omitted field: it records that no teacher or support layout belongs to this arm. Every arm starts from the same immutable S1 hash. Any topology change, keep-map change, data overlap, or undeclared teacher path creates a different experiment rather than another recovery optimizer.

0x0AA The Immediate Artifact Is Recovery Step Zero

Every recovery comparison must begin from the same physical, fresh-loadable artifact. Let conversion produce

where is the frozen pruning decision and maps target tensors and axes back to source identities. Before any update, the artifact must pass the conversion oracle from Loops 5-8, serialize, load in a fresh process, and produce a baseline quality result. Clone this checkpoint for every recovery arm.

S0  source checkpoint
     -> prune, freeze, physically convert
S1  immediate target artifact: G_T, theta_0, keep map K
     -> clone independently into recovery arms
S2  local reconstruction | LoRA | full CPT | KD | staged recovery
     -> merge / project / freeze as required
S3  finalized recovered artifact
     -> fresh-load parity and workload evaluation
S4  release candidate

The topology invariant for an ordinary recovery experiment is

for every recovery step . For fixed unstructured or N:M support , also require

If training inserts replacement MLPs, restores layers, or changes a target width, it creates another architecture rather than merely recovering the S1 artifact. ShortGPT’s separate post-trained replacement experiment therefore needs its own artifact row; it is not the direct-deletion checkpoint with a larger optimizer budget.

0x0AB Derive What A Fixed Graph Can Recover

Write for the immediate artifact and for a reference response, such as the source model’s logits, hidden states, or a ground-truth training target. The response error at S1 is

For a small permitted update in recovery coordinates , a first-order expansion gives

Stack examples and output coordinates. The local squared-response problem is

With the Moore-Penrose pseudoinverse, one minimum-norm solution is

and the best locally reachable response is the projection . The residual decomposes orthogonally:

This derivation explains the recovery hierarchy. Local reconstruction exposes only one layer or block’s permitted coordinates. LoRA exposes low-rank update coordinates on selected surviving matrices. Full continued pretraining exposes all surviving parameters. KD changes the reference signal and can supervise outputs or aligned internal states, but its student Jacobian is still the Jacobian of . A wider trainable space can reduce the projected component; none can directly span a deleted operator that is absent from .

The result is a local diagnostic, not a global impossibility theorem. Nonlinear training can leave the initial tangent space and discover other functions. Capacity, data, and optimization can still limit the final result, and severe structural deletion can leave an irreducible approximation gap even after a long recovery run.

0x0AC Local Reconstruction And Global Recovery Solve Different Problems

SparseGPT-style reconstruction operates on one linear map and its calibration inputs. For , fixed support , and candidate , the local problem is

The target is the dense layer’s sampled output, not next-token likelihood or task accuracy. Sequential reconstruction should feed later layers with the already modified prefix so the input distribution reflects previous pruning decisions. Its state is the local activation matrix or Gram matrix, support, compensation update, and layer-order cursor.

Global recovery instead minimizes a model-level objective over a stream:

Define the model-level recovery risk

The recovered parameters are then

The two operations can be composed, but they are not interchangeable:

PathTrainable stateTarget signalTypical evidence scaleWhat it cannot establish alone
local reconstructionone matrix/block under fixed supportdense local outputscalibration batchesglobal language or task recovery
no-update structural exportnoneconversion parity onlyno recovery datawhether optimization could restore quality
LoRAlow-rank branches on selected survivorsLM, instruction, task, or KD lossexamples or tokensoptimum over all surviving weights
full CPTall surviving weightsnext-token data, optionally KDoften billions of tokensrecreation of deleted graph capacity
KDstudent parameters plus optional projectorsteacher outputs/internal statesteacher-labeled streamzero teacher cost during training

For SparseGPT, local reconstruction is part of the pruning/conversion path, so the S1 checkpoint already contains the update. Do not call S1 unrecovered and then compare it with a Wanda S1 checkpoint as if both selectors left weights untouched.

0x0AD LoRA Must Be Defined On Post-Surgery Shapes

For a surviving linear map , LoRA adds

with and . The trainable count is

Those dimensions are the target dimensions after structural surgery. A source adapter for a removed block has nowhere to attach; a residual-width adapter must use the sliced input and output axes; heterogeneous head or MLP pruning can make nominally identical module names have different shapes by layer. Initializing makes the live adapter branch exactly reproduce S1 before the first optimizer step.

immediate = fresh_load(target_config, immediate_checkpoint)
assert topology_hash(immediate) == manifest["target_topology_hash"]

for name, linear in selected_surviving_linears(immediate):
    d_out, d_in = linear.weight.shape       # post-surgery dimensions
    A = Parameter(randn(rank, d_in) * init_scale)
    B = Parameter(zeros(d_out, rank))       # exact S1 function at step zero
    attach_lora(linear, A, B, alpha / rank)

freeze_base_parameters(immediate)
optimizer = AdamW(only_adapter_parameters(immediate), lr=lr)

prune then LoRA and LoRA then prune are not generally equal. Training the adapter first can alter pruning scores; structural conversion must slice or discard both and consistently; and a whole removed block discards its adapter. Even for a fixed decision ,

unless the target adapter is constructed with the exact coupled input/output keep maps and the converter is linear over that representation.

Unstructured and N:M artifacts add another constraint. Merging a dense low- rank update into a sparse can refill pruned positions:

Valid release choices are different systems:

  1. keep a sparse base plus a live dense low-rank branch and measure both paths;
  2. apply the fixed mask in the forward and ensure optimizer/master state cannot regrow masked values;
  3. merge, reproject to the required support, and optionally reconstruct again;
  4. use a sparsity-aware adapter parameterization whose update obeys the support.

The third option can lose some LoRA recovery during reprojection. The first can erase the expected sparse-kernel speedup with an extra dense branch. Therefore adapter merged is not a sufficient release contract.

0x0AE Full Continued Pretraining Is A New Optimization Run

Full CPT exposes every surviving parameter to the language-modeling objective:

For one token, let abbreviate the probability conditioned on while executing the fixed graph , and define

The sequence objective is

It can update embeddings, norms, attention, MLPs, and the output head that remain in , so its local reachable space is broader than a fixed-rank adapter’s. It also pays backward and optimizer cost for those parameters. Graph surgery invalidates ordinary parameter-object identity and often changes tensor shapes, so blindly loading the source optimizer by parameter ordinal is wrong.

model = fresh_load(target_config, immediate_checkpoint)
assert_exact_keep_map(model, recovery_manifest.keep_map)

# Safest default: initialize optimizer state for the target graph.
optimizer = AdamW(model.parameters(), **recovery_manifest.optimizer)
scheduler = build_schedule(
    optimizer,
    warmup_steps=recovery_manifest.warmup_steps,
    total_steps=recovery_manifest.total_steps,
)

loader = restore_loader_state(
    mixture=recovery_manifest.domain_proportions,
    sample_ids=recovery_manifest.used_sample_ids,
    cursor=recovery_manifest.data_cursor,
)
train_causal_lm(model, loader, optimizer, scheduler)

If optimizer moments are intentionally migrated, they must be transformed by the same tensor identity and slice map as the parameter:

for target_name, target_param in target_model.named_parameters():
    origin = keep_map[target_name]
    source_name, axis_slices = origin.source_name, origin.axis_slices
    target_state[target_param]["exp_avg"] = slice_tensor(
        source_state[source_name]["exp_avg"], axis_slices
    )
    target_state[target_param]["exp_avg_sq"] = slice_tensor(
        source_state[source_name]["exp_avg_sq"], axis_slices
    )

This is valid only when the target value really descends from that source coordinate. New shortcut matrices, KD projectors, adapters, or replacement modules need fresh state. A new learning-rate warmup is usually a deliberate recovery choice, not an automatic continuation of the source pretraining clock.

Sheared LLaMA is the clearest cost warning: the paper’s 0.4B-token pruning stage is followed by 50B tokens of full continued pretraining, with dynamic domain loading carried into recovery. Shortened LLaMA likewise reports that moderate depth deletion can use a small LoRA run, while aggressive targets use far larger SlimPajama CPT budgets. Pruning cost that omits CPT does not describe either final checkpoint.

0x0AF KD Adds A Reference Model, Not Missing Student Operators

M6.10 owns temperature, divergence direction, cached versus online teachers, relation targets, and the general KD runtime. Here the pruning-specific issue is alignment after graph surgery.

Let student block align to teacher block . If width differs, train or define a projector . A pruning-recovery objective may be written

The pruning manifest must define , , the tap point relative to normalization/residual addition, and whether each target is tokenwise, pooled, or relation-based. Typical choices expose different assumptions:

Student surgeryLow-assumption targetAdditional design choice
width/heads/MLP changed, depth retainedlogit KDsame-depth hidden targets need width projectors
blocks deletedlogit KDmap each student block to a surviving-origin or spaced teacher block
depth and width changedlogit KDboth layer map and width projection
iterative family pruningteacher can remain the large rootrecord pruning parent separately from supervising teacher

Minitron reports a source-scoped version of these choices. It uses the unpruned model as teacher, finds logit-based KD effective for its width-pruned models, and studies intermediate targets and learned width projections when depth differs. The paper’s hidden targets are post-LayerNorm. These empirical choices are not a theorem that one divergence or layer map is universally best.

The teacher usually disappears from the released student, but it is not free during recovery. Online KD adds teacher weight residency, teacher forwards, communication or placement, and possible hidden/logit buffers. Offline KD moves some of that cost into cache generation and storage. Neither enlarges the deployed student’s missing architecture.

0x0AG Recovery Operations Do Not Commute

The order of pruning and recovery is part of the method:

Recovery changes weights, activations, gradients, curvature, and block ablation loss, so it can change which subset a later selector chooses. Pruning changes tensor shapes and deletes optimization coordinates, so it changes the recovery problem. Other common order mismatches are equally material:

Order AOrder BWhy they differ
local reconstruction then CPTCPT then prune/reconstructthe first starts global training from compensated S1; the second selects on a different model
CPT then instruction LoRAinstruction LoRA then broad CPTthe first specializes the recovered base; the second may overwrite task behavior
merge LoRA then N:M projectenforce mask during live LoRAprojection is discontinuous and can remove learned update mass
KD from root teacherKD from immediate pruning parentteacher capacity, logits, layer map, and family lineage differ
rank candidate before recoveryrank after equal light recoveryoptimization trajectories can reverse the ordering

Minitron explicitly motivates the final row: its candidate architecture ranking can change during an equal lightweight retraining budget. A single immediate loss is therefore insufficient when the release path includes substantial recovery.

0x0AH Optimizer And Memory State Are Part Of The Recovery Contract

For an Adam-like optimizer, a useful training-state ledger is

where is trainable parameters, is gradient bytes per parameter, are moment bytes, is optional master- weight state, is retained activation state, is online teacher residency, and covers distributed buffers. Sharding and offload change placement, not which state exists.

For LoRA,

while full CPT uses approximately the surviving parameter count. LoRA reduces trainable optimizer state; it does not remove the frozen target weights, forward activations, K/V cache, or adapter branch compute. Online KD can make a LoRA run use less optimizer memory yet more total resident memory than plain CPT on the student alone.

Checkpoint at least:

{
  "target_topology_hash": "...",
  "keep_map_hash": "...",
  "support_or_nm_layout": "...",
  "trainable_parameter_names": ["..."],
  "optimizer_and_scheduler": {"...": "..."},
  "adapter_or_projector_shapes": {"...": "..."},
  "teacher_checkpoint_and_taps": {"...": "..."},
  "data_mixture_and_cursor": {"...": "..."},
  "tokens_seen": 0,
  "recovery_stage": "immediate|local|lora|cpt|kd|finalized"
}

For fixed masks, checkpointing only model values is insufficient: optimizer moments, master weights, and parameterizations must preserve the support after resume. For Sheared-style dynamic loading, the domain proportions and sample IDs are part of the recovery trajectory, not disposable dataloader metadata.

0x0AI Calibration, Recovery, And Evaluation Data Have Different Jobs

Use separate symbols:

unless an overlap is intentional and disclosed. estimates importance, activations, curvature, or architecture candidates. supplies the optimization objective. estimates generalization and deployment quality. Reusing validation tasks as recovery data turns evaluation into adaptation.

Recovery data also defines what is being repaired. A small instruction corpus can restore benchmark-following behavior without restoring broad base-model perplexity. A pretraining mixture can recover language modeling while leaving instruction alignment unaddressed. This is why Shortened LLaMA studies CPT followed by LoRA as a staged path, and why SliceGPT reports materially different RFT outcomes between WikiText-2 and Alpaca recovery data.

Record tokens rather than only examples, but do not stop there:

Equal tokens do not equalize LoRA and CPT backward work, or student-only and online-KD teacher work. Sequence length, packing, trainable modules, checkpoint recomputation, and hardware utilization remain part of the budget.

0x0AJ The Papers Use Radically Different Recovery Contracts

All numbers below are source-reported and describe the studied paper path, not recommended universal hyperparameters.

PipelineRecovery state and objectiveSource-reported data/scheduleContract lesson
SparseGPTlayer-local fixed-support reconstruction during pruningcalibration activations; no global post-pruning fine-tune in the core pathcompensated S1 is not a no-update artifact
LLM-PrunerLoRA over all linear modules in the reported recovery setupabout 50K cleaned Alpaca examples, rank 8, LR , 100 warmup steps, batch 64 or 128, AdamW, two epochs; appendix reports about 2.5 hours on one RTX 4090small adapter state still depends on target-module surface and instruction data
SliceGPToptional LoRA recovery fine-tuningabout 1K sequences after WikiText-2 slicing or 5K after Alpaca slicing, rank 32, alpha 10, sequence length 1024; source reports model/data-dependent recovery and no helpful tested RFT setting for OPTLoRA works is not architecture- or data-independent
Shortened LLaMAno update, LoRA, CPT, or CPT then LoRALoRA on refined 50K Alpaca: every projection, rank 8, LR , batch 64, two epochs; CPT on SlimPajama ranges from 37B to 271B tokens in reported targetspruning severity changes whether adapter-only recovery is credible
Sheared LLaMAall surviving weights continue LM pretraining with dynamic domain loading0.4B-token pruning stage followed by 50B CPT tokensfinal quality cannot be attributed to pruning alone
Minitronequal light candidate retraining, then KD-centered family recoveryabout 1.8B tokens per architecture candidate; released tables report 94B training tokensarchitecture search and final recovery are separate costs
ShortGPTno recovery in the direct-deletion core path; separate trained replacement modulesseparate experiment reports 50B post-training tokensinserting trainable replacements changes the artifact contract

Shortened LLaMA makes the scale difference concrete. Its source reports the 20%-pruned 7B LoRA run at about two hours and 22GB on one A100, while its CPT runs use eight H100 80GB GPUs and tens to hundreds of billions of tokens. The same paper name can therefore contain recovery paths separated by orders of magnitude in training work.

Recovery ledger starting from one immutable immediate pruned artifact and comparing local reconstruction, LoRA, full continued pretraining, and knowledge distillation by trainable state, data, optimizer, teacher, finalization, and irreducible deleted capacity

The complete compression bill is

Define the recovery share

If architecture candidates each receive a light budget before one candidate receives , then

This exposes two different optimization questions. A deployment team may prefer a selector with slightly worse immediate quality if it reaches the target after much less recovery. A model-family program may accept expensive candidate warmups and KD because the recovered descendants are reused many times. Neither answer follows from parameter count alone.

Report at least four quality checkpoints:

CheckpointWhat it isolates
sourceoriginal capability and workload baseline
S1 immediate artifactpruning plus conversion, including any in-loop reconstruction
equal-budget recoveredhow candidate graphs respond to controlled recovery
paper-final recoveredquality of the complete published recipe

Without S1, recovery quality is falsely attributed to selection. Without an equal-budget checkpoint, unlike paper recipes cannot isolate method effects. Without the paper-final checkpoint, a controlled ablation does not reproduce the published system.

0x0AL Equal-Budget Recovery Harness

The harness must clone one immutable S1 checkpoint and prevent cross-arm state leakage:

immediate = validate_and_snapshot(converted_artifact)

results = {}
for contract in recovery_contracts:
    model = fresh_load(immediate.config, immediate.weights)
    assert topology_hash(model) == immediate.topology_hash
    assert output_hash(model, parity_batch) == immediate.output_hash

    trainer = build_recovery_trainer(
        model=model,
        trainable_space=contract.trainable_space,
        objective=contract.objective,
        teacher=contract.teacher,
        optimizer=contract.optimizer,
        data=shared_recovery_stream(contract.data_view),
    )
    trainer.run(token_budget=contract.token_budget)
    final = contract.finalize(model)

    assert topology_hash(final) == immediate.topology_hash
    assert_support_and_shape_contract(final, immediate.manifest)
    results[contract.name] = evaluate_and_profile(final, workload_suite)

The output row should include:

immediate quality
recovered quality trajectory versus tokens and wall time
trainable / resident / teacher parameters
student forward, backward, and teacher FLOPs
peak device and host memory
optimizer, adapter, projector, and cache bytes
finalization step: live, merge, project, or slice
fresh-load parity and topology/support hashes
artifact bytes, prefill, decode, throughput, and quality suite

Same token budget is one harness mode. Same wall-clock, same training FLOPs, and same peak-memory envelope answer other practical questions. A rigorous study labels the mode instead of calling all of them fair recovery.

The Loop 10 dependency-free fixture executes the recovery invariants on one small frozen graph:

CheckExact fixture resultContract
local reachable residual and produce reachable energy 9, irreducible energy 16, total 25recovery coordinates cannot directly span a deleted direction
post-surgery LoRAtarget weight shape is 2 x 3; rank-one factors are A: 1 x 3, B: 2 x 1source-shape adapters are rejected
zero- initializationlive LoRA output equals S1 exactly; first-step while adapter attachment does not move the immediate baseline
sparse mergedense low-rank merge refills two forbidden positions; mask projection restores zero support but changes the merged outputmerge and sparse finalization are separate operations
operation orderprune(recover(W)) and recover(prune(W)) produce different selected values on the same toy objectivepruning and recovery do not commute
optimizer migrationmoments migrate only for retained source coordinates; new adapter/projector state initializes freshparameter ordinal is not state identity
equal token budgetLoRA, CPT, and online KD each consume 8 student tokens but expose different trainable counts, student backward work, teacher forwards, and resident stateequal tokens do not equalize recovery cost
resumeoptimizer step, scheduler step, loader cursor, sample IDs, dynamic mixture, teacher ID, and stage round-trip exactlyvalues-only checkpoints are insufficient
data firewallevidence, recovery, and evaluation row identities are disjointevaluation is not silently adapted upon
claim ceilingpaper quality, training reproduction, framework/GPU execution, runtime, deployment, and publication remain falsedeterministic recovery algebra is not an empirical result

The fixture compares four arms only where their graph and S1 identity match. Local reconstruction is recorded as part of conversion when the pruning method already performs it; LoRA updates selected surviving matrices; CPT updates all survivors; and KD changes targets and state cost without adding deleted student operators. Their loss values are teaching outputs, not cross-paper quality measurements.

0x0AM Recovery Failure Lab

FailureHidden state errorConsequenceRepair
start each recovery arm from a separately pruned modelselector/converter noise differsrecovery methods absorb architecture varianceclone one verified S1 artifact
compare SparseGPT S1 with Wanda as both no recoverySparseGPT already compensated surviving weightsselector effect is confoundedrecord in-loop local reconstruction explicitly
attach adapters using source dimensionstarget axes changed after surgeryshape errors or wrong coordinate mappingconstruct LoRA from fresh target modules
initialize both LoRA factors randomlyS1 function changes before trainingimmediate baseline is lostinitialize one factor, usually , to zero
merge dense LoRA into N:M weightspruned positions refillsparse eligibility or tactic selection failspreserve support, reproject, or measure a live branch
assume LoRA recreates deleted blocksadapter exists only on survivorssevere capacity loss remainsstate the fixed-graph capacity boundary
load source Adam state by parameter orderidentities and shapes changedmoments attach to wrong coordinatesrebuild optimizer or migrate by exact keep map
reset dynamic data proportions on CPT resumedata state differs from pruning endpointrecovery trajectory no longer reproduces sourcecheckpoint mixture, sample IDs, and cursor
call instruction LoRA broad language recoveryrecovery data is task-shapedbenchmarks improve while base perplexity/coverage regressesevaluate both base and instruction capabilities
align student layer to teacher layer after depth deletionold-to-new semantics ignoredhidden KD supervises mismatched stagesserialize and justify
compare KD and CPT by student tokens onlyteacher forwards omittedKD cost is understatedreport teacher weights, FLOPs, buffers, and placement
merge or project without a post-finalization checkpointrelease function differs from trained functionmeasured quality cannot be reproducedevaluate live and finalized states separately
rank architectures only at S1recoverability differs by graphwrong candidate may win final budgetapply equal light recovery before selection
treat paper-final quality as pruning qualityrecovery may dominate the budgetpruning efficacy is overstatedreport source, S1, equal-recovery, and paper-final states
insert replacement modules but retain the old method labelgraph changed during recoveryruntime and capacity comparisons become invalidassign a new artifact and topology contract

0x0AN Loop Boundary

Loop 10 closes the recovery state machine:

one fresh-load immediate artifact
    -> declare fixed topology and trainable recovery coordinates
    -> separate locally reachable from deleted-capacity residual
    -> choose local reconstruction, LoRA, CPT, KD, or staged objectives
    -> preserve optimizer, mask, teacher, data-mixture, and layer-map state
    -> account for student backward work and optional teacher work
    -> finalize adapters/support/projectors without changing the hidden contract
    -> fresh-load the recovered artifact and compare S1, equal-budget, and final states

Loop 11 owns final runtime and evaluation closure. It will connect recovered quality to physical artifact bytes, memory, prefill, decode, throughput, hardware/runtime support, release manifests, rollback, and the article-wide publish gate. It will not hide recovery cost inside the word pruning.

0x0B A Pruned Release Is An Artifact-Runtime-Workload Tuple

Owner question. How do we prove that a finalized pruned model loads as the intended graph, executes the intended operators, preserves the declared quality contract, improves a specified workload, and can be rolled back without mixing state from two incompatible releases?

The pruning algorithm is finished before this question begins. A mask, keep map, sliced checkpoint, or shorter ModuleList is an intermediate result. A release claim needs a physical artifact, a loader, a runtime plan, a workload, quality and resource gates, and a known-good rollback target. This section owns the pruning-specific joins among those objects and retains their local proof. When the pruned model is composed with low-bit W/A/KV or a native low-bit representation, M8.9 owns that quantized integration and M8.10 adds the composed tuple’s release gates. Neither replaces the pruning-specific graph, sparse dispatch, quality and rollback evidence below. Generic nonquantized production integration remains a separate, unassigned program boundary; shared measurement principles do not imply universal M8 ownership. M8.10’s release and rollback procedure is the closest material in this series; its low-bit parts do not apply.

0x0BA Freeze One Release Identity

A checkpoint hash cannot identify an executable pruned model by itself. Width, head, and layer surgery can move meaning from weights into configuration, keep maps, custom modules, shortcut matrices, sparse metadata, shard plans, and engine tactics. Define the candidate release as

where:

FieldMeaningExamples
executable graph and schemaper-layer widths, kept layers, custom SliceGPT blocks, cache layout
finalized learned tensorssliced weights, recovered weights, merged adapters
support and storage statedense mask, N:M pattern, compressed values and metadata
source-to-target coordinate mapretained heads, channels, residual coordinates, old-to-new layer IDs
finalization transformmerge, support projection, physical slicing, packing
execution environmentloader, compiler, engine, kernels, container, driver, TP/PP plan
measured workloadhardware, requests, batch/concurrency, input/output/cache lengths
release gatesparity, quality, runtime, resource, canary, rollback

The executable evidence uses one immutable record rather than reconstructing these coordinates from filenames or deployment labels:

@dataclass(frozen=True)
class PrunedReleaseRecord:
    graph_hash: str
    tensor_hash: str
    support_hash: str
    keep_map_hash: str
    finalization_hash: str
    environment_hash: str
    workload_hash: str
    gates_hash: str
    cache_namespace: str
    predecessor_release_hash: str

cache_namespace and predecessor_release_hash are operational references, not members of the candidate’s mathematical function. They are nevertheless identity-bearing release state: changing either can make a correct offline artifact unsafe to canary or roll back.

The identity relation follows from the execution path. Inference evaluates runtime(load(graph, tensors, layout, shards), request), not weights(request). Changing any executable input can change the function, memory layout, tactic, or latency while the weight bytes remain identical. The release identifier therefore hashes the full immutable closure:

This is a provenance hash, not a quality proof. It says which candidate was measured. The parity report, quality samples, profiler trace, and canary report say what that candidate did.

0x0BB Artifact Classes Require Different Proofs

The physical representation determines what must be validated. Two artifacts with the same mathematical zero set can expose entirely different operators.

Artifact classSerialized stateCorrect first oracleRuntime proofCharacteristic rollback state
source-sized dense maskfull dense values plus mask or baked zerosmasked dense moduleprove that dense execution was intended; do not claim sparse speedupmask convention and merge order
coordinate sparseretained values, indices, row/column pointers, shapedense reconstruction of the same sparse tensorsupported sparse operator and observed sparse kernelindex ABI and sparse library version
N:M packedretained values plus backend-defined pattern metadatasame dense weight with the identical local supportprune check, compression check, eligible op, selected and observed tacticpacking ABI, reduction axis, dtype, shape
physically smaller densesliced tensors, target config, keep mapssame dense source graph with deleted gates fixed closedordinary dense kernels on target shapescustom loader, axis maps, TP/PP migration
shorter graphsurvivor tensors and old-to-new layer mapsame graph with deleted complete-block gates fixed to identityfewer executed blocks in both prefill and decodecache layer IDs, PP stages, graph captures
custom transformed graphtransformed weights plus executable shortcuts/projectorspre-serialization transformed reference graphcustom operator/graph path actually observedcustom code, norm convention, shortcut tensors

The conversion oracle is deliberately not always the original source model. Pruning is lossy, so a physical target should not equal the full source. The converter must first equal the mathematical target that conversion was meant to serialize. Quality comparison against the source comes later.

0x0BC The Equivalence Ladder Finds Where Meaning Changed

Use a ladder rather than one final benchmark:

  1. Schema parity: every tensor axis, per-layer field, tie, shortcut, cache slot, sparse pattern, and keep-map entry satisfies the target manifest.
  2. Immediate functional parity: the in-memory converted module matches the correct dense-masked, gate-closed, identity-skipped, or transformed oracle.
  3. Finalization parity: adapter merge, support projection, packing, and serialization preserve the declared finalized function.
  4. Fresh-load parity: a new process reconstructs the same graph and logits; no Python object left from pruning supplies hidden state.
  5. Cached-decode parity: prefill plus repeated cache append agrees with the uncached or reference decode path over several cache lengths.
  6. Distributed parity: TP/PP execution agrees with a single-rank target within the declared numerical tolerance.
  7. Engine parity: the compiled engine agrees with the freshly loaded target before performance claims are collected.

For a deterministic output tensor , record both absolute and scale-aware error:

The tolerance is dtype-, operator-, and reduction-order-specific. An FP16 TensorRT engine should not be required to match an eager FP32 path bit for bit. But a changed greedy token, a large logit-rank inversion, or a divergence that starts exactly at a deleted head/layer boundary deserves investigation rather than a wider tolerance.

The reference pairs are important:

N:M packed operator       <-> same masked dense target
physical head/MLP slice   <-> same source module with those gates fixed closed
physical layer deletion   <-> same source graph with those block gates at identity
recovery live state       <-> declared merged/projected final state
fresh serialized load     <-> pre-save finalized target
distributed/compiled run  <-> fresh-load target, not the unpruned source

0x0BD Bytes, Residency, And Peak Memory Are Different Ledgers

Start from serialized bytes. A compressed object contains more than surviving values:

B_auxiliary includes keep maps, tokenizer/template files, sparse plans, and custom shortcut state required to reconstruct the graph. It need not be large, but omitting it makes the artifact non-reproducible. The packed N:M ledger in 0x04 gives one backend-specific value/metadata ratio; it must not be reused for coordinate sparsity or structural slicing.

Resident and peak memory add runtime state:

This decomposition explains several apparent contradictions. A shorter graph can reduce layer-local K/V while leaving weight dtype unchanged. Residual-width slicing can reduce matrix weights without reducing K/V head-space width. A packed sparse matrix can reduce value storage while requesting a temporary compression or matmul workspace. A physically smaller model can admit a larger batch, which lowers weight bytes but raises the measured peak through K/V and activations. Report at least:

artifact bytes on disk
host bytes after load
device resident bytes after load
engine/context/workspace bytes
prefill peak at declared input lengths
decode peak at declared cache lengths and concurrency
per-rank peak and aggregate cluster residency

0x0BE Eligibility, Selection, And Observation Are Three Gates

Let be operators eligible for an optimized tactic, the tactics selected by the engine builder, and the operators observed in an execution trace. A release claim requires evidence across

plus an explicit list of intended operators that fell out at each boundary. For TensorRT structured sparsity, current documentation states that an eligible layer may still receive a dense tactic when profiling predicts it is faster. Verbose build logs or the engine inspector establish selection; an Nsight Systems trace establishes what the measured process executed. PyTorch’s current semi-structured API remains a version-scoped prototype with device, dtype, shape, and operator constraints. Direct cuSPARSELt has an equally concrete pipeline: descriptors, prune/check, compression, algorithm plan, workspace, and execution.

def build_and_prove_runtime(candidate, workload):
    validate_schema(candidate.graph, candidate.manifest)
    validate_keep_maps(candidate.keep_maps)
    validate_support_and_layout(candidate.weights, candidate.sparsity)

    engine = build_engine(
        candidate,
        verbose_tactics=True,
        deterministic_build_environment=True,
    )
    selected = parse_selected_tactics(engine.build_log, engine.inspector)
    assert_required_operator_coverage(selected, candidate.runtime_contract)

    warm_until_compilation_and_caches_are_stable(engine, workload)
    trace = profile_kernel_execution(engine, workload.profile_slice)
    observed = classify_observed_operators(trace)
    assert_no_forbidden_dense_fallback(selected, observed)

    # Timings are collected separately, without profiler instrumentation.
    return benchmark_uninstrumented(engine, workload.measurement_slice)

Even an observed faster kernel does not imply the same model-level speedup. If fraction of baseline time is accelerated by factor and the new path adds normalized overhead , then Amdahl’s decomposition gives

and therefore

This derivation identifies the missing evidence behind a headline such as 2x sparse Tensor Core throughput: may be small, metadata/workspace and launch overhead raise , and another operator may become the bottleneck. Measure prefill and decode separately because their shapes and memory pressure produce different , , and .

0x0BF Structural Surgery Migrates Shards And Graph State

Physical pruning changes partition arithmetic. Under simple equal tensor parallelism, retained axes usually must satisfy divisibility and kernel tile constraints such as

If a retained width violates hardware alignment , padding produces

The useful-width ratio exposes when a nominal structural reduction mostly turns into padded compute. Alternatives are to choose an aligned pruning target, replicate a small axis, permit uneven shards, change TP degree, or use a kernel that supports the retained shape. Those alternatives change communication, memory, or device count and must be measured as different release tuples.

Depth surgery similarly invalidates the old pipeline split. A first-order stage model is

Deleting layers from one stage can leave another stage dominant. Repartition survivors, regenerate shard files, renumber cache/graph layer IDs, and recapture compiled graphs. Custom SliceGPT shortcut matrices must be assigned to the stage that executes their boundary. Source shard files are not reusable merely because their surviving tensor names still exist.

0x0BG The Manifest Drives A Reproducible Build

One pruning-specific release manifest should make hidden state explicit:

{
  "release_id": "sha256:...",
  "lineage": {"source": "...", "s1": "...", "recovered": "..."},
  "artifact": {"weights": "...", "config": "...", "tokenizer": "..."},
  "topology": {"layers": [], "residual_widths": [], "heads": [], "mlp": []},
  "keep_map_hash": "sha256:...",
  "sparsity": {"kind": "2:4", "axis": "K", "metadata_abi": "..."},
  "finalization": {"adapter": "merged", "support_projection": "..."},
  "runtime": {"container": "...", "driver": "...", "hardware": "..."},
  "engine": {"plan_hash": "...", "tactic_report": "...", "graph_hash": "..."},
  "parallelism": {"tp": 0, "pp": 0, "shard_plan_hash": "..."},
  "workload": {"suite_hash": "...", "request_trace_hash": "..."},
  "gates": {"quality": "...", "runtime": "...", "resource": "..."},
  "rollback": {"target_release": "...", "cache_namespace": "..."}
}

The build pipeline consumes rather than reconstructs this state heuristically:

manifest = load_signed_manifest(path)
verify_all_hashes(manifest)

model = construct_target_graph(manifest.topology, manifest.sparsity)
load_exact_tensors(model, manifest.artifact)
assert_topology_and_support(model, manifest)

parity = run_equivalence_ladder(model, manifest.reference_suite)
engine = compile_with_pinned_environment(model, manifest.runtime)
tactics = export_tactic_report(engine)
profile = trace_required_workload(engine, manifest.workload)
measurements = benchmark_without_profiler(engine, manifest.workload)

sign_evidence_bundle(manifest, parity, tactics, profile, measurements)
Pruned release proof chain from immutable artifact and fresh-load parity through eligible, selected, and observed runtime operators, workload and quality measurement, canary comparison, rollback, and a signed evidence bundle

0x0BH Artifact And Runtime Failure Lab

FailureHidden contract violationMisleading conclusionRequired repair
dense zeros labeled sparse modelno compressed representation or sparse operatorzero count is reported as speedupname the dense-masked artifact and measure its dense runtime
eligible operator labeled acceleratedbuilder may select a dense tactictheoretical support is reported as executionretain selected-tactic and observed-kernel evidence
profiler trace used as the latency runinstrumentation changes timingobserved latency is not production latencyuse a separate uninstrumented benchmark run
physical target compared directly with source logitspruning loss and converter error are mixedconverter bugs hide inside expected quality losscompare first with gate-closed/identity-skipped target oracle
original shards loaded after width/layer surgerypartition coordinates no longer describe target graphmissing, duplicated, or imbalanced tensorsregenerate and hash the target shard plan
unaligned retained width silently paddednominal width differs from executed widthexpected FLOP saving disappearsreport padded shape and
custom shortcut omitted from artifacttransformed graph is incompletefresh load changes the functionserialize custom executable graph state and test it
dense LoRA merge refills N:M slotsfinal support differs from trained sparse contractsparse tactic becomes ineligibleconstrain, reproject, or retain and measure the live branch
only checkpoint bytes reportedworkspace, K/V, activations, and allocator state omittedfits in memory claim fails at loadreport phase-specific resident and peak ledgers
prefill-only block skip labeled shorter modeldecode graph and cache can remain unchangedend-to-end saving is overstatedinspect both prefill and decode execution traces

0x0C Evaluation Must Preserve Candidate Identity

Runtime evaluation is not the last row of the pruning paper’s quality table. It is a controlled comparison between immutable release tuples. The candidate must not change between parity, quality, profiling, benchmark, canary, and rollback rehearsal.

0x0CA Baselines Form A Ladder, Not One Number

BaselineStateQuestion answered
B0 sourceoriginal full modelhow much source capability and cost changed?
B1 S1immediate physical target before recoveryhow much did selection and conversion remove?
B2 recovered livetrained adapters/projectors/support before finalizationwhat did recovery optimize?
B3 finalized fresh loadexact release candidatewhat function and artifact will ship?
B4 dense same-target artifactsame target shape/support interpreted denselydid sparse packing/kernel help beyond geometry?
B5 current production releaseimmutable control tupleshould traffic move in this environment?

A same-target-shape model trained from scratch can be informative, but it is a separate research baseline rather than a mandatory release gate. B1 and B3 separate pruning damage from recovery. B3 and B4 separate representation from geometry. B3 and B5 drive the release decision.

0x0CB Quality Gates Are Bucketed And Conjunctive

Use the same tokenizer, prompt template, attention-mask semantics, stopping policy, and scoring implementation for source and candidate. The suite should cover token loss/perplexity, task accuracy, language and domain slices, short/long contexts, structured output, safety-critical behavior, and long generation with cache reuse. Recovery data must remain separate from final evaluation data.

For paired example differences , a bucket-level non-inferiority gate can be written

and the release quality gate is

The lower confidence bound and tolerance belong to the evaluation design, not to pruning algebra. M8.10 derives paired comparison, uncertainty, and release policy in depth. The pruning-specific requirement is that buckets sensitive to the removed structure remain visible: long context after depth/head surgery, languages or domains absent from recovery, cache-backed generation, and any task used to choose the pruned architecture. A weighted average must not let an easy bucket compensate for failure on a critical one.

0x0CC Measure The Workload By Phase

Reuse the workload tuple from 0x09F:

The release report expands it into a matrix:

PhaseSweepPrimary measuresPruning-specific evidence
cold startartifact and engine cache stateload/build time, host/device peakdecompression, engine rebuild, custom-loader cost
prefillTTFT, tokens/s, HBM, workspaceretained dense shapes, sparse GEMM coverage, deleted blocks
decodeinter-token latency/TPOT, tokens/sK/V width/layer count, one-token kernel selection
open-loop servingarrival trace, concurrency, prioritiesqueue time, goodput, SLO attainmentchanged batch capacity and scheduler fit
streaming E2Ereal request distributionTTFT, TPOT, completion latencyoutput-length and stopping-policy parity
resourceevery phasepower/energy, device/host memory, networksparse metadata/workspace, TP/PP communication

For one request,

A pruning method can improve while leaving queueing or decode dominant; it can lower decode compute but increase if a changed shape fragments batching. Report distributions, not only means, and keep quality/output length controlled so a model is not called faster merely because it terminates early or emits different text.

0x0CD The Measurement Protocol Is Executable State

def compare_release(control, candidate, request_trace, protocol):
    assert control.workload_hash == candidate.workload_hash
    assert protocol.sampler_and_stop_policy_equal

    for release in (control, candidate):
        verify_manifest_and_fresh_load(release)
        warm_until_steady(release, protocol.warmup_rule)

    samples = []
    for request in randomized_interleaving(request_trace):
        # Pair the same request while avoiding a fixed control/candidate order.
        for release in randomized_order(control, candidate):
            samples.append(run_uninstrumented(release, request))

    trace = run_separate_profile_slice(candidate, protocol.profile_requests)
    return {
        "raw_samples": samples,
        "quantiles_and_intervals": summarize_paired(samples),
        "observed_operators": classify_observed_operators(trace),
    }

Pin hardware, clocks/power policy, driver, runtime, engine, dtype, quantization, parallelism, scheduler, request corpus, output cap, stop conditions, and random seeds. Warm until compilation, allocator, and engine caches are stable. Use randomized or interleaved control/candidate runs to reduce drift. Retain raw per-request samples and profiler exports; a table copied from a terminal is not a reproducible benchmark artifact.

0x0CE Acceptance Is A Conjunction Of Non-Substitutable Gates

Define

Typical meanings are:

G_schema       exact graph, tensor, support, shard, and manifest checks
G_parity       fresh-load and compiled-engine equivalence within tolerance
G_quality      all critical bucket non-inferiority / absolute floors
G_runtime      phase-specific latency, throughput, and goodput targets
G_resource     memory, device count, energy, and startup constraints
G_operational  canary observability, capacity, rollback, and cache isolation

Do not combine these into one weighted score. A 20% latency gain cannot pay for wrong cache state, failed safety behavior, or a missing rollback artifact. Both absolute and relative gates matter: a candidate can beat a degraded control while both violate the service SLO.

0x0CF Canary The Exact Tuple That Passed Offline Gates

The canary is a time-bounded, attributable comparison between the immutable candidate and control, not a rebuild from the same source checkpoint. Start with offline and shadow checks, then use small sticky traffic partitions before larger stages. Exact percentages and dwell times depend on risk and traffic; they are policy, not pruning constants.

Monitor candidate and control labels for quality proxies, errors, TTFT, TPOT, queueing, goodput, memory, engine/tactic identity, dense fallback, cache events, and request-bucket mix. Check both absolute SLOs and relative differences, as recommended by the Google SRE canarying guidance. Sticky sessions matter because autoregressive K/V and prefix state belongs to one release namespace. Shadow traffic can compare outputs and some resource behavior, but it does not prove scheduler interference, user-visible streaming, or rollback under live state.

0x0CG Roll Back The Complete Executable Tuple

A rollback target includes the previous artifact, graph/config, loader, container, engine plan, shard plan, tokenizer/template, scheduler policy, and cache namespace. Keeping only old weights in object storage is not rollback readiness.

def rollback_pruned_release(candidate, known_good):
    stop_new_sessions(candidate.release_id)
    route_new_sessions(known_good.release_id)

    drain_or_cancel_candidate_sessions(policy=known_good.rollback_policy)
    invalidate_prefix_and_kv_namespace(candidate.cache_namespace)

    assert warm_capacity(known_good)
    assert manifest_hashes_match(known_good)
    assert smoke_and_parity_checks_pass(known_good)

    restore_release_routing(known_good.release_id)
    verify_slos_and_release_identity(known_good)

Never attach candidate K/V or prefix-cache blocks to the prior release unless an explicit compatibility proof covers graph topology, layer IDs, head-space dimensions, RoPE/tokenizer semantics, dtype/layout, and runtime interpretation. For structural pruning, incompatibility is the default: deleted layers remove cache slots, GQA/head changes alter cache geometry, and custom graphs can alter layer ownership.

Kubernetes Deployment rollback is only one control-plane step. Current Kubernetes documentation states that a Deployment revision rolls back its Pod template; external model files, ConfigMaps outside that revision, engine caches, traffic policy, and data services need their own versioned restoration. Rehearse rollback with warm known-good capacity and record detection, decision, routing, warmup, drain, and verification time using the recovery-ledger principles explained in M8.10. That methodological reuse alone does not assign this nonquantized pruning release to M8; low-bit composition adds its separate gate.

The Loop 11 dependency-free fixture exercises the release joins without pretending to benchmark an actual model or service:

CheckExact fixture resultContract
release identitychanging any of the eight closure hashes changes H_releasea checkpoint hash does not identify an executable release
artifact bytesvalues 12, metadata 4, config 2, adapters 3, auxiliary 3 sum to 24 bytesserialized values are only one artifact ledger
memoryresident 24, K/V 8, activations 6, workspace 4, allocator 2, communication 4 sum to peak 48disk bytes, residency, and peak remain distinct
parity ladderschema, immediate, finalization, fresh load, cached decode, distributed, and engine checks all pass their declared toleranceparity is staged against the correct target oracle
runtime evidenceintended operators are eligible, selected, and observed; uninstrumented timing samples are separate from profiler sampleseligibility alone is not execution or latency evidence
Amdahl ledgerf=0.6, operator speedup 2, and overhead 0.05 give end-to-end speedup a 2x operator does not imply a 2x model
quality gatesevery critical bucket passes its own lower-bound tolerancebroad average cannot rescue one failed critical bucket
release gatesschema, parity, quality, runtime, resource, and operational booleans are conjunctiveone gain cannot pay for a failed hard gate
canarycandidate and control see identical request buckets and absolute plus relative gatesa healthier relative result can still miss the SLO
rollbacknew sessions route to the warm predecessor, candidate sessions drain, and the candidate cache namespace is invalidatedrollback restores a tuple, not weights only
fault closurehash, parity, operator, bucket, workload, cache, and rollback mutations fail closedhidden state cannot silently cross a release boundary
claim ceilingreal framework/GPU execution, measured speedup, quality preservation, canary, deployment, and publication remain falsesynthetic release arithmetic is not production evidence

0x0CH Evaluation, Canary, And Rollback Failure Lab

FailureWhat is actually comparedConsequenceRepair
quality source and runtime control are silently differenttwo baselines answer different questionsrelease decision cannot be reconstructedlabel B0-B5 explicitly
only aggregate quality passescritical slice can regressbroad average hides pruning damageconjunctive bucket gates and absolute floors
baseline and candidate see different output capsless generated work looks fasterlatency gain is confoundedpair request, sampler, stop, and output policy
prefill benchmark stands for servingdecode, queue, and scheduler omittedexpected user latency does not appearphase and open-loop workload matrix
profiler timing used as production timinginstrumentation overhead includedlatency estimate is biasedseparate trace and uninstrumented runs
canary receives an easier request mixworkload distribution differsrelative metrics look healthystratify and compare request buckets
canary beats a broken control but misses SLOonly relative gate checkedboth versions are unhealthyrequire absolute and relative gates
sessions move between versionsK/V belongs to incompatible graphfuture tokens condition on wrong statesticky routing and versioned cache namespaces
old weights restored under new engine/configrollback tuple is incompleteold model executes new semanticsrestore and verify full release identity
rollback image exists but is coldno warm capacity or engine planrecovery exceeds incident budgetcontinuously test and prewarm known-good path
Deployment rollback assumed to restore external filesPod template is only restored objectmodel/config remains candidate versionversion and restore every external dependency
driver/runtime update changes tacticsselected kernel no longer stablelatency or dense fallback regressesrebuild, trace, and gate a new release tuple

0x0D Evidence Bundle, Publish Gate, And Handoff

0x0DA The Evidence Bundle Is The Release Record

Store immutable, checksummed raw evidence beside the signed manifest:

method contract and source provenance
S1 immediate target manifest and keep maps
recovery state/cost ledger and finalization record
fresh-load schema/support/parity reports
artifact byte inventory and per-rank memory samples
engine build log, inspector export, selected-tactic report
kernel trace and classified observed-operator report
raw quality predictions/scores and bucket definitions
raw per-request latency/throughput/goodput samples
workload trace, hardware/runtime/container/shard identity
canary control/candidate observations and stop decision
rollback target manifest and rehearsal transcript
hashes and signatures tying every file to H_release

Derived charts and summary tables should be reproducible from these files. A paper citation supports the method lineage; it does not replace measurements for this artifact, runtime, and workload.

0x0DB Article-Wide Publish Gate

This is the article’s prose-completeness gate. It is not evidence-program closure: Loop 1 has frozen the immutable source, figure, code, owner, and artifact identities, while the mechanism oracles and final render/source audit remain owned by Loops 2 through 12.

  • Pruning is separated from quantization, distillation, dynamic routing, and token pruning.
  • Removal units and their full dependency closures are defined before importance scores.
  • Magnitude, Taylor, curvature, Movement, Wanda, and reconstruction scores expose their evidence, reduction, and budget assumptions.
  • Unstructured, N:M, head, MLP, residual-width, and depth conversion paths include tensor-axis and code-level traces.
  • Dense masks, coordinate sparse tensors, N:M packed tensors, smaller dense graphs, shorter graphs, and transformed custom graphs remain distinct.
  • Immediate conversion is checked against the correct gate-closed, identity-skipped, masked-dense, or transformed reference oracle.
  • Recovery begins from one fixed fresh-load target and reports local reconstruction, LoRA, CPT, KD, optimizer, teacher, data, and finalization state separately.
  • Paper variants are compared by complete pipeline contract rather than by method name, nominal sparsity, or reported speedup alone.
  • Artifact bytes, resident memory, peak memory, K/V, workspace, and communication have separate ledgers.
  • Runtime eligibility, builder selection, observed execution, and end-to-end improvement require separate evidence.
  • Structural surgery regenerates graph, cache, TP/PP, shard, and alignment state rather than reusing source assumptions.
  • Quality gates are bucketed and conjunctive; workload measurements split cold load, prefill, decode, open-loop serving, and streaming end to end.
  • Canary and rollback operate on immutable release tuples with sticky sessions, versioned caches, warm known-good capacity, and rehearsed evidence.
  • Empirical paper results remain source-scoped; current runtime behavior is explicitly version-scoped.
  • M6.10 owns distillation; M8.9/M8.10 receive low-bit compositions for quantized integration and whole-tuple release. Pruning-specific proof remains here, and generic nonquantized production ownership is not silently reassigned.

0x0DC Final Owner Answer

Pruning becomes deployment compression only when the removal decision survives five boundaries:

scored removal contract
    -> physically correct target artifact
    -> fixed-graph recovery and finalization
    -> selected and observed executable runtime
    -> measured quality/resource release with rehearsed rollback

A zero, gate, or smaller parameter count proves only an internal property of one stage. The defensible claim names the final graph and representation, the actual engine and workload, the quality and resource gates, and the rollback tuple. That is the closure from pruning theory to executable sparsity.

0xFE Current Claim Status

ClaimStatus
individual-weight and structured component pruning are distinct removal granularitiesSource-local and externally verified
attention heads can sometimes be removed with limited degradation in the studied modelsExternally verified, source-scoped; not a universal head-redundancy theorem
LayerDrop trains depth-robust subnetworks through structured layer dropoutExternally verified; expected-depth derivation and selector separation are in 0x07
2:4 compressed storage includes retained values and pattern metadataCurrent official documentation; dtype/device/API details are version-scoped
satisfying TensorRT sparsity eligibility guarantees sparse tactic selectionFalse; the builder may choose a dense tactic
dense tensors become smaller merely because entries equal zeroFalse
physically sliced dense matrices can use ordinary dense kernelsRewrite interpretation grounded in tensor shapes; realized speed depends on shape/hardware
the five-contract pruning signature is a paper theoremNo; rewrite interpretation and audit framework
a pruning score is an intrinsic, data-independent property of a unitFalse in general; magnitude is data-free but still parameterization- and checkpoint-dependent
and define the same Taylor estimatorFalse; reduction order controls cancellation
shared-group Taylor equals the sum of absolute scalar Taylor scoresFalse in general; one shared gate yields the absolute of a signed sum
OBD and OBS use the same deletion counterfactualFalse; OBD uses diagonal curvature while OBS permits inverse-curvature compensation
movement scores can be recovered from final weights aloneFalse; they depend on the fine-tuning trajectory and score updates
learned expected sparsity guarantees an exact exported budgetFalse unless an exact projection or constraint enforces it
one-at-a-time ablation effects add to the joint subset effectFalse in general because units interact
the six-way toy disagreement is an empirical benchmarkNo; constructed rewrite example used to expose assumptions
SparseGPT and Wanda optimize the same algorithm with different score namesFalse; Wanda applies a no-update per-output mask while SparseGPT performs adaptive reconstruction updates
the layerwise reconstruction objective decomposes across output rowsTrue for the stated linear squared-error objective; row masks can still differ
a masked submatrix of equals the inverse of the masked False in general
SparseGPT’s indexed Hinv object in the inner code loop is always the raw inverse HessianFalse in the reference implementation; the inner loop uses an upper Cholesky factor of the damped inverse
Wanda’s activation statistic is one scalar per output rowFalse; it is one norm per input feature, broadcast across output rows
Wanda reference unstructured pruning ranks globally over a whole matrixFalse; it removes a fixed fraction independently in each output row
one-shot pruning means no calibration data or weight updatesFalse; method contracts differ
lower local reconstruction error guarantees lower deployment latencyFalse; execution depends on artifact format and runtime support
N:M notation and an API argument named prunen always count the same objectFalse; this article counts selected positions while the SparseGPT/Wanda reference argument counts positions to zero
50% global density proves 2:4 eligibilityFalse; every local group on the declared reduction axis must satisfy the constraint
exactly two selected slots and at most two numerical nonzeros are identical invariantsFalse; a selected stored value may itself equal zero
the 3-bit information lower bound for six 2:4 patterns is the current hardware metadata ABIFalse; PyTorch’s documented ledger uses 4 metadata bits per group and physical codes are backend-defined
half the FP16 values means half the compressed artifact bytesFalse; the documented values-plus-metadata fraction is before other overhead
masking only the current gradient guarantees fixed N:M recoveryFalse when stale optimizer or master-weight state can regrow deleted positions
PyTorch semi-structured dtype, shape, operation, and backend support is a universal 2:4 contractFalse; it is a version-scoped prototype API surface
a nominal 2x sparse arithmetic capability guarantees 2x layer or model latency speedupFalse; shape, traffic, metadata, launch, workspace, fusion, selected tactic, and eligible-time fraction remain in the result
setting a head gate to zero produces a smaller checkpointFalse; the gate defines a functional counterfactual, while structural export must delete every coupled producer and consumer slice
classic MHA head deletion closes over Q/K/V output blocks and the matching O input blockTrue under the stated separate-projection layout; packed projections require a layout-specific physical index map
a physically sliced classic MHA head should match the original unpruned moduleWrong conversion oracle; it should first match the same dense module with the deleted head gates fixed to zero
pruning one GQA query head also removes one K/V head and its cache payloadFalse; query-only pruning leaves shared K/V projection and persistent cache width unchanged
full-head deletion and head-width reduction are the same transformationFalse; head-width reduction changes coordinates inside every surviving head, including RoPE and kernel shape contracts
deleting one SwiGLU channel means slicing only gate_projFalse; one channel couples a gate_proj output row, an up_proj output row, and a down_proj input column
a state dictionary alone always describes a structurally pruned modelFalse for heterogeneous or nonstandard layouts; the loader also needs architecture, pack-order, original-to-new index, cache, and sharding metadata
heterogeneous per-layer head and MLP widths can always be reconstructed from ordinary scalar config fieldsFalse; they require per-layer metadata or custom module construction
fewer structurally exported parameters guarantee lower end-to-end latencyFalse; dense shape alignment, kernel selection, TP balance, graph reuse, KV traffic, and the baseline eligible-time fraction remain in the result
head-removal ratios reported by one paper are universal redundancy budgetsFalse; sensitivity is checkpoint-, task-, data-, schedule-, and metric-dependent
deleting one residual coordinate is local to one Transformer blockFalse; every producer, consumer, normalization boundary, shortcut, and endpoint that shares that state belongs to the dependency closure
an orthogonal residual rotation and the later coordinate deletion are both exactFalse; the full-width change of basis is exact up to numerical order, while slicing the retained subspace is lossy
bare fixed-denominator RMS normalization commutes with an orthogonal residual rotationTrue under the stated row-vector algebra; learned affine state must first be handled separately
a learned elementwise RMSNorm scale commutes with every dense orthogonal matrixFalse; a diagonal scale and a general dense rotation do not commute
independently rotated block boundaries can retain an identity residual shortcutFalse; they require the explicit map, or after slicing
SliceGPT shortcut maps are free metadata or can always be folded into a branch weightFalse; they are executable dense matrices on bypass paths around nonlinear branches
replacing the sliced artifact’s norm with ordinary RMSNorm(d') preserves the official computationFalse; the reference implementation retains the original hidden width as the normalization denominator
residual-width slicing alone reduces persistent K/V-cache widthFalse; Q/K/V residual input axes shrink while their head-space output dimensions remain unchanged
residual-width reduction percentage equals whole-model parameter reductionFalse; embeddings, head policy, unsliced inner axes, shortcut matrices, and tied/untied endpoints change the ledger
the official constant SliceGPT scheduler always slices the LM-head inputFalse; the current main path defaults to do_slice_head=False and returns the final boundary to the original head width
minimum calibration reconstruction error proves minimum language-model lossFalse; PCA optimizes the stated sampled squared-reconstruction objective, not downstream task loss
a sliced SliceGPT state dictionary can be loaded correctly by changing only a stock model’s scalar hidden_sizeFalse; boundary dimensions, shortcut matrices, norm reference width, endpoint policy, and custom layer classes are executable architecture state
a residual connection makes whole-block deletion function-preservingFalse; it makes the identity bypass shape-valid, while can still be important
the correct physical depth-export oracle is the original full modelFalse; first compare against the same dense model with deleted complete-block gates fixed to identity
removing uniform layers removes exactly of whole-model parametersFalse when embeddings, final norm, head, or other fixed state remain
physical layer deletion can reduce persistent KV-cache bytes even when surviving head widths are unchangedTrue for a standard layer-local cache; only kept blocks need cache slots
LayerDrop’s random mask automatically identifies the best fixed inference subnetworkFalse; training-time robustness and inference-time layer selection are separate contracts
Block Influence proves that a layer output equals its inputFalse; it is an average cosine-distance proxy and is insensitive to positive rescaling
one-block PPL effects add to the effect of deleting the selected setFalse in general; deletions change adjacency and can interact
ShortGPT’s official direct-deletion and generation paths execute the same shortened graphFalse; the generation path can skip full prefill blocks while still projecting/writing K/V and executes all layers at one-token decode
deleting entries from an in-memory ModuleList is a portable compressed checkpointFalse; config, keys, runtime layer IDs, cache schema, and fresh-load construction must agree
depth pruning requires token-position or RoPE index compactionFalse; sequence positions remain token positions, while layer-indexed graph state is renumbered
lower zero-shot loss identifies the best architecture after recoveryFalse in general; Minitron reports source-scoped candidate ranking changes during lightweight retraining
Shortened LLaMA proves depth pruning universally outperforms width pruningFalse; its latency conclusions are scoped to the studied shapes, hardware, batches, software, and recovery
current NeMo pruning CLI behavior is part of the Minitron paper theoremFalse; it is versioned official tooling with documented API limits
a Movement score is the final weight magnitude or final-minus-initial displacementFalse; hard Movement accumulates a signed first-order score trajectory through the STE during fine-tuning
the scheduled Movement topK threshold in the official code denotes sparsityFalse; it is passed as the fraction retained, so this article records keep fraction and sparsity separately
a cached global Movement threshold enforces an exact top- count at every intervening stepFalse; scores can cross a fixed numeric threshold until the global order statistic is recomputed
a masked Movement weight receives the same ordinary task gradient as an active weightFalse in the stated masked-linear algebra; the weight gradient contains , while the STE still lets its score move and potentially regrow
the official Movement masked-linear path proves sparse inference accelerationFalse; it multiplies a dense weight by a mask and invokes dense F.linear, and the repository reports no significant speedup from that path
hard Movement, soft Movement, and hard-concrete use interchangeable budget controlsFalse; they use direct top-, threshold-plus-penalty, and relaxed expected-gate objectives respectively
an expected hard-concrete open count is an exact exported architectureFalse; Sheared LLaMA follows relaxed training with deterministic exact-count projection and physical conversion
Sheared LLaMA constrains only one total parameter sparsity ratioFalse; its target-shape contract separately controls layer count, shared hidden width, per-layer heads, and per-layer intermediate width
the Sheared LLaMA paper and historical official code use the same hard-concrete temperatureFalse; the paper reports while the inspected official L0Module uses
ordinary minimization with a positive learning rate implements ascent on Sheared constraint multipliersFalse; the official optimizer assigns the multiplier group a negative gate learning rate
surviving deterministic Sheared gates are guaranteed to equal oneFalse in the official converter path; lowest scores are zeroed exactly, while surviving soft values may need to be absorbed into weights before slicing
the Sheared LLaMA paper algorithm and released dynamic-loading callback implement identical deficit updatesFalse; Algorithm 1 clamps deficits at zero, while the inspected callback uses the signed loss difference and adds a uniform probability floor
the 0.4B-token Sheared pruning stage is the complete compression-training budgetFalse; the paper separately reports 50B tokens of continued pretraining for the compact targets
a named pruning method is fully specified by its importance scoreFalse; Loop 9 records source, unit, evidence, budget, freeze, conversion, recovery, artifact, and validation contracts
the same removed-parameter percentage identifies the same compressed graphFalse; depth, residual width, heads, MLP width, and hybrid targets alter different parameter, operator, and cache terms
quality after pruning denotes the same pipeline state in all six papersFalse; reported endpoints can be immediate conversion, LoRA recovery, full CPT, or distillation-centered family training
ten calibration examples, 50K instruction examples, and 50B pretraining tokens are comparable data budgetsFalse; sequence length, objective, epochs, trainable state, backward work, and teacher cost differ
equal immediate post-pruning loss is sufficient to select Minitron’s best architectureFalse in the paper’s studied search; candidate order can change during equal lightweight recovery
ShortGPT direct deletion, phase-specific generation skipping, and MLP-replacement post-training are one artifactFalse; they execute different graphs and have different recovery and cache contracts
paper-reported pruning speedups can be ranked across papersFalse; source models, hardware, runtimes, dtypes, sharding, batches, sequence lengths, recovery states, and baselines differ
the inspected Minitron paper directly proves end-to-end deployment latency or throughput for its released modelsNot reported in the inspected paper; its evidence centers on architecture search, training, distillation, and quality, so runtime requires separate measurement
a paper-faithful reproduction and an equal-budget comparison answer the same questionFalse; the first reproduces a complete recipe while the second controls recovery, artifact, and workload to isolate method effects
recovery can recreate a structurally deleted operator without changing the target graphFalse; recovery optimizes functions representable by the surviving graph, while deleted layers, heads, coordinates, and cache slots remain absent
the tangent-space residual decomposition proves a global lower bound for nonlinear recoveryFalse; it is a local first-order diagnostic, while nonlinear training can leave the initial tangent space
local layer reconstruction and global CPT optimize the same objective at different scaleFalse; the former matches sampled local outputs under support constraints, while the latter optimizes a model-level data objective over surviving parameters
LoRA adapters should be constructed with the source model’s pre-pruning dimensionsFalse; post-surgery modules define the executable input/output axes and may be heterogeneous by layer
LoRA before structural pruning and LoRA after structural pruning generally commuteFalse; training can change pruning evidence, and conversion must slice or discard both adapter factors through the exact dependency closure
merging a dense LoRA update into an N:M tensor automatically preserves sparse eligibilityFalse; the low-rank update can refill masked positions unless support is constrained or the merged result is reprojected
reducing trainable LoRA parameters removes frozen target weights and forward activations from training memoryFalse; frozen weights remain resident and active, and the low-rank branch adds forward state
source optimizer state can be loaded into a structurally pruned graph by parameter ordinalFalse; parameter identity and shape can change, so moments must be rebuilt or migrated through the exact keep map
calibration, recovery, and validation data are interchangeable datasetsFalse; they estimate pruning evidence, optimize the target graph, and measure generalization respectively
an online KD teacher is a required dependency of the released standalone studentFalse in the ordinary export contract; it is recovery-time state unless the deployed system intentionally retains teacher routing or ensembling
equal recovery-token counts imply equal LoRA, CPT, and KD computeFalse; trainable state, backward work, teacher forwards, sequence packing, buffers, and hardware utilization differ
the best immediate pruned architecture must remain best after equal recoveryFalse in general; Minitron reports source-scoped candidate ranking changes during lightweight retraining
a recovered checkpoint and its merged or support-projected release artifact are necessarily functionally identicalFalse; finalization can change the function and requires a separate parity and quality checkpoint
a weight-file hash uniquely identifies an executable pruned releaseFalse; graph, support metadata, keep maps, finalization, runtime, shards, workload, and gates are part of the release tuple
the unpruned source is the correct first conversion oracle for every physical targetFalse; conversion first matches the intended masked, gate-closed, identity-skipped, or transformed target, while source quality is measured separately
an operator eligible for a sparse tactic necessarily executes that tacticFalse; builders can select a dense tactic, and selected tactics still require observed-execution evidence
observing one faster sparse kernel proves model-level speedupFalse; eligible-time fraction, unaffected work, metadata/workspace, launch overhead, memory pressure, batching, and queueing remain in end-to-end latency
checkpoint bytes equal device peak memoryFalse; K/V, activations, workspace, allocator, communication, engine, and concurrency state contribute to peak residency
source TP/PP shards remain valid after width or layer surgeryFalse; target axes, layer IDs, alignment, stage balance, cache schema, and graph captures can all change
fewer retained coordinates always produce proportionally less executed workFalse; alignment padding, kernel tiles, replicated axes, dense fallback, and fixed overhead can erase the nominal reduction
an aggregate quality average can compensate for a failed critical bucketFalse under the stated release contract; critical quality gates are conjunctive and retain absolute floors
a paper-reported speedup transfers to a new release tupleFalse; artifact, hardware, runtime, engine, workload, sharding, dtype, recovery state, and baseline must be remeasured
a canary is healthy whenever it outperforms the control relativelyFalse; candidate and control must also satisfy absolute service and quality gates
restoring a prior checkpoint alone completes rollbackFalse; graph/config, runtime/container, engine, shards, tokenizer/template, routing, cache namespace, and warm capacity also belong to the rollback tuple
K/V or prefix-cache state is portable across pruned releases by defaultFalse; topology, layer IDs, head-space geometry, dtype/layout, tokenizer, RoPE, and runtime interpretation require explicit compatibility proof
Kubernetes Deployment rollback restores external model artifacts and configuration automaticallyFalse under current Kubernetes documentation; Deployment revisions restore Pod templates, while external state needs separate versioning and restoration
a profiler-instrumented timing run is interchangeable with an uninstrumented latency benchmarkFalse; tracing changes execution and is retained as separate operator evidence
current PyTorch, TensorRT, cuSPARSELt, or Kubernetes behavior is a pruning-paper theoremFalse; these are version-scoped official runtime and operations contracts

0xFF References

Local Sources

Primary And Official Sources Used Through Loop 12

Loop 8 has now added Movement Pruning’s STE derivation, signed score trajectory, complete warmup/cubic/cooldown keep schedule, local/global exact-budget timing, hard/soft/ alternatives, code-level optimizer and regrowth state, and sparse-export boundary. It also follows Sheared LLaMA from structured hard-concrete gates and min-max target-shape constraints through deterministic projection, soft-gate absorption, physical tensor slicing, dynamic batch loading, continued pretraining, and fresh target-config release. Loop 9 now normalizes LLM-Pruner, Sheared LLaMA, SliceGPT, ShortGPT, Shortened LLaMA, and Minitron into source, evidence, target, conversion, recovery, artifact, and workload contracts. It separates paper-faithful reproduction from equal-budget comparison and rejects direct ranking of unlike paper endpoints. Loop 10 derives recovery on the fixed surviving graph, separates local reconstruction from global optimization, follows post-surgery LoRA through sparse finalization, specifies full-CPT optimizer/data resume state, and keeps pruning-specific KD layer/width alignment bounded by M6.10. It adds source- reported recovery schedules, equal-budget accounting, a recovery failure lab, and one responsive original recovery-state visual. Loop 11 closes the article with a full artifact-runtime-workload release tuple, conversion and fresh-load equivalence ladder, byte/residency ledgers, eligibility-selection-observation runtime proof, TP/PP and shard migration, immutable manifests, phase-specific evaluation, conjunctive gates, canary isolation, complete-tuple rollback, and a signed evidence bundle. One responsive original release-proof visual follows the candidate from artifact identity through observed execution and rollback. Loop 12 verifies the final source rather than adding another method: the build, all 982 MathJax expressions, desktop/mobile light/dark rendering, image alternatives, internal article links, external references, source-figure captions, and M5/M6/M8 ownership boundaries were audited together.