soup shrink: make a model smaller (v0.71.29)
v0.71.29 ships soup shrink: one command that makes a model *permanently* smaller and faster by removing its least-useful decoder layers, optionally healing the damage by distillation, and printing a single SHIP / DON'T-SHIP perplexity verdict. The output is one dense smaller model, not a base plus an adapter.
It implements "The Unreasonable Ineffectiveness of the Deeper Layers" (Gromov et al.), with a Minitron-style distillation heal in place of the paper's plain fine-tune.
No fine-tuning CLI (Unsloth, Axolotl, LLaMA-Factory) ships one-command importance-ranked depth pruning with a distill-heal and a binary perplexity verdict.
How it works
- Importance scan. For each contiguous block of decoder layers, Soup runs one forward pass per calibration prompt and measures the angular distance the residual stream travels from the block's input to its output, averaged over every non-pad token. A block that barely changes the residual stream is doing little work and is the safest to drop.
- Select + prune. The least-important block is removed. The first and last decoder layers are always protected (they carry the most transformation). The model is sliced, its
num_hidden_layerspatched, saved, and reloaded so the surviving layers re-index cleanly. - Distill-heal (optional). With
--heal, the full-depth original (teacher) is distilled into the pruned student via a LoRA logit-distillationsoup trainsubprocess. The resulting adapter is then fused back into the pruned base, so the shipped artifact stays a single dense model. - Verdict. Perplexity is measured before and after. Soup SHIPs only if the perplexity ratio stays within your tolerance (default 10%), else DON'T SHIP.
Quick start
# Plan only: print the layer-importance table + chosen block, write nothing.
soup shrink --model HuggingFaceTB/SmolLM2-135M-Instruct --drop-ratio 0.25 \
--calib calib.jsonl --device cpu --plan-only
# Prune 25%, distill-heal, get a verdict, attach the report to a registry entry.
soup shrink --model HuggingFaceTB/SmolLM2-135M-Instruct --drop-ratio 0.25 \
--calib calib.jsonl --heal heal.jsonl --heal-steps 200 \
--tolerance 0.10 -o shrunk --device cpu --attach-to-registry <id>The shrunk model lands in <out>/model, and the verdict is written to <out>/shrink_report.json.
Flags
| Flag | Default | Meaning |
|---|---|---|
--model | required | Model id or local path to shrink |
--calib | required | Calibration prompts (JSONL, chat rows), must stay under cwd |
--drop-ratio | — | Fraction of layers to drop (e.g. 0.25). Exactly one of ratio / layers |
--drop-layers | — | Explicit contiguous-layer count to drop |
--heal | off | Distill the original into the pruned student, then fuse the adapter back |
--heal-steps | 200 | Approx optimiser steps for the distill heal |
--tolerance | 0.10 | Perplexity-ratio tolerance for the SHIP verdict (max 5.0) |
-o, --output-dir | ./shrunk | Output dir; model in <dir>/model, verdict in <dir>/shrink_report.json |
--device | auto | cpu or cuda for the importance + perplexity passes |
--attach-to-registry | — | Attach the shrink report to a registry entry id |
--plan-only | off | Print the importance table + chosen block, then exit 0 (writes nothing) |
--trust-remote-code | off | Allow custom modeling code; defaults off with a probe + warn |
The first and last layers are always protected, so the resolved drop count is clamped to [1, num_layers - 2].
The verdict + exit codes
soup shrink drops into CI exactly like `soup ship` and soup diagnose:
| Exit code | Meaning |
|---|---|
0 | SHIP — perplexity ratio within tolerance |
2 | DON'T SHIP — pruning raised perplexity past tolerance |
1 | Error |
Pruning always raises perplexity, so shrink uses this dedicated ratio rule rather than soup ship's absolute-improvement gate. The perplexity numbers are the same procedure applied before and after, so the ratio is valid, but they are not directly comparable to soup eval / lm-eval absolute scores.
Supported architectures (v1)
Llama, Qwen, SmolLM. Anything else is a friendly reject with a pointer to open an issue. The family is checked twice: fail-fast from the config before weights load, and again on the loaded model.
Validated results
Live-validated on SmolLM2-135M on a single RTX 3050 (4 GB):
- Drop 25%, unhealed: 30 → 22 layers (~21% of params gone), perplexity x2.98.
- Drop 4 + CPU heal: perplexity recovered to x1.35.
The importance pass loads the model, so it is live-validated on models up to ~3B on that hardware; larger models work but are unvalidated on the reference card.
Security
Every path (--calib, --heal, --output-dir, and every derived write path like <out>/model and the fuse staging dir) is held under cwd with realpath + commonpath + O_NOFOLLOW + symlink rejection, re-validated right before each write (TOCTOU defence, including after the potentially hours-long heal). The heal runs as an argv-list subprocess (no shell) with a timeout and a schema-validated config; its output is control-char-stripped before it reaches your terminal. Input files are capped at 64 MiB and 10,000 calib rows. --model defaults trust_remote_code=False.
See also
- soup ship — the SHIP / DON'T-SHIP verdict for a fine-tune;
shrinkmirrors its exit codes. - Spectrum targeted training — the complementary move: instead of removing layers, freeze all but the high-signal ones.
- Fine-tune Doctor — pre-flight your data before you prune-and-heal.