Structured Pruning: Head, Layer, and Width Pruning

Michael BrenndoerferFebruary 27, 202648 min read

Part of Language AI Handbook

Explains how structured pruning removes entire attention heads, transformer layers, and feed-forward neurons to create smaller, faster models.

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Structured Pruning

Modern language models have grown to sizes where deploying them in production is difficult. A model with billions of parameters demands significant memory, processing time, and energy on every inference call. Knowledge distillation, which we covered earlier in this part, trains a smaller model to mimic a larger one. Quantization reduces the numerical precision of weights. Pruning takes a different approach: it removes parts of an existing model entirely, creating a physically smaller architecture that performs nearly as well as the original.

Pruning broadly divides into two families. Unstructured pruning, covered in the previous chapter, sets individual weights to zero without changing the model's computational graph. The weight matrices remain the same shape, which means standard GPU kernels see no speedup unless you use specialized sparse computation hardware. Structured pruning, the focus of this chapter, removes entire structural components: whole attention heads, entire transformer layers, or full rows and columns from weight matrices. Because the model architecture changes shape, structured pruning produces real speedups on any hardware without special sparse kernels.

Structured Pruning

Structured pruning removes entire units from a neural network (heads, layers, neurons, or channels) rather than individual weights. The result is a smaller, denser network that runs faster on standard hardware.

The distinction matters enormously in practice. When you remove a single attention head from a 12-head transformer layer, the resulting layer has 11 heads and its weight matrices shrink proportionally. The forward pass takes less memory and fewer FLOPs on every call. When you remove a full transformer layer from a 12-layer model, every inference call skips that layer entirely. These are concrete, measurable speedups that show up in production latency metrics. Structured pruning is not about theoretical sparsity; it is about real hardware-visible compression that you can benchmark directly.

The field of structured pruning for transformers emerged from broader work on network pruning in convolutional networks, where researchers found that removing entire filters rather than individual weights produced better hardware acceleration. When BERT arrived in 2018 and researchers began analyzing its attention patterns, it quickly became clear that the same principle applied: many heads were redundant, many layers were near-identity, and removing them in bulk produced models that were both smaller and faster without catastrophic quality loss. This observation motivated a wave of papers on head pruning and layer dropping that established the foundational techniques we will study here.

Why Structures Can Be Removed

A recurring observation in transformer research is that not all components contribute equally to model quality. Many attention heads appear redundant: when researchers examined BERT's attention patterns, they found that many heads learned similar patterns, and individual heads could be masked off at test time with minimal quality loss. The 2019 paper "Are Sixteen Heads Really Better than One?" by Michel and colleagues showed that many BERT heads could be removed without any fine-tuning and the model would retain most of its performance. This result was surprising at the time because it suggested that the model was substantially over-provisioned for most tasks.

Similarly, analysis of layer-by-layer representations in deep transformers shows that middle layers often produce very similar hidden states, suggesting many computations could be skipped. Gromov and colleagues demonstrated in 2024 that in large language models like Llama 2, angular distances between consecutive layer representations vary dramatically across the depth of the network. Early layers perform large, varied transformations. Late layers also apply significant transformations, particularly those responsible for mapping representations to output token probabilities. But a set of middle layers often pass the representation through nearly unchanged.

This redundancy is not an accident. Large models are typically overparameterized relative to any specific downstream task. During pretraining on broad corpora, the model learns many different capabilities, some of which are irrelevant for a particular application. Fine-tuning on a narrow task does not eliminate the circuits dedicated to irrelevant capabilities; it just reduces their influence on the output. Structured pruning provides a systematic way to find and excise these underutilized components.

The key insight driving all structured pruning methods is that you need a criterion for measuring importance. An importance score tells you which heads, layers, or neurons matter most. Once you have scores, you can rank all components and remove the least important ones until you reach a target size or quality threshold. The challenge is computing reliable importance scores without running an exhaustive ablation study, which would require removing each component individually and evaluating quality, an approach that does not scale to models with hundreds of layers and thousands of heads.

There are three main families of importance criteria. Perturbation-based methods measure the actual quality change from removing a component. Gradient-based methods use the gradient of the loss with respect to component outputs to estimate sensitivity. Activation-based methods measure how active a component is on representative inputs. Each has different computational costs and different accuracy characteristics, and practitioners often combine them.

Head Pruning

Transformer attention layers contain multiple parallel attention heads. Each head applies a separate set of queries, keys, and values to the input, then projects the results back to the model dimension. The motivation for multiple heads is that different heads can attend to different aspects of the input: one head might track syntactic subject-verb dependencies while another tracks positional relationships, and another might focus on coreference.

The multi-head attention computation concatenates the outputs of all heads and applies a final linear projection. The full attention output for a layer is:

MultiHead(X)=Concat(head1,head2,…,headH)WO\text{MultiHead}(\mathbf{X}) = \text{Concat}(\text{head}_1, \text{head}_2, \ldots, \text{head}_H) \mathbf{W}_O

where each head computes:

headh=softmax ⁣(XWQ(h)(XWK(h))⊤dk)XWV(h)\text{head}_h = \text{softmax}\!\left(\frac{\mathbf{X}\mathbf{W}_Q^{(h)} (\mathbf{X}\mathbf{W}_K^{(h)})^\top}{\sqrt{d_k}}\right) \mathbf{X}\mathbf{W}_V^{(h)}

where:

  • HH is the number of heads
  • dkd_k is the head dimension
  • WQ(h),WK(h),WV(h)∈Rdmodel×dk\mathbf{W}_Q^{(h)}, \mathbf{W}_K^{(h)}, \mathbf{W}_V^{(h)} \in \mathbb{R}^{d_{\text{model}} \times d_k} are the query, key, and value projections for head hh
  • WO∈R(H⋅dk)×dmodel\mathbf{W}_O \in \mathbb{R}^{(H \cdot d_k) \times d_{\text{model}}} is the output projection that assembles all heads

Because each head's contribution is additive after the output projection, you can set any head's output to zero and the remaining heads still produce a valid (though potentially degraded) result. This additive structure is precisely what makes head pruning possible: it is a well-defined operation with predictable effects.

Importance Scores for Heads

The most direct importance score for a head is its sensitivity to removal. You can estimate this by computing how much the loss increases when you zero out that head's output. Formally, for head hh in layer ll, define the importance score as:

Ih(l)=∣L(θ)−L(θ∖hl)∣I_h^{(l)} = \left| \mathcal{L}(\theta) - \mathcal{L}(\theta \setminus h_l) \right|

where:

  • L(θ)\mathcal{L}(\theta) is the loss with all heads present
  • L(θ∖hl)\mathcal{L}(\theta \setminus h_l) is the loss with head hh in layer ll zeroed out
  • ∣⋅∣|\cdot| is the absolute value, giving a non-negative importance score

Computing this exactly requires a separate forward pass for each head, which is expensive. A first-order approximation uses gradient information instead. For a head parameterized by its output projection weights WO(h,l)\mathbf{W}_O^{(h,l)}, the gradient-based importance score is:

Ih(l)=∥∂L∂WO(h,l)⊙WO(h,l)∥FI_h^{(l)} = \left\| \frac{\partial \mathcal{L}}{\partial \mathbf{W}_O^{(h,l)}} \odot \mathbf{W}_O^{(h,l)} \right\|_F

where:

  • ∂L∂WO(h,l)\frac{\partial \mathcal{L}}{\partial \mathbf{W}_O^{(h,l)}} is the gradient of the loss with respect to the head's output projection weights
  • ⊙\odot is element-wise multiplication
  • ∥⋅∥F\|\cdot\|_F is the Frobenius norm, giving the overall magnitude of the score

This gradient-weight product, sometimes called the Taylor expansion criterion, approximates the change in loss from zeroing out the head. Intuitively, a head with large weights and large gradients is one where the model is heavily relying on that head and adjusting it actively. Large values indicate that the weights are large and their gradients are large, meaning the head contributes substantially to the loss. Small values indicate the head could be removed with little effect. This approximation is derived from a first-order Taylor expansion of the loss around the current parameters, treating the removal of the head as a perturbation.

A simpler but surprisingly effective approach uses attention head activation norms. For each head, compute the mean magnitude of its output across a calibration dataset:

Ih(l)=Ex∼D[∥headh(l)(x)∥2]I_h^{(l)} = \mathbb{E}_{x \sim \mathcal{D}} \left[ \left\| \text{head}_h^{(l)}(x) \right\|_2 \right]

where headh(l)(x)\text{head}_h^{(l)}(x) is the output vector produced by head hh in layer ll for input xx, before it is passed through the output projection.

Activation norm-based importance has the advantage that it requires no gradient computation and can be collected during normal inference. The disadvantage is that it measures how active a head is, not how important it is to the final output. A very active head whose output is largely cancelled by the output projection could score high by this criterion yet contribute little to the model's predictions. In practice, gradient-based scores tend to be more accurate, but activation norms are a useful fast approximation.

Head Pruning Mechanics

Once importance scores are computed, pruning proceeds by selecting the heads to remove and modifying the weight matrices accordingly. An attention layer in a transformer has query, key, value, and output projection matrices. With HH heads each of dimension dkd_k:

Q∈Rdmodel×(H⋅dk),K∈Rdmodel×(H⋅dk),V∈Rdmodel×(H⋅dk)\mathbf{Q} \in \mathbb{R}^{d_{\text{model}} \times (H \cdot d_k)}, \quad \mathbf{K} \in \mathbb{R}^{d_{\text{model}} \times (H \cdot d_k)}, \quad \mathbf{V} \in \mathbb{R}^{d_{\text{model}} \times (H \cdot d_k)}

Each head hh uses columns (h−1)⋅dk(h-1) \cdot d_k through h⋅dkh \cdot d_k of these matrices. The output projection WO∈R(H⋅dk)×dmodel\mathbf{W}_O \in \mathbb{R}^{(H \cdot d_k) \times d_{\text{model}}} assembles the contributions from all heads. To remove head hh, you delete the corresponding columns from Q\mathbf{Q}, K\mathbf{K}, V\mathbf{V}, and the corresponding rows from WO\mathbf{W}_O.

After removing kk heads, the model dimension of the attention projections shrinks from H⋅dkH \cdot d_k to (H−k)⋅dk(H - k) \cdot d_k. This directly reduces the memory footprint of the attention layer and the number of FLOPs in every forward pass. The change is structural and hardware-visible: the new, smaller weight matrices are stored in contiguous memory, and standard matrix multiplication routines operate on the smaller tensors with no overhead.

Pruning decisions can be made at the layer level (removing a fixed number of heads from each layer independently) or globally (ranking all heads across all layers together and removing the globally least important ones). Global pruning is generally preferable because it finds that some layers have more redundant heads than others. In BERT, for example, researchers consistently found that middle layers had more redundant heads than early or late layers. A layer-level strategy that forces uniform pruning across layers would retain unnecessary heads in middle layers while potentially over-pruning early or late layers.

Recovery Fine-Tuning After Head Pruning

Removing heads causes an immediate quality drop. The model was trained with all heads present, and the surviving heads were not optimized to compensate for the removed ones. A brief fine-tuning pass, sometimes called recovery fine-tuning, allows the remaining heads to adapt. In practice, this recovery phase needs far fewer steps than the original training because you are not learning from scratch but adjusting weights that already encode useful representations.

The amount of quality you can recover depends on how aggressively you pruned. Removing 10-20% of heads typically recovers fully within a small number of fine-tuning steps. Removing 50% or more often results in permanent quality degradation, though the model may still be useful for applications that tolerate some accuracy loss in exchange for speed. The recovery fine-tuning learning rate should be substantially lower than the original training learning rate to avoid disrupting the representations that the surviving heads have already learned.

One practical consideration is the order of operations. If you plan to prune both layers and heads, the recommended order is: compute layer importance and remove layers first, then compute head importance on the reduced model and remove heads, then fine-tune. This order matters because removing layers changes the distribution of attention patterns, which can shift head importance scores. Computing head importance on the original model and then removing both layers and heads simultaneously tends to produce worse results.

Layer Pruning

Layer pruning removes entire transformer blocks from the model. A transformer block includes the full self-attention sublayer, the feedforward sublayer, and their associated layer normalization layers. Removing a block reduces the number of sequential operations in every forward pass and eliminates all associated parameters. For a 12-layer BERT model, each layer represents roughly 8% of the total transformer parameters (excluding embeddings), so removing one layer provides approximately that fraction of total parameter reduction.

The computational benefit of layer removal is particularly significant for autoregressive generation. In generation, each new token requires a full forward pass through all transformer layers. Removing two layers from a 12-layer model reduces per-token generation time by roughly 17%, a meaningful speedup when you are generating hundreds of tokens per request.

Measuring Layer Importance

The conceptually cleanest importance score for a layer measures its contribution to the residual stream. Modern transformers use residual connections: each block adds its output to the input before passing to the next block. You can measure how much a layer changes the representation by computing the cosine similarity between the input and output of that layer:

sim(l)=1N∑x∼Dcos⁡(h(l)(x),h(l+1)(x))\text{sim}^{(l)} = \frac{1}{N} \sum_{x \sim \mathcal{D}} \cos\left( \mathbf{h}^{(l)}(x), \mathbf{h}^{(l+1)}(x) \right)

where:

  • h(l)(x)\mathbf{h}^{(l)}(x) is the hidden state entering layer ll for input xx
  • h(l+1)(x)\mathbf{h}^{(l+1)}(x) is the hidden state leaving layer ll
  • cos⁡(⋅,⋅)\cos(\cdot, \cdot) is cosine similarity, ranging from -1 to 1
  • NN is the number of tokens evaluated

A layer with similarity close to 1.0 barely modifies the representation; it is essentially a near-identity transformation. Such layers are strong candidates for removal. A layer with similarity much less than 1.0 applies a substantial transformation and is important to retain. This metric is intuitive because it measures what matters to the residual stream: whether the layer changes the information flowing through the model.

