James T. O'Neill (University of Liverpool), 2020

A Survey of Neural Network
Compression

Five ways to shrink an overparameterized network — weight sharing, pruning, low-rank decomposition, knowledge distillation, and quantization — one mental map, with the bit-level math of quantization made interactive.

Prerequisites: Backpropagation + Floating point + Basic linear algebra
11
Chapters
6
Simulations

Chapter 0: The Problem

You trained a model. It works. Then you try to ship it — onto a phone, an embedded chip, a server you actually have to pay for — and you hit a wall. A modern Transformer has hundreds of millions to billions of parameters. GPT-3, the largest model when this survey was written, has 175 billion of them. At 4 bytes each (FP-32), that is 700 GB just to store the weights, never mind run them.

The survey opens with an uncomfortable fact: pushing accuracy has meant making models bigger, and the trend shows no sign of stopping. MegatronLM was 8.3 billion parameters across 512 GPUs. The carbon footprint, the VRAM, the storage — all of it has put state-of-the-art models out of reach for most practitioners.

The core question of the whole survey: Given a large, accurate, overparameterized network that already exists, how do we make it smaller and faster — fewer bytes, fewer FLOPs — while losing as little accuracy as possible?

Why not just train a small model from scratch?

This is the first objection everyone raises, and the survey answers it head-on. The empirical finding, repeated across pruning, distillation, and the lottery-ticket literature, is that a large network compressed down to size N beats a small network of size N trained from scratch.

The intuition: a network with more parameters has more degrees of freedom — a bigger menu of solutions in parameter space — and overparameterization smooths the loss landscape so SGD finds better minima. Train big, then compress, and you inherit a good solution that a small-from-scratch model could never have found. Frankle & Carbin's lottery ticket hypothesis pushed this further: inside the big trained network there is a small sub-network that, with the right initialization, matches the full network's accuracy.

How do we even measure "compressed"?

Three numbers, and they do not always move together — this is the trap that catches beginners:

MetricWhat it measuresGotcha
Model size (params × bits)Storage / download / RAM footprintSparse models save size only if you store indices efficiently
FLOPsCompute per forward pass → speedUnstructured sparsity rarely speeds up real hardware
Run-time memoryActivation storage during inferenceQuantizing weights doesn't shrink activations unless you quantize those too

Almost everything below is a curve in the accuracy vs. one-of-these-numbers plane. The art of compression is moving up-and-left on that curve: same accuracy, smaller/faster — or a tiny accuracy hit for a huge size win.

Why does the survey argue for "train big, then compress" over "train small from scratch"?

Chapter 1: The Map of Compression

A survey's real gift is a taxonomy — a single picture that tells you where each technique lives and why they're different. There are five families, and they attack the parameter count from genuinely different angles.

The unifying view: Every weight tensor is a big grid of numbers. You can make that grid cheaper in exactly four ways: (1) store fewer distinct numbers (sharing), (2) set some numbers to zero and skip them (pruning), (3) express the grid as a product of smaller grids (decomposition), or (4) store each number in fewer bits (quantization). Distillation is the odd one out — it doesn't shrink the grid, it trains a new, smaller grid to imitate the big one.

The five families at a glance

FamilyCore moveWhat it reduces
Weight sharingMany weights point to one shared value (clusters, hashing, tied layers)# distinct values
PruningRemove unimportant weights / neurons / filters → set to zero# non-zero params
Low-rank / tensor decomp.Factor a big matrix W ≈ U·V into thin matrices# params & FLOPs
Knowledge distillationTrain a small "student" to mimic a big "teacher"whole network
QuantizationStore each value in fewer bits (FP32 → INT8 → binary)bits per value

Two cross-cutting distinctions that decide everything

Before any specific method, the survey hammers two axes that recur in every family:

