Guo, Qiu, Leng et al. (SJTU, Microsoft Research, Alibaba), ICLR 2022

SQuant: On-the-Fly
Data-Free Quantization

Quantize a network to 4-bit in well under a second — no training data, no synthetic data, no back-propagation, not even knowledge of the architecture — by minimizing one surprisingly simple quantity: the signed sum of rounding errors.

Prerequisites: Neural net weights + basic calculus (Taylor / Hessian)
11
Chapters
6
Simulations

Chapter 0: The Problem

You have a trained ResNet that diagnoses tumors from MRI scans. It works. But it is a 32-bit float model: every weight is 4 bytes, the model is hundreds of megabytes, and it is too heavy and too slow to run on the little inference chip inside the scanner. You need to quantize it — squeeze every weight from a 32-bit float down to a 4-bit integer, an 8× shrink — without wrecking its accuracy.

The standard way to do this well is to feed the model a few hundred real MRI scans and watch how the quantization perturbs the activations, then tune the rounding to minimize the damage. This is calibration, and it needs data.

Here is the catch that this paper is built around: you do not have the data. The MRI scans are protected patient records. The model was shipped to you as a black box by a vendor who legally cannot hand over the training set. This is not a corner case — it is the normal situation for medical, financial, and confidential models.

The core question: Can you quantize a trained network to 4 bits — accurately — using only the weights themselves? No training data, no calibration set, no fine-tuning, and ideally so cheaply it runs on the inference device on the fly?

The landscape before SQuant

People had attacked this "data-free quantization" (DFQ) problem two ways, and both had a fatal flaw:

ApproachWhat it doesThe flaw
Plain DFQ (round-to-nearest)Just round each weight to the closest grid point. Fix BN statistics.Fast but terrible at low bits — 0.1% top-1 on 4-bit ResNet18.
Data-generative DFQ (ZeroQ, DSG, GDFQ)Synthesize fake images from the model's BatchNorm statistics, then calibrate / fine-tune on them.Generating fake data is gradient-based and slow — hours per model. Needs back-prop.

So the field faced a dilemma: fast but inaccurate (plain rounding) versus accurate but glacially slow (data generation + fine-tuning). SQuant's claim is that this is a false choice.

A worked sense of scale

Let us anchor the stakes. The numbers below are from the paper's headline results on ImageNet:

Method4-bit ResNet18 top-1Time to quantize
Plain DFQ (rounding)0.10%seconds
GDFQ (generate + fine-tune)60.60%~1.7 hours
SQuant66.14%0.084 seconds

SQuant beats the hours-long data-generative method by more than 5 points while running ~70,000× faster — and it never touches a single image. That last row is the whole paper. The rest of this lesson is the story of how minimizing one humble quantity makes it possible.

What makes "data-free" quantization necessary in the first place?

Chapter 1: The Key Insight

When you round a weight, you make an error. Round-to-nearest minimizes the size of each individual error — but it treats every weight in isolation. SQuant's insight is that what actually hurts the network is not the individual errors; it is what they add up to.

Picture a single neuron computing a dot product: y = ∑i wi xi. If you perturb each weight by Δwi, the output changes by i Δwi xi. Now here is the trick: if the inputs xi are all roughly the same on average (they are — a kernel scans nearby pixels of one feature map), then the output error is roughly proportional to i Δwi — the signed sum of the rounding errors.

The insight: Round-to-nearest minimizes ∑|Δwi| (each error small). But the network output cares about |∑ Δwi| (errors should cancel). If one weight rounds up by 0.4 and its neighbor rounds down by 0.4, the individual errors are large — but the sum is zero, and the neuron's output is untouched. SQuant deliberately accepts bigger individual errors to drive the signed sum toward zero.

The quantity that runs the whole paper

The paper names this signed sum the Absolute Sum of Error (ASE), and minimizing it under a rounding constraint is the Constrained Absolute Sum of Error, or CASE:

ASE = | ∑i Δwi |