This block influence (BI) criterion was investigated by several research groups studying large language models and consistently found that some middle layers have very high input-output similarity, suggesting they contribute little. The distribution of layer similarities is not uniform: early layers typically apply large transformations (encoding syntactic and positional structure into the embeddings), late layers also apply significant transformations (projecting representations toward output token probabilities), and some middle layers are nearly identity. This U-shaped distribution of layer importance has been replicated across model families from BERT to GPT to Llama.

Another approach uses gradient-based sensitivity. Compute the gradient of the task loss with respect to the output of each transformer block. Layers where the gradient magnitude is small contribute little to the final prediction and can be removed without large quality drops:

S(l)=∥∂L∂h(l+1)∥FS^{(l)} = \left\| \frac{\partial \mathcal{L}}{\partial \mathbf{h}^{(l+1)}} \right\|_F

where:

  • S(l)S^{(l)} is the sensitivity score for layer ll
  • ∂L∂h(l+1)\frac{\partial \mathcal{L}}{\partial \mathbf{h}^{(l+1)}} is the gradient of the task loss with respect to the layer's output hidden state
  • ∥⋅∥F\|\cdot\|_F is the Frobenius norm, measuring the overall magnitude of the gradient signal

Layers with large sensitivity scores are critical to the model's predictions because backpropagation flows strongly through them. Layers with small scores carry little gradient and can be removed with minimal quality impact. The gradient-based score is computationally more expensive than the cosine similarity score because it requires a backward pass, but it is more directly connected to the training objective.

Layer Pruning by Angular Distance

A more refined version of the cosine similarity criterion uses angular distance rather than cosine similarity directly. For each layer, you compute how much the hidden state rotates on average as it passes through:

θ(l)=arccos⁡(sim(l))\theta^{(l)} = \arccos\left(\text{sim}^{(l)}\right)

where:

  • θ(l)\theta^{(l)} is the angular distance (in radians) for layer ll
  • sim(l)\text{sim}^{(l)} is the mean cosine similarity between the input and output hidden states of layer ll
  • arccos⁡\arccos converts cosine similarity to an angle, with 0 meaning no rotation and π\pi meaning a complete reversal

Layers with small θ(l)\theta^{(l)} (small rotation, near-identity) are pruned first. This angular perspective is useful because it is invariant to the scale of the hidden state norms, focusing purely on the geometric transformation. A layer that scales all hidden states by a constant factor but does not rotate them would have θ(l)=0\theta^{(l)} = 0 and would be correctly identified as removable, whereas a norm-based criterion might incorrectly flag such a layer as active because its output magnitude differs from its input.

The angular distance criterion is the foundation of the ShortGPT method, introduced by Ma and colleagues in 2024. They applied it to Llama 2 and found that removing the 25% of layers with the smallest angular distances produced models that retained over 95% of the original performance on standard benchmarks while running substantially faster. The layers identified as most removable were consistently in the middle of the network, confirming the U-shaped importance profile observed in earlier work.

Block Dropping and Performance Recovery

When you drop a transformer block, the layers before and after it are now adjacent. The hidden state that was computed by layer l−1l-1 flows directly into layer l+1l+1. If the removed layer was performing a useful but subtle transformation, the model can often compensate through fine-tuning because the surviving layers can adjust their parameters to approximately recover the removed layer's contribution.

Recovery from layer pruning benefits significantly from targeted fine-tuning where only the layers adjacent to the removed block are updated initially, then the whole model is fine-tuned at a lower learning rate. This staged approach stabilizes training and typically achieves better final quality than fine-tuning the entire pruned model from the start. The intuition is that the adjacent layers need to make the largest adjustments and giving them a focused fine-tuning phase before introducing global optimization pressure prevents conflicting gradient signals.

When removing multiple layers, the order of removal matters. Removing the single least-important layer first, then recomputing similarities on the resulting model, and repeating this process gives better results than selecting the kk least-important layers simultaneously and removing them all at once. The iterative approach allows the model to adapt after each removal, which changes the importance scores for the remaining layers. This iterative single-layer removal is more compute-intensive but consistently produces higher-quality pruned models.

A practical consideration when deploying layer-pruned models is that layer count affects KV-cache size in autoregressive models. Each transformer layer contributes entries to the KV cache for each token in the context. Removing two layers from a 32-layer model reduces KV-cache memory by 6.25% for long contexts, a meaningful saving in memory-constrained deployments.

Width Pruning

Width pruning targets the internal dimensions of individual layers rather than removing entire heads or layers. In transformer feedforward sublayers, the input dimension dmodeld_{\text{model}} is projected up to an intermediate dimension dffnd_{\text{ffn}} (often 4 times larger), passed through a nonlinearity, then projected back down. Width pruning removes neurons from this intermediate dimension, making the feedforward sublayer smaller while keeping the layer itself in the architecture.

Width pruning is more fine-grained than layer or head pruning, which allows you to tune the compression ratio with greater precision. Instead of choosing between keeping or removing an entire layer (which jumps the parameter count by a large discrete amount), you can remove neurons one at a time, finding the exact tradeoff between size and quality that meets your deployment constraints.

Feedforward Neuron Importance

For the feedforward sublayer, each intermediate neuron receives input from the first linear layer and sends output through the second. Neuron nn in the feedforward sublayer of layer ll has activation an(l)(x)a_n^{(l)}(x) for input xx. A natural importance score is the mean absolute activation:

In(l)=Ex∼D[∣an(l)(x)∣]I_n^{(l)} = \mathbb{E}_{x \sim \mathcal{D}} \left[ \left| a_n^{(l)}(x) \right| \right]

where:

  • In(l)I_n^{(l)} is the importance score for neuron nn in feedforward sublayer ll
  • an(l)(x)a_n^{(l)}(x) is the activation value of neuron nn for input xx after the first linear projection and nonlinearity
  • Ex∼D\mathbb{E}_{x \sim \mathcal{D}} is the expectation over the calibration dataset D\mathcal{D}

Neurons that are consistently near zero contribute little to the computation. A threshold τ\tau determines which neurons to remove: all neurons with In(l)<τI_n^{(l)} < \tau are eliminated.

The choice of nonlinearity affects how well this criterion works. With ReLU activations, neurons that are dead (always outputting zero) have zero importance by definition. With GELU or SiLU activations, which are smooth and only approach zero asymptotically, you need the mean absolute activation criterion to identify low-importance neurons because no neuron is ever exactly zero. In modern language models that use GELU or SiLU, mean absolute activation is the standard criterion for feedforward width pruning.

The mechanics of width pruning are similar to head pruning. The first feedforward projection W1∈Rdmodel×dffn\mathbf{W}_1 \in \mathbb{R}^{d_{\text{model}} \times d_{\text{ffn}}} has its columns pruned, and the second projection W2∈Rdffn×dmodel\mathbf{W}_2 \in \mathbb{R}^{d_{\text{ffn}} \times d_{\text{model}}} has its rows pruned correspondingly. Removing kk neurons shrinks both matrices and reduces FLOPs proportionally. The bias terms (if present) must also be updated consistently: the bias of the first layer loses kk entries, and if the second layer has a bias, it remains unchanged in dimension because it is added to the dmodeld_{\text{model}}-dimensional output rather than the intermediate dimension.