retrain or not?
Post-hoc compression (no retraining) is fast but degrades accuracy more. Retraining (fine-tuning the compressed net) recovers accuracy but costs compute. The deeper you compress, the more you need retraining.
structured or unstructured?
Unstructured = remove individual weights anywhere → high flexibility, high sparsity, but needs sparse-matrix libraries and is often SLOWER on real hardware. Structured = remove whole filters/channels/heads → less flexible, less sparsity, but a genuine dense speedup.
The hardware truth nobody tells you first: unstructured magnitude pruning can zero out 90% of weights and still not run faster on a GPU — because dense matrix multiply is so optimized that sparse matmul (SMP) on irregular sparsity is slower. That is why structured pruning exists, and why quantization (which keeps the grid dense) is often the most practically useful family.
Which family does NOT shrink the original weight grid, but instead trains a new smaller one?

Chapter 2: Weight Sharing

Start with the simplest idea. A weight matrix has, say, a million distinct floating-point values. What if many of those weights could share a single value? Then you only store the few shared values plus a tiny index per weight saying which shared value it uses.

This is the cheapest form of compression because it doesn't change the network's shape at all — same layers, same forward pass — it just reduces how many distinct numbers you must store.

Clustering-based sharing (the workhorse)

Run k-means on all the weights of a layer. Pick k cluster centroids. Replace every weight with the centroid of its cluster. Now instead of a million 32-bit floats you store: k centroids (the "codebook") plus a million log₂(k)-bit indices.

Worked example. A layer has 1,000,000 weights at FP-32 = 4,000,000 bytes. Cluster into k = 16 centroids:

Storage componentSize
Codebook: 16 centroids × 4 bytes64 bytes
Indices: 1,000,000 × log₂(16) = 4 bits each500,000 bytes
Total vs. 4,000,000 bytes≈ 8× smaller

The codebook is negligible; the win comes from 4 bits per index instead of 32 bits per float. This is exactly the same lever quantization pulls (Ch 7) — sharing is "learned, non-uniform quantization."

Soft weight sharing (Nowlan & Hinton): instead of hard k-means, fit a Gaussian mixture over the weights as a learned prior. Each weight is softly pulled toward a mixture component during training. A broad Gaussian penalizes large weights gently; a tight Gaussian forces a subset of weights together with low variance. After training, components with low KL divergence are merged. Ullrich et al. merged 17 components down to 6 with near-zero accuracy loss on LeNet/MNIST — sharing and quantization unified under one Bayesian objective.

Other flavors of sharing

Weight Sharing via k-means — drag the slider

The grey dots are raw weights. Each colored band snaps its weights to one shared centroid (the vertical line). Fewer centroids = more compression but bigger error (the grey-to-line gaps). This is the size-vs-error tradeoff every compression method faces.

With 1M weights, clustering to k=16 centroids gives roughly what compression over FP-32?

Chapter 3: Pruning — Removing Weights

If sharing reuses values, pruning deletes them. The survey calls pruning "perhaps the most commonly used technique." The premise: most weights in a trained network are nearly useless — set them to exactly zero and the network barely notices.

There's even a neuroscience parallel the survey draws: as humans learn, the brain prunes synaptic connections, keeping the salient ones. The key word is salient — random pruning hurts; selective pruning works.

Magnitude-based pruning (MBP) — the default

The simplest rule that actually works: the smaller |w|, the less it matters. Set a threshold γ; any weight with |w| < γ becomes zero. Or pick a target sparsity (say 90%) and zero out the 90% of weights closest to zero.

prune wi  ⇔  |wi| < γ

Then retrain (fine-tune the survivors) to recover the accuracy lost. Iterative MBP repeats: prune a little, retrain, prune more, retrain — until the size/accuracy tradeoff is met. Iterating beats one-shot pruning because each retraining lets the network compensate.

Global beats layer-wise. You can apply the threshold per-layer (same sparsity everywhere) or globally (rank all weights in the whole network together). Global wins, because it gives the network freedom to keep one layer dense and another very sparse — important layers stay rich, redundant ones get gutted. The survey reports this finding holds across decades of work.

Pruning via regularization (push weights toward zero first)

Instead of pruning whatever happens to be small, train the network to make weights small. Add an L2 penalty:

C(w) = L(w) + ½ Σi wi2

But the survey flags a subtle flaw: plain L2 decays all weights at the same exponential rate and over-penalizes large ones. Weigend et al.'s fix uses a saturating penalty that approximates "count of non-zero weights":

C(w) = L(w) + Σi  wi2 / (1 + wi2)

For small |w| this term ≈ w², penalizing it; for large |w| it saturates to 1, so big weights aren't crushed. The derivative 2w/(1+w²)² stops pulling hard on already-large weights — exactly what you want when large weights are the important ones.

The lottery ticket twist: prune before training

Lottery Ticket Hypothesis (Frankle & Carbin): there exist sparse sub-networks ("winning tickets") that, trained from scratch with their original initialization, match the full network. The recipe: (1) init f(x;θ₀), (2) train to θ, (3) prune p% by magnitude → mask m, (4) reset survivors to their θ₀ values, retrain. The reset is the magic — the same sparse mask with random re-init fails. Liu et al. then argued the architecture (the mask) matters more than the inherited weights, reframing pruning as architecture search.
Iterative Magnitude Pruning — raise the sparsity

Each square is a weight; brighter = larger |w|. Pruned weights go dark. Watch how global mode keeps the dense, salient column while gutting the weak one — per-layer mode forces equal sparsity everywhere, killing useful weights in the strong column.

In the Lottery Ticket recipe, why reset surviving weights to their ORIGINAL initialization θ₀?

Chapter 4: Second-Order Pruning

Magnitude is a crude proxy. A weight can be small yet important (a tiny lever on a sensitive output), or large yet redundant. The right question isn't "how big is w?" but "how much does the loss change if I delete w?" That is loss-sensitivity pruning, and the survey's centerpiece here is Optimal Brain Damage.

Manufacturing the need: a Taylor expansion of the loss

We want δL, the change in loss when we perturb the weights by δw. Expand L around the trained weights with a Taylor series:

δL = Σi gi δwi + ½ Σi hii δwi2 + ½ Σi≠j hij δwiδwj + O(‖δw‖3)

Define every symbol with its plain meaning:

LeCun's three assumptions (this is the derivation)

OBD makes the messy expansion tractable with three honest approximations:

1. trained → g ≈ 0
At a converged minimum the gradient is ~zero. So the first-order term drops out. (This is WHY pruning is done post-training, not mid-training.)
2. diagonal Hessian
Assume off-diagonal hij = 0 — weights' deletions don't interact. Drops the cross terms. (False, but cheap; Optimal Brain Surgeon later keeps them.)
3. drop higher order
Small perturbations → O(‖δw‖³) ≈ 0.

What survives is beautifully simple:

δL ≈ ½ Σi hii δwi2

To delete weight i, δwi = −wi, so its saliency — the loss damage from removing it — is:

si = ½ hii wi2
Read what this means. Saliency multiplies magnitude squared (wi²) by curvature (hii). Magnitude pruning uses only the first factor. OBD adds the second: a small weight on a high-curvature (sensitive) direction has high saliency and is kept, even though MBP would have killed it. That is the whole improvement — and exactly why second-order pruning beats magnitude pruning when the network isn't wildly overparameterized.

Worked example: when magnitude lies

Two weights. w₁ = 0.10 with curvature h₁₁ = 50. w₂ = 0.30 with curvature h₂₂ = 1.

Weight|w|MBP would pruneOBD saliency ½·h·w²OBD keeps
w₁0.10 (small)✗ prune it½·50·0.01 = 0.25✓ keep — high saliency
w₂0.30 (large)✓ keep it½·1·0.09 = 0.045✗ prune — low saliency

Magnitude pruning and OBD make opposite decisions here. OBD is right: deleting w₁ costs 0.25 of loss; deleting w₂ costs only 0.045. The curvature carried the information magnitude missed.

Saliency: Magnitude vs. Optimal Brain Damage

