- 只有消费级单卡(4~8 GB 显存)却要微调 7B/8B:层流式把冻结的基座权重留在显存外逐层搬运,这是它区别于普通 QLoRA 脚本的地方;换 peft 官方示例会直接 OOM
- 想快速验证一份数据集值不值得训:
soup init --template chat出模板、改 YAML、soup train,不用自己拼 Trainer 参数和量化配置,适合一天内跑通闭环 - 训练完要立刻交付成可分发模型:内置 merge、push 和 GGUF 导出,省掉自己写合并脚本再接 llama.cpp 转换那一段
- 需要把训练配置和实时指标给不写代码的同事看:
soup ui起本地面板,比让人读 YAML 和 tensorboard 现实

Soup
MakazhanAlpamysApache-2.0活跃维护 有专家点评用一份 YAML 配置就能微调大模型,靠分层流式加载让 8B 模型在 4GB 显存的笔记本 GPU 上跑起来,省去手写训练脚本和显存焦虑。
Fine-tune LLMs from one YAML. Layer streaming trains an 8B model on a 4 GB laptop GPU.
专家点评
上手难度:进阶更新于 2026-11-11一份 YAML 加层流式,让 4 GB 笔记本显卡也能跑 8B 模型的 LoRA 微调
适用场景
踩坑
- 层流式是 BETA 且要显式
stream_layers: true才开,默认路径不是那个 4 GB 跑 8B 的路径;按 README 的显存数字预期去跑默认配置会失望 - 从旧版本升级会撞上配置校验变严:v0.75 起未知键直接报错,老 YAML 里攒下的冗余字段会让
soup train起不来,升级前先soup doctor --config - issue 里反复出现 recipe 元数据与实际 checkpoint 不符(如声明 text 实为多模态架构)以及
device=cpu在有 CUDA 卡时被忽略,说明自动检测和内置 recipe 表这两块是薄弱面;用非主流模型或想强制指定设备时,别信自动推断,自己核对配置
同类对比
要完全掌控训练循环、自定义 loss 或做非 LoRA 的全参微调,直接用 transformers + peft/TRL 写脚本;显存够(24 GB 以上)且追求成熟生态时,Unsloth 或 axolotl 的配置面更宽、踩坑资料更多。
pip install "soup-cli[train]"
soup init --template chat
soup trainREADME 称可在 4 GB 显存的 RTX 3050 Laptop 上训练 Llama-3.1-8B-Instruct(NF4 + LoRA),实测峰值约 3.32 GB;也提到在 H100 上复现。Apple Silicon 走 MLX 后端。README 未说明纯 CPU 训练。
项目说明
由 README 自动翻译提炼用一份 YAML 微调 LLM 的 CLI,靠层流式把 8B 模型塞进 4 GB 笔记本 GPU
用一份 YAML 配置就能微调大模型,靠分层流式加载让 8B 模型在 4GB 显存的笔记本 GPU 上跑起来,省去手写训练脚本和显存焦虑。
核心能力
- 用一份 YAML 配置驱动微调,自动处理批大小、GPU 检测和量化
- 层流式把冻结的基座模型留在显存外,逐层送入 GPU 计算
- 支持 LoRA/QLoRA 与 4bit 量化,本地单卡即可训练
- 内置 chat、merge、push 与 GGUF 导出,训练完直接部署
- soup ui 提供本地网页面板查看训练配置与实时指标
容易踩的坑
基础包不含 PyTorch,微调必须装 [train] extra,否则 soup train 不可用;extras 要用双引号写成 "soup-cli[train]",单引号在部分 shell 会失败;v0.75 起未知配置键会直接报错而不再静默忽略,升级前先跑 soup doctor --config soup.yaml;层流式仍是 BETA,需显式开启 stream_layers: true。
- v0.77.2v0.77.2 — a cut of main: explicit refusals, MLX preference and GRPO, corrected VRAM estimates and server replies最新正式版2026-11-05 · 2026-11-05 18:52 UTC
v0.77.2, 2026-11-05
153 of the 159 pull requests in this release came from 30 people outside the maintainer. Thank you: the Contributors section below names each of these contributors and what was found or built.
v0.77.2 is a cut of
mainafter v0.77.1, not a backport: it carries every pull request merged since that tag. By the CHANGELOG count that is 6 additions, 5 changes, 2 removals and 117 fixes. Read the upgrade notes under Install / Upgrade before you upgrade asoup servedeployment that sets--tool-auth-token, a shell completion script, a script that checks exit codes, or atask: sftconfig on the vision, audio, MLX, Unsloth, RAFT or pre-tokenized path. Coming from v0.76.x or older, read the v0.77.0 notes first and run:soup doctor --config soup.yamlEvery change, with its pull request, is in CHANGELOG.md, section
[0.77.2]. Compare with the previous release: v0.77.1...v0.77.2.What's New
Added
soup profile --modelanswers "will it fit?" without asoup.yaml(#2285). It prices the run set by--seq,--batch,--method lora|qlora|full,--lora-r,--optimizerand--gradient-checkpointingfrom the model'sconfig.json(a local directory, the Hugging Face cache or the Hub), with the same estimate as asoup.yamldescribing that run; when noconfig.jsoncan be read it exits 1 with the reason. On Apple Silicon,soup profileand the MCPprofiletool now compare the estimate with Metal's recommended working-set size instead of the whole unified memory, andsoup profile --jsonreturns alora_rfield.soup planandsoup applytake--minillm-on-policy(#2315), so an on-policy MiniLLM distill config is validated and hashed like any other, without rewriting YAML.soup applykeeps the flag in its suggested train command and still only records the plan.- MLX preference training (#2333):
backend: mlxwithtask: preferenceandtraining.preference_loss: dpo,orpoorsimporuns the native trainer. The config keepstask: preference;adapter_config.jsonrecords the native task. - MLX GRPO on pre-quantized 4-bit bases (#2386):
task: grpowithbackend: mlxnow trains on a base pre-quantized by mlx-lm at 4 bits inaffinemode (mlx-community/...-4bit,mlx_lm.convert -q). The quantized layers stay frozen and only the LoRA adapter trains; any other quantized base is refused right after the load, naming the first layer outside the rule.adapter_config.jsonrecordsbase_quantization. - Recipe
qwen3.5-0.8b-dpo(#2184), text preference pairs forQwen/Qwen3.5-0.8B, with inherited Qwen3.5 DPO settings rather than hyperparameters tuned for 0.8B. - Recipe
qwen3.5-27b-dpo(#2445), a text-only preference starting configuration forQwen/Qwen3.5-27Bwith inherited Qwen3.5 DPO settings, LoRA and 4-bit quantization. The catalog now has 173 recipes.
Changed
soup servechecks--tool-auth-tokenwith the rulesoup uiandsoup mcp serveuse, and the vLLM, SGLang and MII servers refuse a cross-siteOriginas the transformers server already did (#2329). Details and the one-line token command are in the upgrade notes.- One shared terminal-safe refusal for a
soup dataoutput that aliases an input, links and alternate path spellings included;soup data ingestalso refuses an output that names its source document before reading it (#2349). soup ship --progress jsonlreports live evaluation items after each generation chunk instead of after the whole suite; failed chunks retry only their own items, and scores and the event schema are unchanged.soup eval customand the forgetting probe use the same chunked path with unchanged results
- v0.77.1v0.77.1 — data commands keep their input; plan and apply refuse what train refuses2026-10-28 · 2026-10-28 11:47 UTC
查看更新说明
22 of the 28 pull requests in this release came from 11 people outside the maintainer. Thank you: the Contributors section below names each of them and what they found or built.
v0.77.1 is a cut of
mainafter v0.77.0, not a backport: it carries every pull request merged since that tag. The fixes that matter most are three.soup datacommands no longer write their output over their own input.soup planandsoup applynow refuse the configssoup trainrefuses. And the README's Docker example works again. Nothing in v0.77.0 is taken back; coming from v0.76.x or older, read the v0.77.0 notes first, because a config that loaded on v0.76 can stop loading on v0.77, and run:soup doctor --config soup.yamlFixes that matter
soup datacommands no longer overwrite their input when--outputnames it. Eighteen commands used to write their result over one of their own inputs and exit 0; the original could not be recovered. Each now compares its outputs with its inputs before it reads anything, and refuses the same file under any spelling: the same string,./x,sub/../x, the absolute path, a case variant on a case-insensitive file system, a hard link or a symlink. The message names both paths and says what to change, for example--output './d.jsonl' is the same file as the input 'd.jsonl'; choose a different --output. An output that is a different file works as before, and default output names are unchanged.decontaminate,toxicity,langdetect,piiandeducational(#2245, closes #2225). Their default output names (clean.jsonl,toxicity.jsonl,langdetect.jsonl,pii.jsonl,educational.jsonl) used to replace an input of the same name.decontaminatealso compares--outputwith--benchmark-file. On Windows a trailing space or dot,::$DATAand a junction are refused too. Exit code 1.- Thirteen more (#2261, refs #2226):
lint,doctorandtopics(the JSON report replaced the dataset),from-traces(the preference pairs, often an empty file, replaced the trace file),canary insert,sample,dedup,filter,convert,merge,active-sample,augmentandevolve(the result replaced the input). A default output name that the input already has is refused too:merged.jsonl,prefs.jsonl,active_samples.jsonl,augmented.jsonl.canary insertalso refuses a--manifestthat is the dataset or the--output; before, the dataset write replaced the manifest that had just been written, and the only record of the inserted secrets was lost. Exit code 1, exceptlint(3) andaugmentandevolve(2), which keep the code of their other path errors. soup data cleanalready refused this and is unchanged.
soup planandsoup applyrefuse the configssoup trainrefuses (#2242). They checked only for unknown keys, so a config with a staged field set (training.early_stop_patience: 5), a refused deprecated value (data.remove_unused_columns: true) or any other schema error got a plan (exit 0), andsoup applyandsoup apply --dry-runaccepted it. Both commands now exit 1 with theConfig validation error:linessoup trainprints for the same file, write no plan, and leave an existing state file unchanged (so it is not marked applied). A valid config plans and applies as before. This is the gap the v0.77.0 notes said was open.- **The README's Docker example wo
- v0.77.0v0.77.0 — what v0.76 warned about is now refused2026-10-26 · 2026-10-26 13:18 UTC
查看更新说明
151 of the 226 pull requests in this release came from 44 people outside the maintainer. Thank you: the Contributors section below names each of them and what they found or built.
v0.77.0 is a cut of
main. It ends the one-release notice v0.76 gave: the config fields and values that v0.76 loaded with a "Soup v0.77 will refuse it" warning now stop the load, and so do fourteen settings that v0.76 accepted without a word and never applied. A config that loaded on v0.76 can stop loading on v0.77, so before upgrading a working setup, run:soup doctor --config soup.yamlWhat's New
- DPO, ORPO, SimPO and KTO train natively on Apple Silicon.
task: dpo,orpo,simpoandktowithbackend: mlxrun Soup's own MLX loop on TRL's objectives (DPO's sigmoid loss withdpo_beta; ORPO and SimPO with no reference model; KTO withkto_betaagainst a reference precomputed once from the base). MLX text SFT gains native DoRA (lora.use_dora: true),training.eval_stepscounted in optimizer updates, the loss watchdog, and seeded runs (training.seedandtraining.data_seedare applied, so a run reproduces).lora.use_rslorais now applied on MLX and Unsloth (it was ignored there). - Layer streaming reaches DeepSeek-V3-family MoE models. Layer streaming, Soup's
implementation of layer-to-layer streaming of a frozen base (L2L, Pudipeddi et al. 2020),
now admits
model_type: deepseek_v3, gated on resident-vs-streamed parity forward and backward. DeepSeek-V3's FP8 block-quantised checkpoints are dequantised at shard time, and Kimi-K2-Base / Kimi-K2-Instruct configs are admitted behind a field-by-field check (a tiny K2-shaped checkpoint streams bit for bit on CPU; nothing has been trained on K2 itself).training.stream_expert_layout: per_expertstreams one routed expert at a time: on one RTX 5070 Laptop 8 GB, the same Moonlight-16B LoRA config peaked at 1.0 GB of GPU memory against 3.1 GB fused (NF4 experts against bf16, different cache tiers), at about 2.7x the step time (one run each; the record's controlled pair reads 1.9 GB against 3.9 GB at about 2x, record).training.stream_recompute: off | autokeeps streamed activations instead of recomputing them in the backward, with bit-identical gradients: on the same card's RAM tier a Mistral-7B NF4 step is 22% / 17% / 8.5% faster at 512 / 1024 / 2048 tokens, with no gain on a read-bound disk tier (record); the defaultonkeeps the shipped behaviour. Also new:training.stream_loss_chunk(head and loss in token chunks,sft) andtraining.stream_nf4_scale_cache(bit-identical, off by default). Opt-in (stream_layers: true) and still BETA. - Provenance you can check.
soup registry push --data <file>records the training file's SHA-256 and--idempotency-keymakes a push safe to retry;soup trainrecords the SHA-256 of a local training file at startup; on the transformers backend, resident, layer-streamed and multi-GPU runs record the Hub commit of the base they loaded;soup ship --require-provenancechecks evidence against the adapter, task and source bytes, suite version and verdict policy it was stamped with. - GRPO can score with an LLM judge.
training.reward_fn: judge_ranksends all of a prompt's completions to one judge call and scores each against the others; the run must statetraining.grpo_judge_max_calls, and a run that could exceed it is refused before the model loads. Prompts and completions are sent to the judge host you configure. - Data, speech, recipes and the CLI.
pip install "soup-cli[documents]"provides the PDF and DOCX readers forsoup data ingest;soup merge --low-memorymerges a plain LoRA adapter into a safetensors base one tensor at a time, with valu
- DPO, ORPO, SimPO and KTO train natively on Apple Silicon.
- v0.76.1Soup CLI v0.76.12026-10-14 · 2026-10-14 14:07 UTC
查看更新说明
Soup CLI v0.76.1
A patch release on the v0.76.0 line. It makes the bundled DPO examples train again, puts the same nesting and expansion limits, and a size cap, on every YAML file Soup reads, fixes the declared Typer floor, and loads Hub dataset ids that contain a dot. Everything in v0.76.0 is unchanged.
Highlights
- The bundled DPO examples train again:
task: dpo,ipo,bcoandpreferenceno longer fail inside trl onexamples/data/dpo_sample.jsonl,examples/data/chat_preferences.jsonlor any dataset written the same way (#1849, #1866). soup.yamland every other YAML file Soup reads go through one loader with the same nesting and expansion limits, and a per-reader size cap (#1827).- The Typer floor is now
typer>=0.15.4, and a CI cell starts the CLI on the declared Typer and Click floors (#1802, #1858). - A single-string
data.trainthat names a Hub dataset with a dot in its name, such asteknium/OpenHermes-2.5, loads from the Hub (#1889). - The README and the performance docs state the 8B NF4 figure with its SM clock and give the post-#331 row (#1207, #1699).
Fixed
- DPO-family tasks on the bundled examples (#1849, #1866). A string
promptbesidechosen/rejectedmessage lists that repeat the user turn failed inside trl after the model had loaded, with an error that changed withPYTHONHASHSEED('list' object has no attribute 'endswith', or aTypeError). Thedpoandktoconverters now hand the trainer every preference row in one layout: all strings, or all message lists with the prompt turns inpromptonly.reward_modelno longer trains on a text with the question in it twice, andorpoandsimpoprepare the same tokens as before. A row that cannot be written that way (one answer a string and the other a message list, a malformed message, no prompt to be found) is dropped at load with a reason that names the field, and a dataset that mixes text rows with conversational ones loads as conversations, with a note. - Hub dataset ids with a dot (#1889).
data.train: teknium/OpenHermes-2.5failed withData file not foundbecause the loader read the dot as a file extension. A single string now takes the same route as the same name inside adata.trainlist: a supported file extension (.jsonl,.json,.csv,.parquet,.txt) means a local file, a://string a remote URI, anything else a Hub id.
Changed
- YAML limits on every reader (#1827). Nesting is limited to 100 levels; expansion to one node per source byte plus 10,000 for anchors, aliases and merge keys, with merge keys chained at most 100 levels. Each reader keeps its own size cap: 1 MiB for the training-config readers and for readers that had no cap before, 256 KiB for recipes,
export --quant-configfiles, RA-DIT configs and sweep files, 5 MiB for agent specs, 16 MiB for can manifests, 64 KiB for README front matter, and 4 MiB for the config read bysoup ship. Asoup.yamlthat is over 1 MiB, nests deeper than 100 levels, cannot be read or is not valid YAML now fails to load with one line naming the file and the problem; the other readers report through their own handlers. - Typer floor (#1802, #1858).
typer>=0.9.0was declared while no command could start below Typer 0.12.4, and Typer 0.12.4 to 0.15.3 start the CLI only next to Click 8.1 or older. The floor istyper>=0.15.4;click>=8.0.0was measured again and holds. A fresh install is unchanged. An environment that holds an older Typer now has it upgraded, or the conflict named, at install time, andsoup doctorreports such a Typer as outdated. - README and docs (#1699, #1825, #1826). The 8B NF4 figure is stated with its clock (119.6 tok/s at 952 MHz) beside the post-#331 row (208.6 tok/s at 1935-1957 MHz), with the caveat that the two are not directly comparable. The What's New block of the four README translations describes v0.76.0. Post-release documentation corrections for v0.76.0, no code change.
Security
- Eve
- The bundled DPO examples train again:
- v0.76.0v0.76.0 — settings Soup accepted and never applied now stop the load2026-10-12 · 2026-10-12 15:46 UTC
查看更新说明
490 pull requests have merged since 2026-09-12, the day v0.75.0 shipped, and 418 of them came from 80 people outside the maintainer. What they found is the release: settings that Soup validated, documented and accepted, and then read nowhere on the path they claimed to configure, so a
soup.yamlcould ask for one thing and quietly train another. This release refuses the settings that never took effect, warns about the rest and names the release that will refuse them, evaluates the validation split Soup always held out and never used, and applies thegrpo_betaKL penalty to every GRPO variant. It also addssoup adapters check,soup bench train,soup data clean,soup recipes verifyandsoup rewind, and the layer-streaming disk tier now reads on a background thread, through direct I/O, and can stripe across NVMe drives. A config that loaded on v0.75 can stop loading: runsoup doctor --config soup.yamlbefore you train.What's New
Breaking changes (read these first)
- A setting that never took effect is refused at load. Each message names the field and what to change. Among them:
training.quantization_aware: true(#1222; it never ran quantization-aware training),training.use_longlora(#1240),training.ebft_variant(#1230),training.gdpo_variant(every non-null value),training.rope_scaling_typewhere no setup reads it (#1697),training.freeze_layers/freeze_ratiooutside the textsft/ttspath on the transformers backend (#1497, #1581),training.auto_mixed_precisionoutsidesft/tts(#1618), andbackend: unslothon seven further tasks that have no unsloth setup (#1357). Other refusals follow the same rule, for exampleexpand_layers,freeze_trainable_layers,loraplus_lr_ratioandmoe_loracombinations that no trainer applies; the changelog lists every one. Settings that are unread but still tolerated for one more release (lr_groups,early_stop_patience,forgetting_eval_stepsand 26 other staged fields) warn at load now and are refused from v0.77. - Validation loss is now evaluated, and
grpo,ppoandonline_dpono longer hold rows out.data.val_splitheld rows out of training on every run, and no trainer on the transformers backend evaluated them (#1223, #1313). Every task that gets a split now evaluates it at the end of each epoch, or every N steps with the newtraining.eval_steps, and records the val loss; runs take a few percent longer.grpo,ppoandonline_dpoevaluate by generating, so by default they neither evaluate nor withhold rows:data.val_splitis ignored with a note and every row trains, which changes the rows a default config trains on. - Every non-standard
grpo_variantapplies thegrpo_betaKL penalty (#1232, #1249).gspo,dapo,dr_grpo,bnpo,two_sidedandrftvalidatedgrpo_betaand dropped it, so existing variant configs will not reproduce prior runs.grpo_beta: 0now runs KL-free and skips the reference forward pass (#1247, #1295). - Exit codes of gate and verdict commands follow one taxonomy: 0 pass, 2 gate failure, regression or drift, 3 usage or input error (#813, #921).
soup eval gateandsoup eval againstfail with 2 (was 1),soup lock checkdrift is 2 (was 3),soup expectsuite failure is 2 (was 3),soup data validateinput errors are 3 (was 1), and a mistyped flag on these commands is 3. A CI script that compares the exit code needs a look. - Other behaviour changes.
training.loraplus_lr_rationow applies on every LoRA task instead of onlysft,pretrainandembedding, so those runs produce different weights (#745, #1080).soup export --format torchaoandtraining.nvfp4resolve their torchao classes from the module that defines them and the[qat]extra now needstorchao>=0.18.0(#826, #1066). An FP8 request that cannot run, or whose conversion fails, stops the run instead of training in bf16 (#835, #1154; #1152, #1333).soup data preprocesssto
- A setting that never took effect is refused at load. Each message names the field and what to change. Among them:
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Soup turns the pain of LLM fine-tuning into a simple workflow. One config, one command, done.
pip install "soup-cli[train]" # add [train] to fine-tune; bare `soup-cli` is the light CLI
soup init --template chat
soup train
Fine-tune an 8B model on a 4 GB laptop GPU. Layer streaming keeps the frozen base out of
VRAM and feeds it to the GPU one decoder layer at a time. Measured on an RTX 3050 Laptop 4 GB:
Llama-3.1-8B-Instruct + NF4 at 119.6 tok/s at 952 MHz, 3.32 GB peak (pre-repair) — bit-exact
against a normal resident run, and reproduced independently on an H100 at 113.00 tok/s in the same
3.32 GB. (Both figures were measured on v0.72.2, before the v0.73.0 correctness repair that cost
−4.8% at 32B. The post-repair run, on another RTX 3050 Laptop 4 GB (@umran666, #1103), reads
208.6 tok/s at 1935–1957 MHz in a 2.40 GB peak. That is 59% of its same-session GEMM ceiling
against 68%: about 14% slower per unit clock, so the raw 1.74x gain is the clock, not a speedup.
Large-layer streaming also changed the memory pattern between the two rows, so even the fraction
is not like-for-like — the record.) Opt-in (stream_layers: true)
and still BETA —
how it works ·
all measurements · paper ·
check it yourself on a free Colab T4 (caps the process to
4 GB, then asserts a streamed model is bit-identical to a normal one)
Why Soup?
Training LLMs is still painful. Even experienced teams spend 30-50% of their time fighting infrastructure instead of improving models. Soup fixes that.
- Zero SSH. Never SSH into a broken GPU box again.
- One config. A simple YAML file is all you need.
- Auto everything. Batch size, GPU detection, quantization — handled.
- Works locally. Train on your own GPU with QLoRA. No cloud required.
What's New
v0.77.2 — a cut of main after v0.77.1: 159 pull requests, 153 of them from 30 people outside
the maintainer. Most of it is fixes. It also adds features and two rules that can refuse what
v0.77.1 accepted, so read the
v0.77.2 release notes before
upgrading a server or a task: sft config. Coming from 0.76.x, run soup doctor --config soup.yaml
first and read the v0.77.0 release notes.
soup profile --model <id>answers "will it fit?" without asoup.yaml. It prices the run from the model'sconfig.json, shaped by--seq,--batch,--method,--lora-r,--optimizerand--gradient-checkpointing; on Apple Silicon it compares with Metal's working set.soup planandsoup applytake--minillm-on-policy.- More training on Apple Silicon.
task: preferencewithpreference_loss: dpo,orpoorsimporuns the native MLX trainer, MLX GRPO trains on 4-bitmlx-communitybases, and MLX Adafactor follows the configured learning rate and schedule. - Two new DPO recipes,
qwen3.5-0.8b-dpoandqwen3.5-27b-dpo. - Closer VRAM estimates. The optimizer step is its own phase, a PRM step is charged the logits
of one position, a sharded FSDP or ZeRO run is priced per card, a
stream_loss_chunkstep its last layer's recompute, and Unsloth's LoRA targets as its loader picks them. - Fewer silent overwrites and empty results. More commands refuse an
--outputthat names an input (soup data ingest,persona-mix,generateand others);soup ship --adapterrefuses an adapter that changes nothing;soup distill-prompt,soup data evolve,gen-magpieandfilter --perplexityfail instead of writing an empty or unfiltered file. - Preference and RL fixes.
data.max_lengthholds again for SimPO and KTO, PPO checkpoints its policy when asked (and no longer crashes under 4-bit and 8-bit), andsoup mergeloads the base commit the adapter was trained on. - Text shown as typed. Dozens of commands print paths, names and messages with brackets and
:name:text kept and terminal control characters removed.
Behaviour changes to know. soup serve --tool-auth-token must be 16 to 128 characters of
A-Z, a-z, 0-9, _ and - (exit 2 otherwise). task: sft refuses reasoning_effort,
train_on_eot, use_flash_attn and use_liger on the vision, audio, MLX, Unsloth, RAFT and
pre-tokenised paths that never applied them. Scripts from soup completions need regenerating.
soup data from-traces --signal thumbs_up, MLX Adafactor, soup merge and the VRAM pre-flight can
give a different result than on v0.77.1.
Older highlights live on the GitHub Releases page.
Quick Start
1. Install
Soup is a command-line application, so the cleanest install gives it its own
environment and puts soup on your PATH:
# Light core: CLI + config + data tools, no PyTorch
pipx install soup-cli
uv tool install soup-cli # same idea, if you already use uv
# Add the training stack (torch, transformers, peft, trl, datasets, …)
pipx install "soup-cli[train]"
# Everything (train + serve + ui + data) in one shot
pipx install "soup-cli[all]"
# Or from GitHub (latest dev)
pipx install "git+https://github.com/MakazhanAlpamys/Soup.git"
Already inside a virtualenv, a Colab notebook, or a Docker image? Use pip
directly, with the same names and extras:
pip install soup-cli
pip install "soup-cli[train]"
pip install "soup-cli[all]"
pip install git+https://github.com/MakazhanAlpamys/Soup.git
Use pip rather than pipx if you also want to import soup_cli from your own
code, since pipx deliberately isolates the application from everything else.
The full extras table (fast, mlx, serve, eval, ui, vision, audio, …) lives in
docs/models.md.
error: externally-managed-environment? That is PEP 668, not a Soup problem. Debian 12, Ubuntu 23.04 and later stoppipfrom writing into the system Python, becauseaptmanages those files too.pipxanduv toolsidestep it by giving Soup its own environment, which is why they are listed first above.python3 -m venv .venv && source .venv/bin/activatethen plainpipworks just as well.
Double quotes, not single.
"soup-cli[train]"is the only spelling that works in every shell —cmd.exe, PowerShell, bash and zsh. If you copied'soup-cli[train]'from an older tutorial and pip rejected it, that is the reason: why, and the exact error.
soup init, soup data …, and the other data/inspection commands work on the light install.
Fine-tuning (soup train) needs the [train] extra.
2. Create a config
soup init # interactive wizard
soup init --template chat # or start from a template
Templates: chat, code, tool-calling, medical, reasoning, vision, kto, orpo,
simpo, ipo, bco, rlhf, pretrain, moe, longcontext, embedding, audio.
3. Train, test, ship
soup train --config soup.yaml # LoRA, quantization, batching — all handled
soup chat --model ./output # talk to your model
soup push --model ./output --repo you/my-model
soup merge --adapter ./output # merge LoRA into the base
soup export --model ./output --format gguf --quant q4_k_m # GGUF for Ollama / llama.cpp
More export targets (ONNX, TensorRT, BitNet) and deployment options live in
docs/serving-and-export.md.
Web UI
Prefer a browser? soup ui serves a local dashboard for experiments,
training setup, live metrics, dataset exploration and model chat.
pip install "soup-cli[ui]"
soup ui
# Opens http://127.0.0.1:7860/?token=<token> and prints the same URL

Configuration
A complete soup.yaml:
base: meta-llama/Llama-3.1-8B-Instruct
task: sft
# backend: unsloth # 2-5x faster, pip install "soup-cli[fast]"
data:
train: ./data/train.jsonl
format: alpaca
val_split: 0.1
training:
epochs: 3
lr: 2e-5
batch_size: auto
lora:
r: 64
alpha: 16
quantization: 4bit
output: ./output
config/schema.py is the single source of truth for every field. Advanced data, training,
and PEFT options are documented under Documentation.
Unknown config keys are rejected since v0.75. A key no model declares — a typo like
quantizaton, or a field that only exists on a newer Soup — used to validate clean and be discarded, so the run proceeded with the setting simply not applied. v0.74 reported it at load with the field you probably meant; from v0.75 the same config fails to load, so fix or remove the key rather than relying on it being ignored. See Unknown config keys.
Documentation
The full feature reference lives in docs/. Start here:
| Guide | Covers |
|---|---|
| Training tasks & methods | SFT, DPO/GRPO/PPO/KTO/ORPO/SimPO/IPO/BCO, tool-calling, PRM, pre-training, distillation, classification, vision/audio/TTS, unlearning, RAFT/RA-DIT, loop-hardening detectors |
| PEFT, long context & efficiency | DoRA, LoRA+, rsLoRA, VeRA, OLoRA, NEFTune, PiSSA, ReLoRA, optimizer & PEFT zoo, LLaMA Pro, GaLore, YaRN/LongLoRA, packing, curriculum, auto-tuning |
| Performance & quantization | QAT, FP8, Quant Menu (I + II), KV-cache, NVFP4, save formats, Cut Cross-Entropy, gradient checkpointing, kernels, activation offloading, layer streaming, multi-GPU / DeepSpeed / FSDP |
| Data engineering | Formats, the Axolotl/LF-parity pipeline, data tools, synthetic generation & forge, quality scorecards, trace tooling, remote datasets, mixing, recipe DAGs |
| Evaluation & probes | Eval design/gate, eval-gated training, benchmarks, NLG metrics, calibration, Elo arena, diagnose, post-train X-ray probes, A/B, drift, tunability, soup advise |
| Serving & export | OpenAI-compatible server, batch inference, benchmarking, merge/export, Anthropic Messages endpoint, speculative decoding (train + measure your own draft), deploy autopilot, Web UI, Agent Forge |
| Adapters, registry & governance | Adapter lifecycle/management, model registry, Soup Cans, the data flywheel (soup loop), knowledge editing, steering, supply-chain controls (scan/sign/BOM/attest/audit/airgap) |
| Compliance & governance quickstart | HIPAA/SOC2/EU-AI-Act/SR-11-7 init templates, provenance (BOM/attest/repro-receipt), audit log, air-gap, model-card autogen (soup card), CI gate (soup ci init) |
| Backends, platform & ops | MLX/Unsloth backends, alternative hubs, HF Hub integration, autopilot, experiment tracking, plan/apply, env lockfiles, hardware-fit, completions, plugins, utility commands |
| Command reference | The full soup command list |
| Supported models & extras | Recommended model families, the VRAM size guide, the pip extras matrix |
Data Formats
Alpaca, ShareGPT, ChatML, preference pairs (DPO / ORPO / SimPO / IPO / KTO), vision, audio,
ASR, plaintext, embedding, RAFT and more — all auto-detected from JSONL, JSON, CSV, Parquet or
TXT, so in most cases you point data.train at a file and nothing else changes. Schemas with a
worked example per format, plus the data pipeline (remote URIs, streaming, sharding,
interleaving, vocab expansion, document ingestion), are in
docs/data.md, with native Sesame CSM audio training documented separately.
Common Commands
soup train --config soup.yaml # train (SFT/DPO/GRPO/PPO/KTO/ORPO/SimPO/IPO/...)
soup infer --model ./output --input prompts.jsonl --output results.jsonl # batch inference
soup chat --model ./output # interactive chat
soup serve --model ./output # OpenAI-compatible API server
soup ui # local browser dashboard
soup merge --adapter ./output # merge LoRA into the base model
soup export --model ./output --format gguf # export for deployment
soup eval benchmark --model ./output # evaluate
soup data inspect ./data/train.jsonl # dataset stats
soup recipes list # 100+ ready-made model recipes
soup autopilot --model <id> --data d.jsonl --goal chat # zero-config
soup doctor # check GPU / deps / environment
The complete command list is in docs/commands.md.
Supported Models
Soup works with any text-generation model on the
HuggingFace Hub — if it loads with
AutoModelForCausalLM, it works, zero config changes. Llama 3.x/4, Qwen 2.5/3, Gemma 3, Mistral,
Mixtral, DeepSeek R1/V3, Phi-4, and 100+ others ship as ready-made recipes (soup recipes list).
| VRAM | Max model (QLoRA 4-bit) | Example |
|---|---|---|
| 8 GB | ~7B | Llama-3.1-8B, Mistral-7B |
| 16 GB | ~14B | Phi-4-14B, Qwen2.5-14B |
| 24 GB | ~34B | CodeLlama-34B, Yi-1.5-34B |
| 48 GB | ~70B | Llama-3.3-70B |
| 80 GB+ | 70B+ (full) or MoE | Mixtral-8x22B, DeepSeek-V3 |
Full model + vision tables and the optional-extras matrix are in docs/models.md.
Docker
Run Soup without installing CUDA or PyTorch locally (image published to GHCR on every release):
docker pull ghcr.io/makazhanalpamys/soup:latest
docker run --gpus all -v $(pwd):/workspace ghcr.io/makazhanalpamys/soup train --config soup.yaml --yes
docker compose up # or build locally
Requirements
- Python 3.11, 3.12 or 3.13 (those are the versions CI tests; 3.14+ is not supported yet because the PyTorch stack has not been validated there)
- GPU with CUDA (recommended), Apple Silicon (MPS), or CPU (experimental — very slow)
- 8 GB+ VRAM for 7B models with QLoRA
All training tasks run on CPU for testing (quantization auto-disabled). Optional extras
(train, all, fast, vision, qat, serve, serve-fast, ui, eval, deepspeed,
liger, mlx, onnx, tensorrt, …) are listed in
docs/models.md.
Troubleshooting
soup doctor # GPU, system resources, dependencies, and version in one place
CUDA wheels, version mismatches: docs/backends-and-ops.md.
Development
git clone https://github.com/MakazhanAlpamys/Soup.git
cd Soup
pip install -e ".[dev]"
ruff check src/soup_cli/ tests/ # lint
pytest tests/ -v # unit tests (fast, no GPU)
pytest tests/ -m smoke -v # smoke tests (downloads a tiny model, trains)
pytest tests/ -m gpu --no-cov -v # GPU tests (need a CUDA card; report results, see CONTRIBUTING.md)
pre-commit install # optional: ruff lint+format on commit
See CONTRIBUTING.md for the full workflow and SECURITY.md to
report a vulnerability. Telemetry is strictly opt-in (SOUP_TELEMETRY=1, default off; see Privacy Policy).
Support Soup
Soup is Apache-2.0 and free — and stays that way. It is built and maintained in the open on a single 4 GB laptop, which is why every performance number in these docs is measured rather than claimed.
If Soup saved you a training run, starring the repo helps most, and it costs nothing. If you would like to fund the work directly:
❤️ Donate — one-off, any amount (use Change amount on the checkout page). Payments are processed by Stripe under the maintainer's registered business, MePlay, Inc. — that name, not "Soup", is what appears on the checkout page and on your card statement.
Donations buy GPU time for the hardware-gated work — multi-GPU, 8B+ validation, Apple Silicon — that a single 4 GB laptop cannot reach.
The other way to move exactly those items is hardware itself. They ship behind honest
"requires <hardware>" gates rather than unverified claims, so if you have access to a bigger
box — or GPU credits going unused — running one of the
help wanted
issues and posting the numbers helps as much as funding the GPU time would. Those issues say
exactly what is blocked on hardware today.
Contributors
Built by the community ❤️ — thank you to everyone who has contributed. See CONTRIBUTORS.md.
Contact
Bugs and feature requests belong in the issue tracker, questions in Discussions — both get answered faster and help the next person with the same problem.
For live chat, setup help, and everything that reads better as a conversation, join the Discord or the Telegram community. Anything that should still be findable in six months belongs in Issues or Discussions — a Discord answer helps one person, an issue helps everyone who hits the same thing. The Code of Conduct applies there too.
For anything that does not fit in public — security reports (see SECURITY.md), Code of Conduct matters, or press — email [email protected]. That is the project address and the right one for anything Soup-related. [email protected] is the maintainer's personal address; it reaches the same person and is a fine fallback.
Citing Soup
Layer streaming — training an 8B model on a 4 GB laptop GPU by streaming the frozen base from host RAM one decoder layer at a time — is described in a preprint, together with the correctness protocol that verifies a streamed run against a resident one (forward and backward stated separately, because they are two claims and not one).
Makazhan, A. (2026). Exact Layer Streaming: LoRA Fine-Tuning of an 8B Model on a 4 GB Laptop GPU (v3). Zenodo. https://doi.org/10.5281/zenodo.21918325
Version 3 (13 August 2026) is current. The title and the claim are unchanged — 8B on 4 GB — and no measured number has changed since v1. What v3 does is withdraw an explanation we had published, which is also the shortest way to describe what the paper is for:
- Retracted in v3: "layer streaming is bound by host-to-device transfer, not by the GPU." That was an inference from the H100 replication below, and it had never been measured. We measured it on 11 August and it is false at the published configuration: deleting every host-to-device byte buys 1.4%, the compute stream waits on a copy for 0.20% of the step, and the step runs at 71.3% of that card's same-session GEMM ceiling. The largest streaming-specific cost is the per-layer NF4 dequantisation, at 9.8% (the record). Every measurement stands; the replication survives in a weaker form — the constraint is common to both machines and is not the GPU's compute.
- Replication on hardware nothing like the original (added in v2): 119.6 tok/s on the RTX 3050 (at 952 MHz) against a median 113.00 on an H100, at the same 3.32 GB peak. Both pre-date the #331 repair and are the figures the paper reports. The post-#331 row (@umran666, #1103) is 208.6 tok/s at 1935–1957 MHz in a 2.40 GB peak, 59% against 68% of the same-session GEMM ceiling: the raw 1.74x gain is the clock, not a speedup, and the memory pattern changed between the rows, so even the fraction is not like-for-like (the record).
- A silent wrong-gradient defect, found and repaired. On NF4 above ~165 MiB per layer the forward stayed bit-exact and the loss curve looked healthy while the gradients were wrong. The cause is named in the upstream library and reported there; the repair is gated against controls on real 32B and 72B.
- Bit-exactness at real model sizes instead of three-layer toys: forward from 0.5B to 72B, backward at 8B and 14B.
- Trained-model quality, measured for the first time, and indistinguishable from a resident run.
- A comparison against DeepSpeed — including the result that does not flatter us: eight cards of ZeRO-3 are slower than one card training resident.
- The limitations section rewritten: of v1's ten items, one closed and four more narrowed, and seven new ones added.
Cite the version you used. 10.5281/zenodo.21771064 is the concept DOI and always resolves to
the latest version (v3 today); v1 and v2 remain citable at their own version DOIs and are not
edited — the retraction above is a new version precisely so that the record of what we claimed,
and when, stays intact.
The measurement records behind every number in it are in benchmarks/, published
as written — including the failures, the assumptions that turned out wrong, and the numbers that
were measured and then discarded.
@misc{makazhan2026exact,
title = {Exact Layer Streaming: LoRA Fine-Tuning of an 8B Model on a 4 GB Laptop GPU},
author = {Makazhan, Alpamys},
year = {2026},
publisher = {Zenodo},
version = {v3},
doi = {10.5281/zenodo.21918325},
url = {https://doi.org/10.5281/zenodo.21918325}
}
License
Apache-2.0. Copyright 2026 Makazhan Alpamys and the Soup contributors. If you redistribute Soup or a modified copy of it, sections 4 and 6 of the license apply to you: keep LICENSE and NOTICE with every copy, mark the files you changed, and do not use the Soup name beyond saying where the work comes from.