That is it. No dataset appears in that formula. No activations. No gradients. It depends only on the weights you are rounding. That is why SQuant is data-free: the entire optimization target was derived from the loss, but every data-dependent term got absorbed into a constant and dropped — leaving a quantity built purely from the weights' own rounding errors.

Round-to-nearest
Minimizes ∑|Δwi| — each error tiny, but they all push the output the same way. Errors accumulate.
SQuant (CASE)
Minimizes |∑Δwi| — flips a few weights so their errors cancel. Output error collapses.

What the whole method is, in one breath

SQuant is: (1) round every weight to nearest — call the leftover error its perturbation; (2) for each kernel, compute the signed sum of perturbations; (3) flip a few weights (round the other way) so that signed sum drops below 0.5; (4) repeat the same flipping trick one level up, across whole kernels in an output channel. Three nested passes — element, kernel, channel — each one canceling the residual error the previous pass left behind. We will derive every piece, and you will watch the flips happen.

Why can two large individual rounding errors be better than two small ones?

Chapter 2: Quantization from Scratch

Before any of SQuant's cleverness, we need to be precise about what "quantize a weight" even means. Quantization maps a continuous float to one of a small, evenly spaced set of integers, then scales back.

The standard uniform quantizer for a weight w is:

ŵ = s · clip( ⌊ w / s ⌉ , min, max )

Symbol by symbol, with intuition:

Why store the integer, not the float? On disk and in compute, you keep the small integer q = clip(⌊w/s⌉) and a single shared s per channel. A 4-bit integer is 8× smaller than a 32-bit float, and integer matrix-multiply hardware is far faster and more energy-efficient. The dequantize is folded into the accumulator. SQuant uses per-output-channel scales — each output channel gets its own sm — which is hardware-friendly and tighter than one scale for the whole layer.

A fully worked int8 example

Let us quantize a real weight to 8-bit. Suppose a channel's weights have a max magnitude of 0.50, and we use symmetric int8 with range [−127, 127]. The scale is the max magnitude divided by the largest integer:

s = 0.50 / 127 = 0.003937

Now quantize w = 0.137:

w/s = 0.137 / 0.003937 = 34.80 → round → q = 35
ŵ = 35 × 0.003937 = 0.13780

The rounding error (perturbation) is Δw = ŵ − w = 0.13780 − 0.137 = +0.00080. In tick units, w/s = 34.80 rounded to 35 — an error of +0.20 ticks. SQuant works entirely in tick units, where the rounding error always lies in [−0.5, +0.5]. Our weight is at +0.20: rounded up, with room to flip down to −0.80 ticks if SQuant decides cancellation is worth it.

Quantization Grid — Drag bit-width, watch a weight snap

Drag bits to see the grid get coarser (fewer levels) as precision drops — at 4 bits the gaps are huge. Drag weight and watch the orange marker snap to the nearest blue grid line; the red bar is the rounding error Δw. Notice how a small drop in bits makes that error explode.

In the quantizer ŵ = s·clip(⌊w/s⌉), what does the scale s represent?

Chapter 3: Why Rounding Is Not Enough

Round-to-nearest feels optimal — it minimizes each weight's error. So why does it collapse to 0.1% accuracy at 4 bits? To see the flaw we need to ask the right question: what does quantization actually do to the loss?

Start with a second-order Taylor expansion of the task loss around the trained weights, as a function of the weight perturbation ΔW:

E[L(W+ΔW) − L(W)] ≈ E[ ΔW·g + ½ ΔWT H ΔW ]

Decode it:

With the gradient gone, the loss damage from quantization is governed entirely by the quadratic term ½ ΔWT H ΔW. This is the heart of every Hessian-aware quantizer (AdaRound, HAWQ, BRECQ).

Here is the flaw in round-to-nearest, stated exactly. Round-to-nearest implicitly assumes H is the identity — that minimizing ∑Δwi2 (each error squared, independently) minimizes the loss. But H is not diagonal. Its off-diagonal entries say that pairs of weight errors interact: Δwi Δwj Hij. Rounding ignores every off-diagonal term — it ignores how errors combine — which is exactly the ∑Δw cancellation effect from Chapter 1.