Each dot is a weight at position (|w|, curvature). Toggle the ranking: MBP prunes by the x-axis alone (leftmost dots). OBD prunes by the product ½·h·w² (the contour). Notice the small-but-curved weights OBD rescues from the chopping block.

Optimal Brain Surgeon (the sequel): Hassibi et al. showed the diagonal-Hessian assumption actually hurts — the off-diagonal terms matter. OBS keeps the full inverse Hessian H⁻¹ (computed recursively to stay feasible) and, as a bonus, gives a closed-form update to the remaining weights so you often don't even need to retrain. The cost: H⁻¹ is huge for big nets, which is why diagonal OBD-style and magnitude pruning stayed dominant in practice.
OBD's saliency is s = ½·h·w². What does it capture that magnitude pruning misses?

Chapter 5: Low-Rank Decomposition

Pruning makes the grid sparse. Decomposition makes it small by exploiting a different kind of redundancy: a big matrix often has low rank — its rows are nearly linear combinations of a few basis rows. If so, you can factor it into the product of two thin matrices and store far fewer numbers, with the grid staying dense (so it's a real hardware speedup).

The arithmetic of why this saves so much

A fully-connected layer is a matrix multiply W·x where W is m×n. Storing W costs m·n parameters; computing W·x costs m·n multiply-adds. Approximate W ≈ U·V where U is m×r and V is r×n, with rank r ≪ min(m,n):

W(m×n) ≈ U(m×r) · V(r×n)

Now storage is m·r + r·n = r·(m+n), and W·x = U·(V·x) costs r·(m+n) FLOPs too. The ratio:

compression =   m·n  /  r·(m+n)

Worked example: a 1024×1024 layer (1,048,576 params). Pick rank r = 64:

QuantityFull WU·V at r=64
Parameters1,048,57664·(1024+1024) = 131,072
FLOPs per token1,048,576131,072
Ratio8× fewer
Where does the rank come from? The Singular Value Decomposition: W = UΣVᵀ, where the singular values in Σ are sorted big-to-small. Keep the top-r singular values (truncated SVD) and you get the best possible rank-r approximation of W (Eckart–Young theorem). The error you pay is exactly the energy in the discarded singular values — so if W's spectrum decays fast (most do), tiny r captures almost everything.

Tensors, not just matrices

Conv layers and attention are 4-D / higher-order tensors, not flat matrices, so the survey covers tensor decompositions (Tucker, CP, Block-Term / Tensor-Train). The CNN trick: a k×k convolution over C channels is a 4-D tensor that decomposes into a sequence of cheaper convs — e.g. a filter decomposition (separable spatial convs) and a channel-wise decomposition (1×1 convs that mix channels). MobileNet's depthwise-separable convolution is exactly this idea baked into the architecture from the start.

Truncated SVD — keep the top-r singular values

The bars are singular values (energy), sorted. The shaded region is what rank-r keeps; the rest is the reconstruction error. A fast-decaying spectrum means a small r already captures most energy — that's when low-rank compression pays off.

Factoring a 1000×1000 matrix into rank-50 factors gives roughly what compression?

Chapter 6: Knowledge Distillation

The four families so far all edit the existing grid. Distillation does something different and clever: it trains a brand-new small network (the student) to imitate the big trained network (the teacher). The student inherits the teacher's behavior without ever being big.

Manufacturing the need: why not just train the student on the labels?

You could train the small network directly on the one-hot ground-truth labels. It works worse. The reason is that the teacher's output is richer than a hard label. When a teacher classifies a "2", it might say 90% "2", 8% "7", 2% "3" — encoding that 2s look more like 7s than 3s. Those relative probabilities across wrong classes are the "dark knowledge." One-hot labels throw it all away.

Temperature is the dial that exposes dark knowledge. A confident softmax is nearly one-hot — the dark knowledge is squashed to ~0. Divide the logits by a temperature ρ > 1 before softmax and the distribution softens, lifting those small wrong-class probabilities into a usable signal. High ρ = softer = more inter-class structure revealed.

The distillation loss (Hinton et al.)

Train the student with a blend of two losses:

LKD = (1 − α)·H(y, yS)  +  α·ρ2·KL( φ(zT/ρ) ‖ φ(zS/ρ) )

Symbol by symbol:

The student learns both the answer (hard labels) and the teacher's whole worldview (soft targets). Buciluǎ et al. did the original version (training a small net on a big ensemble's pseudo-labels); Hinton et al. formalized the temperature-softmax recipe.