For models using gated linear units (such as the SwiGLU variant used in Llama), the feedforward sublayer has two parallel projections instead of one. Both projections must be pruned consistently: removing neuron nn requires removing column nn from both the gate projection and the value projection, and row nn from the output projection. The paired nature of gated feedforward layers means you need to be careful to prune both projections together.

Attention Head Width Pruning

Width pruning can also target the head dimension dkd_k within attention heads. Rather than removing entire heads, you reduce the dimensionality of each head by eliminating less-important dimensions of the query, key, and value projections. This is a finer-grained form of pruning that allows more gradual size reduction.

For attention head hh, the query projection WQ(h)∈Rdmodel×dk\mathbf{W}_Q^{(h)} \in \mathbb{R}^{d_{\text{model}} \times d_k} can be pruned by removing columns whose corresponding activations have low importance. The same columns must be removed from WK(h)\mathbf{W}_K^{(h)} and WV(h)\mathbf{W}_V^{(h)} to maintain dimensional consistency, and the corresponding rows from the output projection must also be removed. Note that the attention score computation divides by dk\sqrt{d_k}, so after reducing dkd_k, you must update this scaling factor accordingly.

Head dimension pruning is less commonly applied in practice than feedforward width pruning because the attention head dimensions are already relatively small (often 64 or 128), and reducing them further tends to hurt quality more than removing entire low-importance heads. Feedforward neurons are more numerous and more unevenly used, making them better targets for width pruning.

Hidden Dimension Pruning

The most aggressive form of width pruning targets the model dimension dmodeld_{\text{model}} itself, reducing the embedding size used throughout the entire model. This affects every weight matrix in the architecture simultaneously and requires careful identification of which dimensions to prune.

Principal component analysis on the activations at each layer can identify dimensions with low variance across inputs. These low-variance dimensions carry little information and are candidates for removal. You compute the covariance matrix of the activations across your calibration dataset, find the eigenvectors with the smallest eigenvalues, and remove the corresponding dimensions from all weight matrices simultaneously. This is related to the idea of intrinsic dimensionality: large language models often operate in a much lower-dimensional subspace than their nominal parameter count suggests.

Hidden dimension pruning is substantially harder to implement correctly than feedforward or head pruning because it requires coordinated changes to all weight matrices in the model. Every embedding matrix, every query, key, value, and output projection, every feedforward projection, and every layer normalization parameter must be updated. Small misalignments (for example, pruning the wrong corresponding rows and columns in two matrices) produce incorrect results without necessarily raising an obvious error. In practice, most practitioners use feedforward and head pruning rather than hidden dimension pruning because the former are safer to implement and still achieve substantial compression ratios.

Worked Example: Pruning a Transformer Encoder

To make these concepts concrete, consider a small BERT-like encoder with 6 layers, 8 attention heads per layer, a model dimension of 512, and a feedforward dimension of 2048. We will walk through each pruning strategy step by step with specific numbers.

Starting parameters:

The full model has approximately:

  • Attention weights: 6×4×5122=6,291,4566 \times 4 \times 512^2 = 6{,}291{,}456 parameters (Q, K, V, O projections per layer)
  • Feedforward weights: 6×2×(512×2048)=12,582,9126 \times 2 \times (512 \times 2048) = 12{,}582{,}912 parameters
  • Total (excluding embeddings): approximately 18.9 million parameters

With H=8H = 8 heads and dk=64d_k = 64 per head, each attention layer contains 4 projection matrices, each 512×512=262,144512 \times 512 = 262{,}144 parameters, for a total of about 1,048,576 per layer. The feedforward sublayer with dffn=2048d_{\text{ffn}} = 2048 has two projection matrices totalling 2×512×2048=2,097,1522 \times 512 \times 2048 = 2{,}097{,}152 parameters per layer. Across 6 layers, the feedforward sublayers collectively dominate the parameter count.

Step 1: Compute layer similarities

Run 1000 representative inputs through the model and compute input-output cosine similarity for each of the 6 layers. Suppose you find:

Cosine similarity between input and output for each transformer layer. Layers 3 and 4 are candidates for removal due to their near-identity behavior.
LayerCosine SimilarityInterpretation
10.72Large transformation
20.68Large transformation
30.95Near identity
40.91Near identity
50.74Large transformation
60.70Large transformation

Layers 3 and 4 have near-identity behavior and are candidates for removal. Notice that the high-similarity layers are not at the ends but in the middle, which is the typical pattern for BERT-style encoders. Removing layers 3 and 4 eliminates 2 of the 6 transformer blocks, reducing the transformer parameter count by 33% and cutting latency by roughly the same fraction (assuming attention and feedforward layers dominate computation, which is true for longer sequences).

Step 2: Head importance scoring

For each head in each of the remaining 4 layers (after considering layer removal), compute gradient-based importance scores. With 4 layers and 8 heads per layer, you have 32 heads in total. Suppose that roughly 6 heads across the 4 layers have importance scores more than one standard deviation below the mean. These 6 heads are candidates for removal, reducing the head count from 32 to 26 and shrinking the attention weight matrices proportionally.

The heads with the lowest importance scores are often concentrated in one or two specific layers. In the typical pattern, layers just after the removed layers (layer 5 in our example, which was previously layer 3 but is now adjacent to where layers 3 and 4 were removed) sometimes show reduced head importance because they had been receiving partially pre-processed representations from the removed layers and now receive less-processed input.

Step 3: Feedforward width assessment

For each feedforward sublayer in the remaining 4 layers, compute mean absolute activation for each of the 2048 intermediate neurons over your calibration set. If you find that roughly 20% of neurons have mean absolute activation below a threshold of 0.01, you can remove those 400 neurons per layer across 4 layers, totalling 1,600 neuron removals. Each removal eliminates one column from W1\mathbf{W}_1 and one row from W2\mathbf{W}_2 in that layer, proportionally reducing feedforward computation.

Step 4: Prune and recover

Remove layers 3 and 4 (leaving 4 layers), then remove the 6 low-importance heads from the 4 remaining layers, then remove the low-activation feedforward neurons. Fine-tune the resulting model for 5% of the original training steps with a learning rate 10 times smaller than the original. The resulting model has roughly:

  • 4 transformer layers instead of 6: 33% reduction in layer count
  • 26 heads across 4 layers instead of 32: 19% reduction in attention heads
  • 80% of feedforward neurons retained: 20% reduction in feedforward width

The combined effect on parameters is multiplicative across the architectural dimensions, typically producing a model around 40-50% smaller than the original with quality loss of 1-3 points on standard benchmarks after recovery fine-tuning.

Code Implementation

We implement structured pruning for a transformer model, focusing on head pruning and layer pruning with gradient-based importance scores.

Setting Up the Model

We start by building a simple multi-layer transformer encoder that we can prune. This implementation makes head pruning explicit by treating each head independently.

In[3]:
Code
from copy import deepcopy

import numpy as np
import torch
import torch.nn as nn


