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.

v0.77.2发布于 2026-11-05 18:52 UTC10,449star +163 / 7 天1,723fork同步于 2026-11-10 23:30 UTC

专家点评

上手难度:进阶更新于 2026-11-11

一份 YAML 加层流式,让 4 GB 笔记本显卡也能跑 8B 模型的 LoRA 微调

适用场景

  • 只有消费级单卡(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 现实

踩坑

  • 层流式是 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 的配置面更宽、踩坑资料更多。

怎么上手

由 README 提炼,执行前请确认
pip install "soup-cli[train]"
soup init --template chat
soup train
硬件要求按 README 说明

README 称可在 4 GB 显存的 RTX 3050 Laptop 上训练 Llama-3.1-8B-Instruct(NF4 + LoRA),实测峰值约 3.32 GB;也提到在 H100 上复现。Apple Silicon 走 MLX 后端。README 未说明纯 CPU 训练。

纯 CPU 可运行支持 Apple Silicon需要 CUDA多卡 / 张量并行有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 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。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.77.2
  1. 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 main after 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 a soup serve deployment that sets --tool-auth-token, a shell completion script, a script that checks exit codes, or a task: sft config 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.yaml
    

    Every 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 --model answers "will it fit?" without a soup.yaml (#2285). It prices the run set by --seq, --batch, --method lora|qlora|full, --lora-r, --optimizer and --gradient-checkpointing from the model's config.json (a local directory, the Hugging Face cache or the Hub), with the same estimate as a soup.yaml describing that run; when no config.json can be read it exits 1 with the reason. On Apple Silicon, soup profile and the MCP profile tool now compare the estimate with Metal's recommended working-set size instead of the whole unified memory, and soup profile --json returns a lora_r field.
    • soup plan and soup apply take --minillm-on-policy (#2315), so an on-policy MiniLLM distill config is validated and hashed like any other, without rewriting YAML. soup apply keeps the flag in its suggested train command and still only records the plan.
    • MLX preference training (#2333): backend: mlx with task: preference and training.preference_loss: dpo, orpo or simpo runs the native trainer. The config keeps task: preference; adapter_config.json records the native task.
    • MLX GRPO on pre-quantized 4-bit bases (#2386): task: grpo with backend: mlx now trains on a base pre-quantized by mlx-lm at 4 bits in affine mode (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.json records base_quantization.
    • Recipe qwen3.5-0.8b-dpo (#2184), text preference pairs for Qwen/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 for Qwen/Qwen3.5-27B with inherited Qwen3.5 DPO settings, LoRA and 4-bit quantization. The catalog now has 173 recipes.

    Changed

    • soup serve checks --tool-auth-token with the rule soup ui and soup mcp serve use, and the vLLM, SGLang and MII servers refuse a cross-site Origin as 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 data output that aliases an input, links and alternate path spellings included; soup data ingest also refuses an output that names its source document before reading it (#2349).
    • soup ship --progress jsonl reports 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 custom and the forgetting probe use the same chunked path with unchanged results
  2. 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 main after v0.77.0, not a backport: it carries every pull request merged since that tag. The fixes that matter most are three. soup data commands no longer write their output over their own input. soup plan and soup apply now refuse the configs soup train refuses. 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.yaml
    

    Fixes that matter

    • soup data commands no longer overwrite their input when --output names 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, pii and educational (#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. decontaminate also compares --output with --benchmark-file. On Windows a trailing space or dot, ::$DATA and a junction are refused too. Exit code 1.
      • Thirteen more (#2261, refs #2226): lint, doctor and topics (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, augment and evolve (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 insert also refuses a --manifest that 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, except lint (3) and augment and evolve (2), which keep the code of their other path errors.
      • soup data clean already refused this and is unchanged.
    • soup plan and soup apply refuse the configs soup train refuses (#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), and soup apply and soup apply --dry-run accepted it. Both commands now exit 1 with the Config validation error: lines soup train prints 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
  3. 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.yaml
    

    What's New

    • DPO, ORPO, SimPO and KTO train natively on Apple Silicon. task: dpo, orpo, simpo and kto with backend: mlx run Soup's own MLX loop on TRL's objectives (DPO's sigmoid loss with dpo_beta; ORPO and SimPO with no reference model; KTO with kto_beta against a reference precomputed once from the base). MLX text SFT gains native DoRA (lora.use_dora: true), training.eval_steps counted in optimizer updates, the loss watchdog, and seeded runs (training.seed and training.data_seed are applied, so a run reproduces). lora.use_rslora is 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_expert streams 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 | auto keeps 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 default on keeps the shipped behaviour. Also new: training.stream_loss_chunk (head and loss in token chunks, sft) and training.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-key makes a push safe to retry; soup train records 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-provenance checks 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_rank sends all of a prompt's completions to one judge call and scores each against the others; the run must state training.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 for soup data ingest; soup merge --low-memory merges a plain LoRA adapter into a safetensors base one tensor at a time, with valu
  4. 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, bco and preference no longer fail inside trl on examples/data/dpo_sample.jsonl, examples/data/chat_preferences.jsonl or any dataset written the same way (#1849, #1866).
    • soup.yaml and 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.train that names a Hub dataset with a dot in its name, such as teknium/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 prompt beside chosen / rejected message lists that repeat the user turn failed inside trl after the model had loaded, with an error that changed with PYTHONHASHSEED ('list' object has no attribute 'endswith', or a TypeError). The dpo and kto converters now hand the trainer every preference row in one layout: all strings, or all message lists with the prompt turns in prompt only. reward_model no longer trains on a text with the question in it twice, and orpo and simpo prepare 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.5 failed with Data file not found because the loader read the dot as a file extension. A single string now takes the same route as the same name inside a data.train list: 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-config files, 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 by soup ship. A soup.yaml that 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.0 was 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 is typer>=0.15.4; click>=8.0.0 was 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, and soup doctor reports 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
  5. 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.yaml could 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 the grpo_beta KL penalty to every GRPO variant. It also adds soup adapters check, soup bench train, soup data clean, soup recipes verify and soup 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: run soup doctor --config soup.yaml before 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_type where no setup reads it (#1697), training.freeze_layers / freeze_ratio outside the text sft / tts path on the transformers backend (#1497, #1581), training.auto_mixed_precision outside sft / tts (#1618), and backend: unsloth on seven further tasks that have no unsloth setup (#1357). Other refusals follow the same rule, for example expand_layers, freeze_trainable_layers, loraplus_lr_ratio and moe_lora combinations 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_steps and 26 other staged fields) warn at load now and are refused from v0.77.
    • Validation loss is now evaluated, and grpo, ppo and online_dpo no longer hold rows out. data.val_split held 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 new training.eval_steps, and records the val loss; runs take a few percent longer. grpo, ppo and online_dpo evaluate by generating, so by default they neither evaluate nor withhold rows: data.val_split is ignored with a note and every row trains, which changes the rows a default config trains on.
    • Every non-standard grpo_variant applies the grpo_beta KL penalty (#1232, #1249). gspo, dapo, dr_grpo, bnpo, two_sided and rft validated grpo_beta and dropped it, so existing variant configs will not reproduce prior runs. grpo_beta: 0 now 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 gate and soup eval against fail with 2 (was 1), soup lock check drift is 2 (was 3), soup expect suite failure is 2 (was 3), soup data validate input 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_ratio now applies on every LoRA task instead of only sft, pretrain and embedding, so those runs produce different weights (#745, #1080). soup export --format torchao and training.nvfp4 resolve their torchao classes from the module that defines them and the [qat] extra now needs torchao>=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 preprocess sto

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    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 a soup.yaml. It prices the run from the model's config.json, shaped by --seq, --batch, --method, --lora-r, --optimizer and --gradient-checkpointing; on Apple Silicon it compares with Metal's working set. soup plan and soup apply take --minillm-on-policy.
    • More training on Apple Silicon. task: preference with preference_loss: dpo, orpo or simpo runs the native MLX trainer, MLX GRPO trains on 4-bit mlx-community bases, and MLX Adafactor follows the configured learning rate and schedule.
    • Two new DPO recipes, qwen3.5-0.8b-dpo and qwen3.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_chunk step 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 --output that names an input (soup data ingest, persona-mix, generate and others); soup ship --adapter refuses an adapter that changes nothing; soup distill-prompt, soup data evolve, gen-magpie and filter --perplexity fail instead of writing an empty or unfiltered file.
    • Preference and RL fixes. data.max_length holds again for SimPO and KTO, PPO checkpoints its policy when asked (and no longer crashes under 4-bit and 8-bit), and soup merge loads 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 stop pip from writing into the system Python, because apt manages those files too. pipx and uv tool sidestep it by giving Soup its own environment, which is why they are listed first above. python3 -m venv .venv && source .venv/bin/activate then plain pip works 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
    

    Soup Web UI — New Training

    Web UI documentation

    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:

    GuideCovers
    Training tasks & methodsSFT, 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 & efficiencyDoRA, LoRA+, rsLoRA, VeRA, OLoRA, NEFTune, PiSSA, ReLoRA, optimizer & PEFT zoo, LLaMA Pro, GaLore, YaRN/LongLoRA, packing, curriculum, auto-tuning
    Performance & quantizationQAT, 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 engineeringFormats, the Axolotl/LF-parity pipeline, data tools, synthetic generation & forge, quality scorecards, trace tooling, remote datasets, mixing, recipe DAGs
    Evaluation & probesEval design/gate, eval-gated training, benchmarks, NLG metrics, calibration, Elo arena, diagnose, post-train X-ray probes, A/B, drift, tunability, soup advise
    Serving & exportOpenAI-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 & governanceAdapter 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 quickstartHIPAA/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 & opsMLX/Unsloth backends, alternative hubs, HF Hub integration, autopilot, experiment tracking, plan/apply, env lockfiles, hardware-fit, completions, plugins, utility commands
    Command referenceThe full soup command list
    Supported models & extrasRecommended 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).

    VRAMMax model (QLoRA 4-bit)Example
    8 GB~7BLlama-3.1-8B, Mistral-7B
    16 GB~14BPhi-4-14B, Qwen2.5-14B
    24 GB~34BCodeLlama-34B, Yi-1.5-34B
    48 GB~70BLlama-3.3-70B
    80 GB+70B+ (full) or MoEMixtral-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.

    Contributors

    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.