Temperature softens the teacher's logits — revealing dark knowledge

Same fixed teacher logits, different ρ. At ρ=1 the softmax is spiky (one-hot-ish) and the wrong-class probabilities — the dark knowledge — are invisible. Raise ρ and those small bars grow into a usable training signal. That softened shape is what the student is taught to match.

Why raise the softmax temperature when distilling?

Chapter 7: Quantization — The Showcase

Here is the family most likely to ship in production, and the survey's most hardware-concrete section. The other families change how many numbers you store; quantization changes how many bits each number takes. Go from FP-32 to INT-8 and you've cut storage 4× and unlocked integer-only inference on phones, FPGAs, and TPUs — with the grid staying dense, so it's a genuine speedup.

The two knobs of any quantizer: the range of values you can represent, and the bit spacing (how finely you slice that range). With n bits you get 2ⁿ levels. FP-32 represents ~4.2 billion values over ±3.4×10³⁸; signed INT-8 represents 256 values over [−128, 127]. You are throwing away precision — the entire game is throwing it away where the network won't miss it.

The math: affine (asymmetric) quantization

To map real values r in [rmin, rmax] onto integers q in [0, 2ⁿ−1], you need a scale S (the size of one integer step in real units) and a zero-point Z (which integer represents real 0.0):

S = (rmax − rmin) / (2n − 1)     Z = round(−rmin / S)
quantize:   q = round(r / S) + Z     dequantize:   r̂ = S · (q − Z)

S is a single FP-32 number stored per tensor (or per channel). This is why real quantization is mixed precision: integer weights/activations plus a handful of FP-32 scale factors. The dot product runs in integers; only the rescale touches floating point.

Worked example — quantize a weight to INT-8

Tensor range [rmin, rmax] = [−0.5, 1.5], n = 8 → 256 levels.

StepComputationResult
Scale S(1.5 − (−0.5)) / 2550.00784
Zero-point Zround(0.5 / 0.00784)64
Quantize r = 0.8round(0.8/0.00784) + 64 = 102 + 64q = 166
Dequantize back0.00784·(166 − 64)r̂ = 0.800
Quantize r = 0.0round(0/S) + 64q = 64 = Z ✓

Note real 0.0 maps exactly to the integer Z = 64 — that's the whole point of the zero-point. Zero must be exact because padding and ReLU produce a lot of true zeros, and you don't want quantization noise on them.

SHOWCASE: Quantize a real distribution — turn the knobs

The grey curve is the real weight distribution (bell-shaped, like a real layer). The vertical lines are the 2ⁿ quantization levels; dots show where sample values snap. Drop bits and the levels thin out (error rises). Tighten clip range to spend levels where the mass is. Turn on μ-law warp to space levels logarithmically — denser near zero, where most weights live. This single sim is the entire chapter: fewer bits + smarter placement = same accuracy, smaller model.

The core insight as runnable Python

python
import numpy as np

def quantize(r, n=8):
    rmin, rmax = r.min(), r.max()
    S = (rmax - rmin) / (2**n - 1)        # scale: real units per int step
    Z = np.round(-rmin / S).astype(np.int32) # zero-point: which int == 0.0
    q = np.round(r / S).astype(np.int32) + Z
    q = np.clip(q, 0, 2**n - 1)            # INT-n, dense, no FP
    return q, S, Z

def dequantize(q, S, Z):
    return S * (q.astype(np.float32) - Z)   # back to ~real for the math