Reducing the quadratic to something concrete

Under the standard layer-wise assumption, the per-output-channel Hessian factors as Hm ≈ lm · E[x xT], where x are the layer inputs and lm a positive scalar. Substituting, the objective for one output channel becomes:

argminΔW ΔWm,: E[x xT] ΔWm,:T = argmin E[ (ΔWm,: · x)2 ]

Read the right-hand side: it is the mean-squared output error of the channel — the error in y = ΔW·x. This is precisely what AdaRound minimizes, but it needs x (the data!) to estimate E[x xT]. And minimizing it exactly is NP-hard combinatorial optimization. SQuant's two contributions are: approximate E[x xT] so the data vanishes (Ch 4–5), and solve it cheaply without back-prop (Ch 7–8).

Why is round-to-nearest provably suboptimal for the loss?

Chapter 4: The Diagonal Hessian Decomposition (the showcase)

The whole game is the matrix E[x xT]. It is dense — every weight error couples to every other through it — and computing it needs data. SQuant's masterstroke is to approximate this dense matrix as the sum of three simple structured matrices, each capturing the coupling at a different granularity of the weight tensor:

E[x xT] ≈ E + K + C

Recall the weight tensor shape: an output channel m has N input channels (kernels), each with K elements (kernel height × width). So one output channel is an N×K grid of weights. The three matrices cover three nested scopes of that grid:

Why this captures the off-diagonal terms rounding missed. The all-ones blocks J are nothing but "sum these errors together." A block of ones acting on ΔW produces (∑Δw)2 — exactly the signed-sum cancellation from Chapter 1. The decomposition is CASE: E = round each weight, K = make each kernel's errors cancel, C = make each channel's errors cancel. Three levels of "make the sum zero."

The objective, expanded

Substituting E + K + C into the channel objective and writing it out term by term:

n,i en,i ΔWm,n,i2 + ∑n kn ( ∑i ΔWm,n,i )2 + cm ( ∑n,i ΔWm,n,i )2

Three terms, three scopes. The first is per-element squared error (what rounding minimizes). The second squares the sum within each kernel. The third squares the sum over the whole channel. The coefficients en,i, kn, cm are positive constants that weight the three scopes — and they are the only thing in this expression that depends on data. That is the door SQuant walks through in the next chapter.

Hessian Decomposition E + K + C — Click a scope

This is the paper's Figure 1, made interactive. The grid is the NK×NK matrix E[xxT] for one output channel (here N=3 kernels of K=3 elements). Click E to see only the diagonal (rounding); K to add the per-kernel blocks; C to fill in the whole-channel coupling; E+K+C to see how the three structured pieces together cover the full dense matrix that rounding ignored.

What does the all-ones block J in the K term compute when applied to a kernel's errors?

Chapter 5: Going Data-Free

We have the objective from Chapter 4 with three coefficients en,i, kn, cm. Those coefficients are the only place the data x still hides — they come from E[xxT]. SQuant's audacious move: just drop them.

Why is that allowed? The paper proves (Appendix A) that you can always decompose E[xxT] so that all three coefficients are strictly positive: en,i > kn > cm > 0. If every coefficient is positive, then driving each squared-sum term toward zero always reduces the true objective — regardless of the exact coefficient values. You do not need to know the weights of the three terms to know which direction is downhill.

argminΔWn,i ΔWm,n,i2 + ∑n ( ∑i ΔWm,n,i )2 + ( ∑n,i ΔWm,n,i )2
Read what just happened. Every e, k, c is gone. There is no x anywhere in this objective. It is built entirely from the weight perturbations ΔW — quantities you compute from the weights alone. The model is now quantizable with zero data, zero activations, and SQuant doesn't even need to know whether the layer is a 3×3 conv or a 1×1 — only the weight tensor's shape. That is the "not even aware of network architecture" claim from the abstract.

Is dropping the coefficients actually safe?

This is the move that should make you suspicious — so the authors checked it empirically. They computed the true coefficients from real data, ran the flipping optimization, and measured how often a flip that helps the approximate (data-free) objective also helps the true (data-driven) one. They call this the approximation precision (AP):