class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, num_heads, d_k=None):
        super().__init__()
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_k = d_k if d_k is not None else (d_model // num_heads)
        d_k_total = self.num_heads * self.d_k
        self.W_q = nn.Linear(d_model, d_k_total, bias=False)
        self.W_k = nn.Linear(d_model, d_k_total, bias=False)
        self.W_v = nn.Linear(d_model, d_k_total, bias=False)
        self.W_o = nn.Linear(d_k_total, d_model, bias=False)

    def forward(self, x):
        B, T, _ = x.shape
        H, dk = self.num_heads, self.d_k
        Q = self.W_q(x).view(B, T, H, dk).transpose(1, 2)
        K = self.W_k(x).view(B, T, H, dk).transpose(1, 2)
        V = self.W_v(x).view(B, T, H, dk).transpose(1, 2)
        scores = (Q @ K.transpose(-2, -1)) / (dk**0.5)
        attn = torch.softmax(scores, dim=-1)
        out = (attn @ V).transpose(1, 2).contiguous().view(B, T, H * dk)
        return self.W_o(out)


class TransformerBlock(nn.Module):
    def __init__(self, d_model, num_heads, d_ff):
        super().__init__()
        self.attn = MultiHeadAttention(d_model, num_heads)
        self.ff1 = nn.Linear(d_model, d_ff)
        self.ff2 = nn.Linear(d_ff, d_model)
        self.ln1 = nn.LayerNorm(d_model)
        self.ln2 = nn.LayerNorm(d_model)

    def forward(self, x):
        x = x + self.attn(self.ln1(x))
        x = x + self.ff2(torch.relu(self.ff1(self.ln2(x))))
        return x


class SimpleTransformerEncoder(nn.Module):
    def __init__(
        self,
        d_model=128,
        num_heads=8,
        d_ff=256,
        num_layers=6,
        num_classes=2,
        vocab_size=1000,
        max_len=32,
    ):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, d_model)
        self.blocks = nn.ModuleList(
            [
                TransformerBlock(d_model, num_heads, d_ff)
                for _ in range(num_layers)
            ]
        )
        self.classifier = nn.Linear(d_model, num_classes)

    def forward(self, x):
        h = self.embed(x)
        for block in self.blocks:
            h = block(h)
        return self.classifier(h.mean(dim=1))


torch.manual_seed(42)
model = SimpleTransformerEncoder(
    d_model=128, num_heads=8, d_ff=256, num_layers=6
)
Out[4]:
Console
Original model parameters: 920,066
Number of layers: 6
Heads per layer: 8

The model starts with a fixed number of parameters spread across 6 transformer blocks, each with 8 attention heads. Even at this small scale, the structural relationships between layers and heads are the same as in production-scale models: each block contains an attention sublayer and a feedforward sublayer connected by residual connections.

Computing Layer Importance Scores

We compute the cosine similarity between input and output for each layer. Layers where the hidden state barely changes are candidates for removal. The function below passes a batch through each block sequentially, saving the input before each block and comparing it to the output.

In[5]:
Code
def compute_layer_similarities(mdl, data_loader, device="cpu"):
    """Compute mean cosine similarity between input and output of each block."""
    mdl.eval()
    sims = [[] for _ in range(len(mdl.blocks))]
    with torch.no_grad():
        for x, _ in data_loader:
            x = x.to(device)
            h = mdl.embed(x)
            for i, block in enumerate(mdl.blocks):
                h_in = h.detach().clone()
                h = block(h)
                cos_sim = nn.functional.cosine_similarity(
                    h_in.reshape(-1, h_in.shape[-1]),
                    h.reshape(-1, h.shape[-1]),
                    dim=-1,
                )
                sims[i].append(cos_sim.mean().item())
    return [float(np.mean(s)) for s in sims]


def make_synthetic_data(
    n=512, seq_len=16, vocab_size=1000, num_classes=2, seed=42
):
    rng = np.random.default_rng(seed)
    X = rng.integers(0, vocab_size, (n, seq_len))
    y = rng.integers(0, num_classes, n)
    dataset = torch.utils.data.TensorDataset(
        torch.tensor(X, dtype=torch.long), torch.tensor(y, dtype=torch.long)
    )
    return torch.utils.data.DataLoader(dataset, batch_size=64, shuffle=False)


data_loader = make_synthetic_data()
layer_sims = compute_layer_similarities(model, data_loader)
Out[6]:
Console
Layer-wise cosine similarity (input vs output):
  Layer 1: 0.9670  #############################
  Layer 2: 0.9714  #############################
  Layer 3: 0.9699  #############################
  Layer 4: 0.9688  #############################
  Layer 5: 0.9722  #############################
  Layer 6: 0.9722  #############################

Pruning candidates (similarity > 0.85): layers [1, 2, 3, 4, 5, 6]

Layers with similarity above the threshold transform the representation very little and are the best candidates for removal. The text bar chart in the output gives an immediate visual sense of which layers are active and which are near-identity.

The bar chart below shows the cosine similarity for each layer, making the near-identity layers visually obvious.

Out[7]:
Visualization
Bar chart of cosine similarity per transformer layer with a red dashed threshold line.
Cosine similarity between each transformer layer's input and output hidden states, computed over the calibration dataset. Bars near 1.0 indicate layers that apply near-identity transformations and are strong candidates for removal. The dashed red line marks the pruning threshold; layers above it are removed during structured pruning.

Computing Head Importance Scores

For head pruning, we use gradient-based importance scores. We run a forward pass to compute the loss, backpropagate to get gradients, and compute the importance of each head using the gradient-weight product norm. This is the Taylor expansion criterion: it approximates how much the loss would change if that head's output were zeroed out.

In[8]:
Code
import torch.nn.functional as F


def compute_head_importance(mdl, data_loader, device="cpu"):
    """Compute importance of each attention head using gradient-weight product norm."""
    mdl.eval()
    num_layers = len(mdl.blocks)
    num_heads = mdl.blocks[0].attn.num_heads
    importance = torch.zeros(num_layers, num_heads)

    for x, y in data_loader:
        x, y = x.to(device), y.to(device)
        mdl.zero_grad()
        logits = mdl(x)
        loss = F.cross_entropy(logits, y)
        loss.backward()

        for l, block in enumerate(mdl.blocks):
            W_o = block.attn.W_o.weight
            grad = block.attn.W_o.weight.grad
            if grad is None:
                continue
            dk = block.attn.d_k
            for h in range(num_heads):
                start = h * dk
                end = start + dk
                score = (grad[:, start:end] * W_o[:, start:end]).norm()
                importance[l, h] += score.item()

    importance = importance / max(1, len(data_loader))
    return importance


head_importance = compute_head_importance(model, data_loader)
Out[9]:
Console
Head importance scores (gradient-weight norm):
  Layer 1: min=0.0018, max=0.0020  (least important: heads [1, 4])
  Layer 2: min=0.0019, max=0.0025  (least important: heads [5, 8])
  Layer 3: min=0.0021, max=0.0025  (least important: heads [2, 6])
  Layer 4: min=0.0023, max=0.0032  (least important: heads [4, 5])
  Layer 5: min=0.0024, max=0.0029  (least important: heads [7, 6])
  Layer 6: min=0.0022, max=0.0027  (least important: heads [5, 3])