w = np.random.randn(1000) * 0.3           # a layer's weights
q, S, Z = quantize(w, n=8)
err = np.abs(w - dequantize(q, S, Z)).mean()
# 4x smaller storage; mean abs error ~ S/4 (half a quantization step)
The PTQ failure mode the survey is honest about: post-training quantization (PTQ — quantize, no retraining) works fine on big overparameterized nets, but collapses on already-tight architectures like MobileNet at 8-bit and below. Why? A lean model has no redundant precision to spare; every bit was doing work. The fix is retraining (next chapter) — and the deeper you go (4-bit, 2-bit, binary), the more retraining you need.
In affine quantization q = round(r/S) + Z, why does the zero-point Z matter?

Chapter 8: Clipping, Retraining & the STE

Two problems stand between you and aggressive quantization. First: outliers blow up the range, starving the bulk of your values of precision. Second: the round() function has zero gradient almost everywhere, so you cannot backprop through it to retrain. The survey's quantization section is largely about these two fixes.

Problem 1 — outliers waste your bits, so CLIP

Most weights cluster near zero; a few outliers sit far out. If S spans the full [min, max], those rare outliers stretch the range and every level lands far apart — the dense middle gets coarse quantization. The fix (Banner et al.): clip at some threshold c < max, accept large error on the few clipped outliers, win small error on the many central values.

The clipping tradeoff, made precise. Total MSE = clipping distortion (from values pushed to ±c) + quantization noise (from the finite step S over the clipped range). Widen c → less clipping, but coarser steps. Narrow c → finer steps, but more clipped outliers. There's an optimal c that minimizes the sum — Banner et al. derive it and report +40% accuracy over naive full-range quantization of VGG16 at 4-bit. NVIDIA's TensorRT picks c by minimizing the KL divergence between the float and quantized activation histograms.

Non-uniform spacing — put levels where the mass is

Uniform quantization spaces levels evenly. But weights are bell-shaped, so most of the action is near zero. Non-uniform / logarithmic quantization (the same μ-law idea from telephony and WaveNet) uses fine spacing near zero and coarse spacing in the tails — fewer levels wasted on rare large values. This is precisely what the μ-law toggle in the Ch 7 showcase demonstrates.

Problem 2 — round() has no gradient, so use the STE

To retrain a quantized network (quantization-aware training), you must backprop through the quantizer. But round() is a staircase: flat everywhere (gradient 0) with jumps (gradient ∞). Useless for SGD. The Straight-Through Estimator (Bengio; used by Zhou et al.) is the elegant cheat: do the real rounding on the forward pass, but pretend it was the identity on the backward pass.

forward:   ro = round((2n−1)·ri) / (2n−1)
backward:   ∂L/∂ri = ∂L/∂ro    (gradient passes straight through, as if round = identity)
Why the lie works. Over many small SGD steps, the rounding error averages out, and the straight-through gradient points in the right direction "on average." It's biased but low-variance, and it's the single trick that made quantization-aware training, binary nets, and ternary nets trainable at all. The other half of QAT is keeping a full-precision shadow copy of the weights for the accumulator: forward/backward use the quantized copy, but gradient updates land on the FP-32 master, which is re-quantized each step. Without the shadow copy, tiny gradients would never move a coarse integer weight.

The extreme end: binary and ternary

Push n all the way down. Binary weights {−1, +1}: a multiply becomes a sign flip — addition/subtraction only, no multipliers. Ternary {−1, 0, +1}: adds a "skip." With binary activations too, the dot product becomes XNOR + popcount — pure bit operations. Rastegari et al.'s XNOR-Net hit 58× speedup and 32× memory savings. The cost is real accuracy loss; this is the bleeding edge of the range/precision tradeoff.

The Straight-Through Estimator — forward staircase, backward ramp

The orange staircase is the real forward quantizer round(); its true gradient is zero on every flat step (useless). The teal line is the STE's pretend gradient — the identity — that backprop uses instead. Toggle it on/off to see the lie that makes quantization-aware training possible.

Why is the Straight-Through Estimator needed for quantization-aware training?

Chapter 9: Picking a Method