AP = (correct flips) / (total flips)

Across ResNet18 layers the AP is ~95–100% for SQuant-E&K and essentially 100% for the channel-level flips. In other words: dropping the coefficients almost never sends a flip the wrong way. The progressive, term-by-term reduction (each term positive) is what makes the approximation so faithful in practice.

Approximation Precision — data-free flips vs. true objective
Per-layer agreement between the data-free objective and the true data-driven one (ResNet18).

Each bar is one layer's approximation precision — the fraction of weight flips chosen by the data-free CASE objective that also reduce the true, data-dependent loss. Near-100% means dropping the coefficients was safe.

Why is it valid to drop the coefficients e, k, c from the objective?

Chapter 6: SQuant-E, -K, -C & Relaxation

The three terms of the data-free objective become three optimization sub-problems, solved one after another. Each one minimizes a signed sum under a constraint, and each constraint is 0.5 — because the quantization grid is discrete and a perturbation past 0.5 ticks means the weight should have rounded the other way.

SQuant-E: rounding (the floor)

The first term, ∑ ΔW2, is minimized by making each |ΔW| ≤ 0.5 — which is exactly round-to-nearest. SQuant-E introduces zero error and is O(1) per weight. It is the starting point, not the goal.

SQuant-E: ∀ element, |ΔWm,n,i| ≤ re = 0.5

SQuant-K: cancel within each kernel

The second term wants each kernel's signed sum small: |∑i ΔWm,n,i| ≤ rk = 0.5. This is the kernel's ASE. To shrink it, you flip some elements (round the other way), trading larger per-element error for a smaller sum.

SQuant-C: cancel across the channel

The third term wants the whole channel's signed sum small: |∑n,i ΔWm,n,i| ≤ rc = 0.5. Same idea, one scope up: now you flip whole kernels.

The tension that forces relaxation. SQuant-E demands every element stay within 0.5 of its rounded value. But SQuant-K may need to flip an element — pushing it to between 0.5 and 1.0 — to make the kernel sum cancel. These constraints fight. The resolution: relax. After rounding (re=0.5), let SQuant-K push elements out to re=1.0 so that rk=0.5 holds; then let SQuant-C push whole kernels out to rk=1.0 so that rc=0.5 holds. Each finer level grants the coarser level a little slack to do its job. A flipped element ends up with 0.5 ≤ |ΔW| < 1.0 — still a valid grid neighbor, just the second-nearest.

Composing in order: E → K → C

The composition is strict and one-directional. First round everything (E). Then, holding the rounding as a baseline, flip elements to fix kernel sums (K). Then, holding the kernel solution, flip kernels to fix the channel sum (C). The progressive order matters: it lets each pass calibrate the error the previous pass left behind, and it is what makes the algorithm linear-time instead of NP-hard.

Why must the constraints be "relaxed" (e.g. r_e from 0.5 to 1.0) as we go from SQuant-E to SQuant-K?

Chapter 7: The Flip Algorithm

Here is the engine. Given a set of rounded weights and their perturbations, how do you cancel their signed sum with the fewest, smallest flips? The answer is a beautiful four-line routine that we will run by hand.

Say a kernel of weights, after rounding, has perturbations (in tick units):

p = [ +0.30, +0.40, −0.10, +0.35, −0.20 ]

Step 1: accumulate the error

The signed sum is e = ∑i pi = 0.30 + 0.40 − 0.10 + 0.35 − 0.20 = +0.75. This exceeds rk=0.5, so the kernel needs fixing. The sum is positive — the kernel rounded up too much on net.

Step 2: disable the wrong-sign elements

To reduce a positive e, we want to flip elements that currently round up (positive p) down by one tick — each such flip subtracts about 1 from the sum. Flipping a negative-perturbation element would make things worse, so we disable them (set their p to 0 as candidates). Surviving candidates: [+0.30, +0.40, 0, +0.35, 0].

Step 3: how many to flip