Each row shows the range of head importance scores in a layer. The least important heads have scores well below the layer average and are removed first. Notice that the ratio between the minimum and maximum importance scores within a single layer can be substantial, confirming that heads within a layer are not equally important.

The heatmap below visualizes importance scores across all layers and heads, making it easy to see which heads cluster at the bottom of the importance distribution and should be removed first.

Out[10]:
Visualization
Heatmap of head importance scores per layer and head index.
Heatmap of gradient-based importance scores for all attention heads, organized by layer (rows) and head index (columns). Lighter cells represent heads with higher importance scores that are critical to model quality. Darker cells represent low-importance heads that are candidates for removal. The global pruning decision ranks all heads together, so heads in one layer compete with heads in other layers.

Performing Layer Pruning

With layer similarities computed, we remove the least important layers by creating a new model with a reduced number of transformer blocks. The pruning function deep-copies the model to preserve the original, then filters out the blocks at the specified indices.

In[11]:
Code
def prune_layers(mdl, layers_to_remove):
    """Create a new model with the specified layers removed."""
    pruned = deepcopy(mdl)
    remaining = [
        block
        for i, block in enumerate(pruned.blocks)
        if i not in layers_to_remove
    ]
    pruned.blocks = nn.ModuleList(remaining)
    return pruned


high_sim_threshold = 0.85
layers_to_remove = sorted(
    [i for i, s in enumerate(layer_sims) if s > high_sim_threshold],
    reverse=True,
)[:2]

model_layer_pruned = prune_layers(model, layers_to_remove)
Out[12]:
Console
Layers removed: [6, 5]
Remaining layers: 4
Original parameters:      920,066
Layer-pruned parameters:  656,130
Parameter reduction:      28.7%

Removing layers that apply near-identity transformations eliminates their parameters while preserving the model's functional behavior as closely as possible. The parameter reduction is proportional to the number of layers removed divided by the total number of layers, which for two layers out of six is roughly 33%.

Performing Head Pruning

We implement head pruning by physically removing the head-corresponding slices from the attention weight matrices. The function below handles the index arithmetic carefully: each head occupies a contiguous slice of columns in the Q, K, V matrices and a contiguous slice of rows in the output projection.

In[13]:
Code
def prune_heads_in_layer(attn_module, heads_to_remove):
    """
    Physically remove heads from a MultiHeadAttention module by slicing weight matrices.
    Ensures at least 1 head is retained to avoid zero-dimension matrices.
    """
    H = attn_module.num_heads
    dk = attn_module.d_k
    d_model = attn_module.d_model

    keep_heads = [h for h in range(H) if h not in heads_to_remove]
    # Guard: always keep at least 1 head
    if len(keep_heads) == 0:
        keep_heads = [0]
    new_H = len(keep_heads)

    keep_cols = []
    for h in keep_heads:
        keep_cols.extend(range(h * dk, (h + 1) * dk))

    # Pass d_k explicitly so __init__ does not recompute it from d_model // num_heads
    new_attn = MultiHeadAttention(d_model, new_H, d_k=dk)

    with torch.no_grad():
        new_attn.W_q.weight.copy_(attn_module.W_q.weight[keep_cols, :])
        new_attn.W_k.weight.copy_(attn_module.W_k.weight[keep_cols, :])
        new_attn.W_v.weight.copy_(attn_module.W_v.weight[keep_cols, :])
        new_attn.W_o.weight.copy_(attn_module.W_o.weight[:, keep_cols])

    return new_attn


def prune_heads(mdl, importance, prune_fraction=0.25):
    """Prune the least important heads globally across all layers."""
    pruned = deepcopy(mdl)
    num_layers = len(pruned.blocks)
    num_heads = pruned.blocks[0].attn.num_heads
    total_heads = num_layers * num_heads
    n_prune = max(1, int(total_heads * prune_fraction))

    flat = [
        (importance[l, h].item(), l, h)
        for l in range(num_layers)
        for h in range(num_heads)
    ]
    flat_sorted = sorted(flat, key=lambda x: x[0])
    to_remove_flat = flat_sorted[:n_prune]

    heads_by_layer = {}
    for _, l, h in to_remove_flat:
        heads_by_layer.setdefault(l, []).append(h)

    for l, heads in heads_by_layer.items():
        pruned.blocks[l].attn = prune_heads_in_layer(
            pruned.blocks[l].attn, heads
        )

    return pruned, heads_by_layer


model_head_pruned, removed_heads = prune_heads(
    model, head_importance, prune_fraction=0.25
)
Out[14]:
Console
Removed heads per layer:
  Layer 1: heads [1, 2, 3, 4, 5, 6, 7, 8]
  Layer 2: heads [2, 5, 8]
  Layer 3: heads [2]

Original parameters:     920,066
Head-pruned parameters:  829,954
Parameter reduction:     9.8%

The pruned model has fewer parameters in its attention layers, with the size reduction proportional to the number of heads removed. The global selection strategy means the removed heads are concentrated in layers where importance scores are most unevenly distributed, which is exactly the desired behavior.

Combining Layer and Head Pruning

In practice, you apply layer pruning and head pruning together to maximize compression while minimizing quality loss. The combined function first removes low-similarity layers, then recomputes head importance on the smaller model and removes low-importance heads.

In[15]:
Code
def prune_combined(
    mdl,
    data_loader,
    layer_sim_threshold=0.85,
    head_prune_fraction=0.20,
    max_layers_to_remove=2,
):
    """Apply both layer pruning and head pruning in sequence."""
    sims = compute_layer_similarities(mdl, data_loader)
    layers_to_drop = sorted(
        [i for i, s in enumerate(sims) if s > layer_sim_threshold], reverse=True
    )[:max_layers_to_remove]
    model_lp = prune_layers(mdl, layers_to_drop)

    importance_lp = compute_head_importance(model_lp, data_loader)
    model_comb, removed = prune_heads(
        model_lp, importance_lp, prune_fraction=head_prune_fraction
    )
    return model_comb, layers_to_drop, removed


model_combined, removed_layers, removed_heads_info = prune_combined(
    model, data_loader, layer_sim_threshold=0.85, head_prune_fraction=0.20
)
Out[16]:
Console
Combined structured pruning results:
  Layers removed: [6, 5] (2 layer(s))
  Remaining layers: 4
  Original parameters:    920,066
  Combined pruning:       606,978
  Total reduction:        34.0%

Combining both strategies achieves substantially more compression than either alone. The sequential order matters: by removing layers first and then recomputing head importance on the smaller model, we ensure the head importance scores reflect the actual model state after layer removal, rather than importance scores computed on a different (larger) architecture.

The three pruning strategies target different parts of the model architecture, and their parameter savings compound when applied together. The visualization below shows how each strategy contributes to the total reduction.

Out[17]:
Visualization
Bar chart comparing parameter counts across original and three pruned model variants.
Parameter reduction achieved by each structured pruning strategy applied to the demonstration model. Layer pruning removes entire transformer blocks and achieves the largest single reduction. Head pruning removes attention heads globally, producing a smaller but still meaningful reduction. The combined strategy applies both in sequence, achieving greater compression than either alone, with total parameter counts computed dynamically from the model objects.

