Optimal Brain Compression
Setup
Post-training compression is done layer-wise: for each layer $\ell$ with weights $W_\ell$ and calibration inputs $X_\ell$, find compressed weights $\widehat{W}_\ell$ (sparse, quantized, or both) minimizing the layer-wise reconstruction error
\begin{equation} \min_{\widehat{W}_\ell} \; \big\lVert W_\ell X_\ell - \widehat{W}_\ell X_\ell \big\rVert_2^2 . \end{equation}Write the layer size as $d = d_{\text{out}} \times d_{\text{in}}$. The punchline of the paper is an exact greedy OBS solver whose cost drops from the naive $O(d^4)$ to $O(d \cdot d_{\text{in}}^2)$.
Background: Optimal Brain Surgeon
Choosing which weights to prune is NP-hard, so OBS is a greedy algorithm that prunes weights one-by-one: at each step, remove the weight with the smallest loss increase and update all remaining weights to compensate,
\begin{equation} w_p = \operatorname*{argmin}_{w_p} \; \frac{w_p^2}{[H^{-1}]_{pp}}, \qquad \delta_p = - \frac{w_p}{[H^{-1}]_{pp}} \, H^{-1}_{:,p}, \end{equation}where $H^{-1}_{:,p}$ denotes column $p$ of the inverse Hessian. Applied directly to a full layer this is hopeless: the Hessian is in $\mathbb{R}^{d \times d}$, which we can't even materialize, and inverting it once would be $O(d^3)$ — so algorithms that re-invert after pruning each weight would be $O(d^4)$.
Efficient OBS solver (ExactOBS)
Idea 1: work with per-row Hessians
For the squared reconstruction error, the problem decomposes over the $d_{\text{out}}$ rows of $W \in \mathbb{R}^{d_{\text{out}} \times d_{\text{in}}}$: each row has its own Hessian $H = 2 X X^{T} \in \mathbb{R}^{d_{\text{in}} \times d_{\text{in}}}$ — and all rows share the same Hessian initially.
The layer-wise problem decomposes over rows: run OBS on each row of $W$ with its own $d_{\text{in}} \times d_{\text{in}}$ Hessian, which is small enough to actually store and invert.
Note that at each global step we don't want to commit to some pre-determined row and its current inverse Hessian — we want to look at all rows and prune the best weight overall. Hence all $d_{\text{out}}$ per-row inverse Hessians are kept in memory.
Idea 2: rank-1 updates of the inverse Hessian
Look at two consecutive steps of the iterative process. At first we have some $H$ and its inverse $H^{-1}$; pruning weight $p$ removes row and column $p$ from $H$, giving $H_{-p}$.
$(H_{-p})^{-1}$ can be computed from $H^{-1}$ directly — no need to re-invert from scratch — with a simple rank-1 correction, one step of Gaussian elimination of row and column $p$:
In practice we don't even need to resize the matrix: the Gaussian elimination formula leaves row and column $p$ full of zeros, so they don't influence any future calculations.
Idea 3: jointly consider all rows
Observation: within one row, the order in which its weights get pruned does not depend on the other rows — the issue is only that row operations can get interleaved in the global greedy order. Even more, the loss increase $\frac{w_p^2}{[H^{-1}]_{pp}}$ of each pruning step can also be computed one row at a time.
Completely prune each row on its own (keeping its full per-row inverse Hessian only while processing that row) and record the loss increase of every step — $O(d)$ numbers total. Then just sort these globally to create the pruning mask $M$.
Note that we still need to look at the whole layer because the target sparsity is per layer, not per row — the global sort of loss increases is what decides how many weights each row loses.
Once the per-row masks $M_i$ are known, the remaining weights of each row can be updated in a single step with the group OBS formula:
\begin{equation} \delta_{M_i} = - H^{-1}_{:,M_i} \big( (H^{-1})_{M_i} \big)^{-1} w_{M_i} . \end{equation}Structured sparsity extensions
- N:M sparsity — $N$ non-zero values in every group of $M$ weights. Each row has the same sparsity, so rows can be treated independently; within a row, at each step prune the weight with the lowest loss increase that belongs to a group with fewer than $M - N$ pruned entries.
- Block sparsity — there are closed-form OBS formulas for pruning whole blocks; the algorithm is otherwise the same.
Extension to quantization
- Greedily quantize the currently “easiest” weight by the same $\frac{w_p^2}{[H^{-1}]_{pp}}$-style metric (with $w_p - \text{quant}(w_p)$ in place of $w_p$), and adjust all remaining unquantized weights to compensate for the loss of precision.
- Handling outliers: quantize them as soon as they appear, so that there are still many unquantized weights left that can be modified to compensate.