We need to remove |e| = 0.75 of error, and each flip removes ~1 tick. So k = ⌊|e|⌉ = ⌊0.75⌉ = 1 flip suffices to push |e| below 0.5.

Step 4: flip the largest perturbations

Which one? The element with the largest |p| — here +0.40. Why the largest? Because it is closest to the flip boundary (0.5), so flipping it incurs the smallest new per-element error. Flipping +0.40+0.40 − 1 = −0.60. New sum: e = 0.75 − 1.0 = −0.25, and |{-0.25}| < 0.5. Done. The kernel's output error is now near zero, at the cost of one element rounding to its second-nearest grid point.

Why "top-k largest perturbation" is provably optimal. Among all ways to flip k elements that satisfy the kernel sum, flipping the k with largest |p| leaves the smallest residual element-wise error (SQuant-E term). The constraint from SQuant-E is what singles out a unique best choice from the huge space of valid flip sets — this is the "Constrained" in CASE, and the paper proves it is the only minimizer.
SQuant Flip — run the algorithm on a kernel

Each bar is one weight's perturbation pi (in ticks). The teal line is the running signed sum e; the dashed band is the ±0.5 target. Hit Step to walk the four phases: accumulate → disable wrong-sign (greyed) → pick top-k (highlighted) → flip. Watch the sum snap inside the band. New random kernel reshuffles.

When the kernel's signed sum is positive, which elements does SQuant flip, and which ones?

Chapter 8: On-the-Fly — The Full Algorithm

The full SQuant is the flip routine wrapped in the progressive E→K→C composition, run independently per output channel. Here it is as the paper's Algorithm 1, with the data flow annotated:

python
# Progressive SQuant: quantize weight tensor W of one layer
# W: [M, N, K]  (out-channels M, in-channels N, kernel size K)
def squant_flip(w, p):                 # the Ch.7 engine
    e = p.sum()                          # 1. accumulate signed error
    p = p.clone(); p[e * p < 0] = 0      # 2. disable wrong-sign
    k = round(abs(e))                    # 3. how many to flip
    f = p.abs().topk(k).indices         # 4. top-k largest |p|
    w[f] = flip(w[f])                    #    flip them (±1 tick)
    return w

def squant(W, s):                       # s: per-channel scale [M]
    for m in range(M):                    # SQuant-C: each output channel
        E = round(W[m] / s[m])           # SQuant-E: round to nearest
        dE = E - W[m] / s[m]              # element perturbation [N,K]
        for n in range(N):                # SQuant-K: each kernel
            E[n] = squant_flip(E[n], dE[n])
        dK = kernel_perturbation(dE)    # one residual per kernel
        E = squant_flip(E, dK)            # SQuant-C: flip whole kernels
        C[m] = E * s[m]                  # dequantize
    return C

Complexity — why it is sub-second

Each kernel's flip is O(K) (a sum and a top-k over K elements). There are MN kernels and M channels, so the layer is linear in the number of weights — no iteration, no gradient, no back-prop. Contrast with AdaRound/GDFQ, which run hundreds of gradient steps over generated data.

Embarrassingly parallel. SQuant-K has MN independent sub-problems and SQuant-C has M — and they share no state. On a GPU they all run at once. That is why a whole ResNet18 quantizes in 84 ms and a single layer in ~3 ms. It is cheap enough to run on the inference device itself (a phone, an IoT chip) — "on the fly" — because it needs no training infrastructure at all.

What happens to the data flow

Trace the tensors: in goes W ∈ ℝM×N×K (fp32) and a scale vector s ∈ ℝM. SQuant-E produces integer codes ∈ [−2b−1, 2b−1−1] and float perturbations ΔE ∈ [−0.5,0.5]. SQuant-K rewrites a few codes (their |Δ| grows to [0.5,1.0)). SQuant-C rewrites a few whole kernels. Out comes the integer tensor C ∈ ℤM×N×K at b bits, plus the same s. For a 1×1 conv or FC layer (K=1), there is nothing to sum inside a kernel, so SQuant-K is skipped entirely — the design degrades gracefully to just E&C.

