Add Reasoning-Aware Compression (RAC) pruning example for reasoning LLMs - #1006
Conversation
Implements "Reasoning Models Can be Accurately Pruned Via Chain-of-Thought
Reconstruction" (Lucas et al., ICLR 2026, arXiv:2509.12464) under
compression/reasoning_aware_compression.
Layer-wise pruners calibrate on prompt activations, which is the wrong
distribution for reasoning models: decoding dominates their token budget, so
pruned models both lose accuracy and get slower by rambling. RAC calibrates on
the dense model's own on-policy chain-of-thought instead, as a drop-in change
to SparseGPT/Wanda.
Two phases:
collect_traces.py on-policy CoT rollouts -> jsonl, with vLLM, plain HF, or
DeepSpeed ZeRO-Inference (ZeRO-3 with CPU/NVMe parameter
offload) for 32B/70B dense models
prune.py one-shot SparseGPT / Wanda / magnitude pruning against a
c4, prompt-only, or RAC calibration set
Pruning streams the model through the accelerator one transformer block at a
time, so peak device memory is one block plus its Hessians. Supports
unstructured and n:m sparsity, MLP-only scope, and per-third block selection.
Includes bash scripts for the end-to-end run, the c4/prompt/RAC ablation and
lighteval MATH-500 evaluation, plus CPU-only unit tests (pytest tests, 23
tests, no network or GPU).
Signed-off-by: Zhipeng Wang <zhipengbayern@gmail.com>
|
Some optional suggestions (non-blocking): use_cache = getattr(model.config, "use_cache", False)
model.config.use_cache = False
# ... pruning ...
model.config.use_cache = use_cache # not reached if pruning throwsIf pruning fails partway, 2. n_completion = int((sequence != tokenizer.pad_token_id).sum())Since 3. README "exactly" wording (L57–59)
Traces are stored as decoded text and re-tokenized in 4. ZeRO-Inference loop termination ( if total >= args.trace_tokens:
break
5. Empty For unsupported architectures (e.g., GPT-2/BLOOM using |
tohtana
left a comment
There was a problem hiding this comment.
Look good to me. I left a comment about the license file of Wanda.
tohtana: include the upstream license files for the adapted pruners. Adds third_party_licenses/ with Wanda's MIT license (c) 2023 CMU Locus Lab and SparseGPT's Apache-2.0 text, and points the module docstrings and the README Attribution section at them. delock: - sequential.py: restore model.config.use_cache in a finally block so a failed pruning run does not leave the config mutated. - collect_traces.py: comment why n_completion counts non-pad tokens rather than the generated length. - collect_traces.py: all-reduce the running token total before the loop's exit check, so every ZeRO-Inference rank leaves on the same iteration instead of relying on the per-rank counts matching; add a cross-rank checksum guard that fails loudly if the ranks decoded different tokens. - modelutils.py: warn when find_linear_layers finds nothing (Conv1D-based architectures such as GPT-2/BLOOM), and fail in prune.py before calibration rather than saving a still-dense checkpoint. - README: "closely approximates" instead of "exactly", with a note that traces are re-tokenized when calibration windows are packed. Adds six regression tests (29 total, still CPU-only).
What
Adds
compression/reasoning_aware_compression/, an example implementing Reasoning-Aware Compression (RAC) from Reasoning Models Can be Accurately Pruned Via Chain-of-Thought Reconstruction (ICLR 2026). Authors' reference code: RyanLucas3/Reasoning-Aware-Compression.Why
Layer-wise pruners (SparseGPT, Wanda, ALPS) minimise
||W X - W' X||_F^2over calibration activations collected from prompts. Reasoning models invert the usual token economics: the prompt is a few hundred tokens and the chain-of-thought answering it is thousands, so pruning optimises the wrong distribution. The failure mode is worse than a plain accuracy drop — the pruned model rambles, so it gets slower as it gets sparser. In the paper, DeepSeek-R1-Distill-Qwen-7B pruned to 50% with C4 calibration takes 135 min on MATH-500 vs 23 min dense, and accuracy falls 0.936 → 0.744.RAC changes only the calibration set: it collects the dense model's on-policy CoT traces and concatenates their activations with the prompt activations, so each layer is reconstructed against the activations it will actually see while decoding. Same 7B model at 50%: 0.900 accuracy in 35 min. No retraining, no distillation, drop-in for any layer-wise pruner.
What's in the PR
collect_traces.py: rollouts from the dense model with vLLM, plain HFgenerate, or DeepSpeed ZeRO-Inference (--backend deepspeed: ZeRO-3 parameter sharding with CPU/NVMe offload, followinginference/huggingface/zero_inference) so 32B/70B dense models can produce traces on a couple of GPUs.prune.py: SparseGPT / Wanda / magnitude against--calibration {c4,prompt,rac}. The model is streamed through the accelerator one transformer block at a time — peak device memory is one block plus its Hessians, which is what lets 70B prune on a single 80GB GPU as in the paper. Supports unstructured and n:m (2:4) sparsity,--scope mlp, and--layer-thirds 1,3for the paper's partial-depth throughput setting.bash_script/run_calibration_ablation.shreproduces the paper's core comparison: same model, same pruner, same 1M-token budget, three calibration sets.Testing
pytest tests— 23 CPU-only tests, no network or GPU, a few seconds. Cover calibration-window packing, RAC-vs-prompt-only token content, target sparsity for all three pruners, 2:4 patterns, block/scope selection, batched-equals-unbatched calibration, dead input channels, and that the calibration set actually changes the mask.collect_traces.py --backend hf→prune.py --calibration rac→ reload the saved checkpoint and verify the sparsity pattern.Notes for reviewers
rac/sparsegpt.pyadapts IST-DASLab/sparsegpt (Apache-2.0),rac/wanda.pyadapts locuslab/wanda (MIT); both are attributed in-file and in the README.