A survey is only useful if it ends with judgment. The five families are not interchangeable — they trade different things, stack in different orders, and pay off on different hardware. Here is the practitioner's decision map distilled from the survey's recommendations.

The decision table

Goal / ConstraintReach forWhy
Smaller download, run on CPU/phone/TPUQuantization (INT-8)4× smaller, dense → real speedup, broad hardware support
Maximum size reduction, accuracy-tolerantUnstructured pruning90%+ sparsity possible — but size-only unless you have sparse kernels
Real wall-clock speedup on a GPUStructured pruning or low-rankRemoves whole filters/heads → dense, faster matmul
Need a fundamentally smaller architectureDistillationStudent can be a different, leaner design entirely
Big FC / embedding layersLow-rank / weight tyingEmbeddings & FC are where low rank pays the most
FPGA / ASIC, ultra-low powerBinary / ternaryMultiplies become bit-ops; accept accuracy hit
They compose — and order matters. The biggest wins in the literature stack techniques: prune to remove redundant weights, then quantize the survivors, then optionally distill into the result (Han et al.'s "Deep Compression" pipeline: prune → quantize+share → Huffman code, for 35–49× on AlexNet/VGG). A common combo is distill into a quantized student. The survey's caution: each step can degrade accuracy, so retrain between steps and validate the actual deployment metric (size? FLOPs? latency?) — not the one that's easy to report.

The trap the survey keeps returning to

Three failure modes that catch everyone:

The compression tradeoff space — where each family lives

Each bubble is a family, placed by typical compression and by accuracy retention (y-axis). Toggle the x-axis between "size reduction" and "real hardware speedup" and watch unstructured pruning collapse on the speedup view — the survey's central practical lesson in one picture.

You need a faster model on a standard GPU. Which is the worst single choice?

Chapter 10: Connections & Cheat Sheet

This survey is a 2020 snapshot, but every family it maps is alive and central today — the techniques that put 70B-parameter LLMs onto a single consumer GPU are direct descendants of these five ideas.

1989–1994
Optimal Brain Damage / Surgeon — the original second-order pruning
2015
Deep Compression (Han et al.) — prune + quantize/share + Huffman, 35–49×
2015
Knowledge Distillation (Hinton) & mu-law-style non-uniform quantization
2018
Lottery Ticket Hypothesis — pruning as architecture search
2020 →
This survey — then LLM.int8(), GPTQ, AWQ, QLoRA, SmoothQuant: quantization eats the world

The cheat sheet — every key equation

IdeaEquation / RuleWhat each symbol means
Weight-sharing storagek·b + N·log₂k bitsk centroids, b bits each, N weights → index size dominates
Magnitude pruningprune if |w| < γγ = threshold (global beats per-layer)
Saturating reg.w²/(1+w²)penalizes small w, saturates for large w
OBD saliencys = ½·h·w²h = curvature ∂²L/∂w², w = weight value
Low-rank compressionm·n / (r·(m+n))r = rank, m×n = matrix shape
Distillation loss(1−α)H(y,yˢ) + αρ²·KL(zᵀ/ρ ‖ zˢ/ρ)ρ = temperature, α = mix, z = logits
Affine quantizeq = round(r/S) + ZS = scale, Z = zero-point
Quantize scaleS = (rmax−rmin)/(2ⁿ−1)n = bit width
STE backward∂L/∂rᵢ = ∂L/∂rₒround() faked as identity on backward

The one-paragraph summary you could whiteboard

The mental model: A trained network is a redundant grid of numbers. Make it cheaper by storing fewer distinct values (sharing), zeroing unimportant ones (pruning, ranked by magnitude or by ½·h·w² curvature-saliency), factoring it into thin matrices (low-rank), training a smaller imitator with temperature-softened soft targets (distillation), or storing each number in fewer bits via q = round(r/S)+Z with clipping and the straight-through estimator (quantization). They compose; retrain between steps; and always validate the metric you actually ship — size, FLOPs, or latency — because they don't move together.

Related lessons on Engineermaxxing

What is the survey's single most repeated practical warning?