Key Parameters

The key parameters for structured pruning are:

  • layer_sim_threshold: Cosine similarity threshold above which a layer is considered near-identity. Typical values range from 0.80 to 0.95. Setting this too low removes important layers; setting it too high removes nothing. Start at 0.90 and adjust based on observed quality on a validation set.
  • head_prune_fraction: Fraction of total attention heads to remove globally. Start conservatively at 10-20% and increase only after verifying quality recovery at lower fractions.
  • max_layers_to_remove: Upper bound on how many layers to prune in a single pass. Removing too many at once makes recovery harder. For 12-layer models, start with at most 2 layers per pruning round.
  • recovery_steps: Number of fine-tuning steps after pruning. Typically 5-20% of original training steps, with a learning rate 5-10 times smaller than the original.

Pruning Schedule and Iterative Pruning

A single aggressive pruning pass often damages quality more than a series of smaller pruning steps with fine-tuning between each step. Iterative pruning cycles through: prune a small amount, fine-tune to recover, evaluate quality, and repeat until the target size is reached. Each fine-tuning phase allows the model to stabilize before the next round of pruning begins. The accumulated effect of many small, recoverable pruning steps is a final model that is both smaller and higher quality than you would get from one large pruning step.

The intuition behind iterative pruning is that importance scores change after each pruning round. When you remove a low-importance head, the remaining heads may redistribute their roles. A head that appeared unimportant under the original model may become more important when another head in the same layer is removed, because the remaining head must now cover both roles. Iterative pruning updates importance scores between rounds, catching these redistributions and making better decisions.

The pruning schedule can be described as a function that determines what fraction of the model to prune at each step tt:

st=sf+(si−sf)⋅(1−t−t0n⋅Δt)3s_t = s_f + (s_i - s_f) \cdot \left(1 - \frac{t - t_0}{n \cdot \Delta t}\right)^3

where:

  • sts_t is the sparsity at step tt
  • sis_i is the initial sparsity (often 0)
  • sfs_f is the final target sparsity
  • t0t_0 is the step at which pruning begins
  • nn is the number of pruning steps
  • Δt\Delta t is the interval between pruning steps

The cubic term (1−t−t0n⋅Δt)3\left(1 - \frac{t - t_0}{n \cdot \Delta t}\right)^3 is the key feature of this schedule. At the beginning of pruning (when tt is just above t0t_0), the term is close to 1, so sts_t is close to sis_i: pruning starts slowly. As tt increases toward t0+n⋅Δtt_0 + n \cdot \Delta t, the term approaches 0 and sts_t approaches sfs_f: pruning accelerates toward the target. Near the end of the schedule, as the target approaches, the cubic function causes pruning to decelerate again, making fine adjustments rather than large jumps. This S-shaped progression (slow, fast, slow) is better matched to how models learn than a constant linear rate.

The cubic schedule starts pruning slowly, accelerates through the middle, then slows down again near the target. This allows the model to accommodate each wave of removals before more components are pruned. The deceleration near the target is particularly important: the final few percentage points of sparsity are often the hardest to achieve without quality loss, and the schedule naturally allocates more fine-tuning time near the target.

The chart below compares the cubic schedule to a linear schedule. The cubic schedule removes fewer components early in training, when the model is still adjusting from prior fine-tuning, and reaches the target sparsity more gradually.

Out[18]:
Visualization
Line chart comparing cubic and linear pruning schedules over training steps.
Comparison of cubic and linear pruning schedules over 100 training steps, both targeting 50% final sparsity starting from step 10. The cubic schedule (blue) removes components slowly at first, accelerates through the middle, and decelerates near the target. The linear schedule (orange) reduces the model at a constant rate. In practice, the cubic schedule gives the model more time to adapt at the critical early and late stages of pruning.

Calibration Data Requirements

All importance-based pruning methods require a calibration dataset: a representative set of inputs used to compute activation statistics, gradients, or similarity scores. The quality of pruning decisions depends directly on how representative this calibration set is.

For a deployed sentiment classifier, you should calibrate using typical user inputs from production traffic rather than the original training data. The distribution of production traffic often differs from the training distribution in ways that affect which components are important. For a question-answering model, the calibration set should include questions across diverse domains, because heads and layers that handle medical questions may differ from those that handle programming questions.

Using an unrepresentative calibration set leads to pruning decisions optimized for the wrong inputs. A model calibrated on formal text may incorrectly identify heads important for informal language as low-importance, leading to quality degradation on real user queries. A model calibrated on short sentences may retain heads that primarily contribute to long-range dependencies, which are underutilized on short inputs but critical for longer ones.

In practice, 1,000 to 10,000 representative inputs provide stable importance estimates for most models. Using far fewer (under 100) leads to noisy estimates and worse pruning decisions, because the sample size is too small to average out example-specific variation in which heads activate. Using more than 10,000 inputs rarely improves importance score quality but increases the computational cost of calibration linearly.

The calibration set should not be the same as the fine-tuning dataset used for recovery. Using the same data for both calibration (to compute importance scores) and recovery fine-tuning (to restore quality) can lead to overfitting, where the pruned model becomes optimized specifically for that dataset rather than the broader task distribution. A practical recommendation is to split your available labeled data three ways: a calibration set for importance scoring (roughly 10-20%), a fine-tuning set for recovery (roughly 70-80%), and a validation set for measuring quality throughout the pruning process (roughly 10-20%).

Structured Pruning in Practice

Several production systems have adopted structured pruning as part of their model deployment pipeline. The patterns that have emerged from these deployments provide practical guidance that goes beyond what the research literature covers.

Task-specific pruning is more effective than generic pruning. When you know your target task, you can calibrate on task-relevant data and fine-tune specifically for that task, recovering more quality at the same compression ratio than you would achieve with task-agnostic pruning. A BERT model pruned for named entity recognition on biomedical text can be much more aggressively compressed than a general-purpose BERT model, because many of the model's general linguistic capabilities are irrelevant for that specific task.

Pruning interacts with other compression techniques. Quantization applied after pruning compounds the savings. A model that is 40% smaller from structured pruning and then quantized to 8-bit precision can be 70-80% smaller than the original full-precision model. The quality loss from quantization tends to be smaller when applied to a pruned model, because the pruned model has already been fine-tuned to work well with fewer components. Applying distillation after pruning is also effective: you prune first to get the target architecture, then use the original model as a teacher to improve the quality of the pruned model beyond what recovery fine-tuning alone achieves.

Early detection of pruning failures saves time. After each pruning round, evaluate on your validation set before proceeding to the next round or starting full recovery fine-tuning. If the quality drop after pruning is much larger than expected (more than 10-15 points on typical benchmarks for mild pruning), it usually indicates that you have removed a critical component. Stop and diagnose before investing compute in recovery fine-tuning that is unlikely to succeed.

Not all tasks prune equally well. Tasks that require broad linguistic knowledge (such as reading comprehension across diverse domains) tend to prune less gracefully than narrow tasks (such as binary sentiment classification on product reviews). The former relies on many components working together; the latter uses a small subset of the model's capabilities and can tolerate removing large parts of the rest.

