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

  1. 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.
  2. 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_layers patched, saved, and reloaded so the surviving layers re-index cleanly.
  3. Distill-heal (optional). With --heal, the full-depth original (teacher) is distilled into the pruned student via a LoRA logit-distillation soup train subprocess. The resulting adapter is then fused back into the pruned base, so the shipped artifact stays a single dense model.
  4. 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

bash
# 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

FlagDefaultMeaning
--modelrequiredModel id or local path to shrink
--calibrequiredCalibration prompts (JSONL, chat rows), must stay under cwd
--drop-ratioFraction of layers to drop (e.g. 0.25). Exactly one of ratio / layers
--drop-layersExplicit contiguous-layer count to drop
--healoffDistill the original into the pruned student, then fuse the adapter back
--heal-steps200Approx optimiser steps for the distill heal
--tolerance0.10Perplexity-ratio tolerance for the SHIP verdict (max 5.0)
-o, --output-dir./shrunkOutput dir; model in <dir>/model, verdict in <dir>/shrink_report.json
--deviceautocpu or cuda for the importance + perplexity passes
--attach-to-registryAttach the shrink report to a registry entry id
--plan-onlyoffPrint the importance table + chosen block, then exit 0 (writes nothing)
--trust-remote-codeoffAllow 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 codeMeaning
0SHIP — perplexity ratio within tolerance
2DON'T SHIP — pruning raised perplexity past tolerance
1Error

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; shrink mirrors 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.