Why can SQuant run "on the fly" on an inference-only device like a phone?

Chapter 9: Experiments

Two numbers matter for any DFQ method: accuracy after quantization, and time to quantize. SQuant wins both, and the margin widens as the bits drop.

Accuracy vs. bit-width

At 8 bits everything is easy — SQuant loses only ~0.1% on average. The story is at 4 bits, where rounding collapses and even data-generative methods struggle:

4-bit ImageNet top-1 — SQuant vs. SOTA DFQ

ResNet18 ImageNet top-1. Grey = float baseline (71.47%). DFQ is the only other true data-free method (no back-prop, no synthetic data); ZeroQ/DSG generate data; GDFQ even fine-tunes. SQuant (orange) tops them all at every bit-width — and at 4-bit it beats the next-best by >5 points. Toggle bits to watch the gap open as precision drops.

Speed

The accuracy is impressive; the speed is almost absurd. Quantizing 4-bit ResNet18 end to end:

MethodTimeNeeds
GDFQ~1.7 hoursdata generation + 400 fine-tune epochs + back-prop
ZeroQ~38 secondsBN-stat data generation
SQuant84 msnothing but the weights

That is roughly a 70,000× speedup over GDFQ — while being more accurate. A single layer averages 3 ms.

Ablation: do you need all three of E, K, C?

The ablation is the cleanest proof that the cancellation idea, not the rounding, is what carries the accuracy. On 4-bit ResNet18 (weights only):

Varianttop-1What it adds
SQuant-E (rounding)48.15%per-element error only — the baseline
SQuant-E&C67.14%+ channel-level cancellation
SQuant-E&K68.07%+ kernel-level cancellation
SQuant-E&K&C69.75%+ both — the most Hessian information

Each cancellation scope you add buys accuracy, and they stack. Note E&K beats E&C — the kernel-level approximation is tighter than the channel-level one (the channel scope is a stronger, looser assumption), exactly as the Hessian decomposition predicted in Chapter 4.

The honest caveat. SQuant quantizes weights. Activations still use a simple BN-based rounding (also data-free, from the earlier DFQ work). The paper's ablation isolating weight quantization shows that weights are where the accuracy is won or lost at low bits — which is exactly why getting the weight rounding right (CASE) matters so much.
What does the E / E&K / E&K&C ablation demonstrate?

Chapter 10: Connections & Cheat Sheet

SQuant sits in a lineage of Hessian-aware quantizers and pushes one idea to its limit: if you approximate the Hessian cleverly enough, the data falls out of the objective entirely.

The cheat sheet

SymbolMeaningPlain-English
Δwŵ − w (in ticks, ∈[−0.5,0.5] after rounding)how far rounding moved a weight
ASE|∑i Δwi|signed sum of errors — what the network output actually feels
CASEminimize ASE subject to round-to-nearestcancel the sum with the fewest, smallest flips
Hloss Hessian; ≈ E[xxT] per channelhow weight errors couple to hurt the loss
E,K,Cdiagonal / block-diag / all-ones approximations of Helement / kernel / channel cancellation scopes
re,rk,rc0.5 constraints (relaxed to 1.0 for finer scope)how far a sum may drift before a flip is needed
flipround a weight the other way (±1 tick)the only operation; top-k largest |p| with sign matching e
smper-output-channel scalereal size of one integer tick

The one-sentence summary

SQuant: the loss damage from quantization is governed by the Hessian; approximate the Hessian as element + kernel + channel "sum-of-error" terms; because those terms have positive coefficients you can drop the data-dependent coefficients and minimize the resulting signed sum of rounding errors (CASE) with a linear-time, back-prop-free, top-k flipping algorithm — quantizing a 4-bit network in sub-second time, on the device, with no data at all.

Related Engineermaxxing lessons

If you came here for compression, also see the quantization and model-compression material in the Gleams and the broader Veanors catalog — SQuant pairs naturally with lessons on the Hessian, post-training quantization, and the AdaRound "up or down" objective it descends from.

In one line: what is SQuant's core contribution over AdaRound?