Limitations and Practical Considerations

Structured pruning delivers real speedups but comes with significant practical challenges that affect how aggressively you can prune in production settings.

The most fundamental limitation is the accuracy-efficiency tradeoff. Every component you remove carries some information that the model was using, even if the component was relatively unimportant. With mild pruning (removing 10-20% of heads or 1-2 layers from a 12-layer model), quality usually recovers fully after fine-tuning. With aggressive pruning (removing 40-50% of components), some permanent quality degradation is unavoidable. The degree of degradation depends on the task: tasks that require broad linguistic knowledge degrade more than narrow tasks that use only a subset of the model's capabilities.

Recovery fine-tuning requires labeled data for the downstream task. This is straightforward for supervised classification tasks but harder for generative models, where you need a proxy objective that correlates with generation quality. Using next-token prediction loss on a representative text corpus works for language models, but the quality of the pruned generative model may not improve as smoothly as for classifiers. For generative models, human evaluation or automatic metrics like ROUGE or BERTScore should be used to track quality throughout the pruning process, not just training loss.

The importance score computation itself has limitations. Gradient-based importance scores are computed on a specific calibration dataset and may not generalize across domains. A head that is unimportant for formal news text may be necessary for informal conversational text. This means pruning decisions made for one deployment context may not transfer well to other contexts, requiring re-pruning when the use case changes.

Structured pruning interacts with other compression techniques in important ways. Applying quantization after pruning compounds the compression but also compounds potential quality loss. Applying distillation after pruning is more stable: you prune first to get the target architecture, then distill to recover quality from a larger teacher. This two-stage pipeline (prune then distill) often outperforms either technique applied alone.

Head pruning has an interesting failure mode: when you remove an attention head, the surviving heads may not fully compensate because their query and key spaces were trained independently. Multi-head attention does not guarantee that any single head can replicate the behavior of another. Recovery fine-tuning helps, but there are limits to how much compensation is possible without retraining from scratch.

Layer pruning has a different failure mode: removing middle layers can disrupt the flow of information from early to late layers. If removed layers were responsible for building up specific intermediate representations that later layers relied on, the resulting model cannot route information correctly even after fine-tuning. Careful importance scoring based on layer similarity helps identify which layers are safe to remove, but abrupt removal of critical layers can be difficult to recover from. This is why the iterative approach (remove one layer at a time with fine-tuning between rounds) is more reliable than removing multiple layers simultaneously.

Finally, structured pruning is sensitive to the model's pretraining history. Models that were pretrained with larger batches and for more steps tend to be more amenable to pruning, because they have had more opportunity to develop redundancy in their components. Models that were undertrained relative to their size (common in resource-constrained academic settings) often cannot be pruned as aggressively because their components are not as redundant. When working with a model of unknown provenance, start with a conservative pruning ratio and increase gradually while monitoring quality.

Summary

Structured pruning removes entire architectural components from transformers, producing models that run faster on any hardware without requiring specialized sparse computation support.

  • Head pruning removes entire attention heads using importance scores based on gradient magnitudes, activation norms, or loss sensitivity. After removal, the attention projection matrices shrink and every forward pass uses fewer FLOPs. Global pruning across all layers outperforms layer-by-layer pruning because it identifies layers with more redundant heads.
  • Layer pruning removes complete transformer blocks. The cosine similarity between a block's input and output identifies near-identity layers that contribute little. Middle layers are often the best candidates, following the U-shaped importance profile observed across BERT, GPT, and Llama model families. Angular distance (via arccos⁡\arccos) provides a scale-invariant version of this criterion.
  • Width pruning reduces the internal dimensions of feedforward sublayers or attention head dimensions by eliminating low-activation neurons. This offers fine-grained control over model size that complements the coarser-grained head and layer pruning strategies. Gated feedforward layers (SwiGLU) require coordinated pruning of both the gate and value projections.
  • Iterative pruning with recovery fine-tuning between rounds achieves better final quality than a single aggressive pruning pass. Cubic or polynomial schedules balance compression speed and quality retention by allowing the model more adaptation time at the critical early and late stages of the pruning process.
  • Calibration data quality determines how accurate importance scores are. Use representative data from the actual deployment context, and keep calibration and fine-tuning data separate to avoid overfitting.
  • Combining techniques amplifies compression ratios. Structured pruning reduces architecture size, quantization reduces numerical precision, and distillation transfers knowledge from a larger model. Together, these techniques make large language models deployable in contexts where the original models would be impractical.

Structured pruning is most powerful when combined with other compression techniques. Pruning reduces architecture size, quantization reduces numerical precision, and distillation transfers knowledge from a larger model. Together, these techniques make large language models deployable in contexts where the original models would be impractical. The next part of this handbook explores reasoning capabilities in language models, where we will see how efficient inference from compressed models enables deploying chain-of-thought reasoning in constrained environments.

Quiz

Ready to test your understanding? Take this quick quiz to reinforce what you've learned about structured pruning.

Structured Pruning Quiz

Question 1 of 80 of 8 completed
What is the key advantage of structured pruning over unstructured pruning?

Comments

No comments yet. Be the first to share your thoughts!

Reference

Citation details

Cite or share this article.

BIBTEXAcademic
@misc{brenndoerfer2026structuredpruning, author = {Michael Brenndoerfer}, title = {Structured Pruning: Head, Layer, and Width Pruning}, year = {2026}, url = {https://mbrenndoerfer.com/writing/structured-pruning-head-layer-width-implementation}, organization = {mbrenndoerfer.com}, note = {Accessed: 2026-10-15} }
APAAcademic
Michael Brenndoerfer (2026). Structured Pruning: Head, Layer, and Width Pruning. Retrieved from https://mbrenndoerfer.com/writing/structured-pruning-head-layer-width-implementation
MLAAcademic
Michael Brenndoerfer. "Structured Pruning: Head, Layer, and Width Pruning." 2026. Web. October 15, 2026. <https://mbrenndoerfer.com/writing/structured-pruning-head-layer-width-implementation>.
CHICAGOAcademic
Michael Brenndoerfer. "Structured Pruning: Head, Layer, and Width Pruning." Accessed October 15, 2026. https://mbrenndoerfer.com/writing/structured-pruning-head-layer-width-implementation.
HARVARDAcademic
Michael Brenndoerfer (2026) 'Structured Pruning: Head, Layer, and Width Pruning'. Available at: https://mbrenndoerfer.com/writing/structured-pruning-head-layer-width-implementation (Accessed: October 15, 2026).
SimpleBasic
Michael Brenndoerfer (2026). Structured Pruning: Head, Layer, and Width Pruning. https://mbrenndoerfer.com/writing/structured-pruning-head-layer-width-implementation

About the author

Continue with the full handbook

This chapter is part of Language AI Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.

Explore Language AI Handbook
Newsletter

Stay up to date

Get articles, book updates, and news delivered to your inbox.

No spam, unsubscribe anytime.

or

Join the community

Sign in to remove popups, track your reading progress, and join the discussion.