Soup CLI는 복잡한 설정이나 SSH 없이 단 하나의 명령어와 YAML 파일로 대형 언어 모델(LLM) 파인튜닝을 수행할 수 있게 해주는 오픈소스 도구입니다. 최신 업데이트를 통해 메모리 최적화 기술(QLoRA, 레이어 스트리밍)을 적용하여 VRAM 4GB 환경에서도 80억(8B) 매개변수 모델의 파인튜닝 및 DPO 등의 선호도 학습이 가능해졌습니다. GPU 인프라 구축에 소모되는 시간을 획기적으로 줄여 개발자와 연구자들이 모델 자체의 개선에 집중할 수 있도록 돕는 것이 핵심 가치입니다.
번역된 본문
제목: Show HN: 4GB 노트북 GPU로 8B 모델 파인튜닝하기
소스: Hacker News
Soup: 단 한 번의 명령어로 LLM 파인튜닝 및 사후 학습(Post-train)을 수행하세요. SSH도, 설정 지옥도 없습니다.
(웹사이트 · 퀵스타트 · 설정 · 문서 · 명령어 · 모델 · 디스코드)
Soup는 LLM 파인튜닝의 고통을 단순한 워크플로우로 바꿔줍니다. 하나의 설정 파일, 하나의 명령어, 그리고 끝.
pip install "soup-cli[train]" # 파인튜닝을 위해 [train]을 추가하세요. 단독 `soup-cli`는 경량 CLI입니다.
soup init --template chat
soup train
왜 Soup인가?
LLM 학습은 여전히 고통스럽습니다. 숙련된 팀조차 모델을 개선하는 대신 인프라와 씨름하는 데 30~50%의 시간을 낭비합니다. Soup가 이 문제를 해결합니다.
SSH 제로 (Zero SSH): 고장 난 GPU 서버에 접속하기 위해 다시는 SSH를 사용할 필요가 없습니다.
단일 설정 (One config): 간단한 YAML 파일 하나면 충분합니다.
모든 것의 자동화 (Auto everything): 배치 사이즈, GPU 감지, 양자화(Quantization) — 모두 자동으로 처리됩니다.
새로운 기능 (v0.72.4 — 노트북에서의 정렬(Align): 레이어 스트리밍을 통한 DPO, ORPO, SimPO 및 KTO 지원)
레이어 스트리밍(Layer streaming)은 동결된(frozen) 베이스 모델을 VRAM 밖으로 유지하고, 한 번에 하나의 디코더 레이어씩 GPU로 공급합니다. 이전에는 지도 파인튜닝(Supervised fine-tuning)만 지원했지만, 이제 선호도 손실(preference losses) 연산도 실행합니다.
DPO의 참조 모델(Reference model)은 무료입니다. DPO는 비교를 위해 참조 모델이 필요하지만, 두 번째 모델 복사본을 띄우면 메모리가 두 배로 늘어나 레이어 스트리밍의 의미가 없어집니다. Soup는 어댑터(adapter)를 끈 상태로 동일한 스트리밍 베이스를 사용합니다 — 즉, 하나의 가중치 세트와 하나의 스트림만 사용합니다.
RTX 3050 4GB 환경 측정 결과: 스트리밍 DPO의 최고점은 지도 파인튜닝 최고점의 0.914배 수준을 기록했습니다. 동일한 테스트에서 실제 두 번째 모델을 강제로 넣으면 정확히 가중치 복사본 하나 크기인 +730 MB가 추가 소모되었습니다.
KTO는 일반적으로 설명되는 것과 달리 참조 모델이 필요 없는(Reference-free) 기법이 아닙니다. DPO와 동일한 방식으로 참조 모델을 선택하므로 동일한 처리를 받습니다. 반면 ORPO와 SimPO는 진정으로 참조 모델이 필요 없습니다.
정상적인 비스트리밍 연산과 완벽히 동일한 결과(Bit-exact): 손실값 차이 0.0. 이 시리즈의 모든 릴리스가 통과해야 하는 기준점입니다.
사전 VRAM 점검 기능: 쌍을 이루는 손실값(paired loss)의 행(row)이 두 배라는 것을 인지합니다. 선택된(chosen) 답변과 거부된(rejected) 답변이 하나의 텐서로 모델을 통과하기 때문입니다.
정직한 비용 평가: 참조 모델은 메모리에서는 공짜지만 시간에서는 그렇지 않습니다. DPO는 단계별로 지도 파인튜닝보다 레이어 스택을 1.52배 더 자주 읽습니다.
grpo / ppo는 의도적으로 제외되었습니다. 토큰 생성 시 모든 레이어를 다시 읽어야 하기 때문입니다. 이는 스트리밍으로 상쇄(amortise)할 수 없는 연산입니다.
여전히 베타(BETA) 버전입니다.
# soup.yaml — 이후 `soup train --config soup.yaml` 실행
training:
stream_layers: true # 베이스 모델은 VRAM에서 스트리밍되고, 오직 어댑터만 학습됩니다.
quantization: 4bit # NF4 — 약 4배 작은 저장 공간으로 8B 모델이 4GB 카드에 맞습니다.
batch_size: 4 # v0.72.3: 더 큰 배치 사이즈가 가중치 읽기를 상쇄합니다.
stream_source: auto # RAM이 여유 있을 때는 RAM을, 부족할 때는 NVMe 디스크를 사용합니다.
v0.72.0에서 stream_layers: true로 학습하셨나요? 그 어댑터는 작동하지 않습니다(inert). 해당 텐서들이 .inner. 세그먼트가 추가된 키 아래 저장되어 모든 로더가 튜닝되지 않은 베이스 모델을 반환했기 때문입니다. v0.72.1에서 수정되었습니다; 다시 실행하거나 다시 저장하세요. 다음 명령어로 확인하세요: python -c "from safetensors.torch import load_file; print([k for k in load_file('adapter_model.safetensors') if '.inner.' in k][:3])"
이전 릴리스 — v0.71.40, 가중치를 위한 CI가 아닌 프롬프트를 위한 CI (배송 판결(ship verdict) 내보내기 및 기원绑定)
(해당 내용 원문 누락으로 번역에서도 제외됨)
이전 릴리스 — v0.71.39, 가중치용 CI (emit + provenance-bind the ship verdict)
soup ship의 판결 결과를 내보내고(emit), 커밋하고, 출처를 귀속시킬(provenance-bind) 수 있게 되었습니다: --emit-evidence는 실행 결과를 똑같은 판결로 재생성하고, soup.yaml의 eval.ship + --config는 게이트 정책을 검토 가능하게 하며, --config는 증거를 생성한 정확한 레시피에 묶습니다(만료된 증거 → 종료 코드 3).
soup ship --push owner/repo#N은 PR에 SHIP / DON'T-SHIP(배포/보류) 카드를 게시합니다.
Soup Fine-tune and post-train LLMs in one command. No SSH, no config hell. Website · Quick Start · Config · Docs · Commands · Models · Discord 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 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.72.4 — align on a laptop: DPO, ORPO, SimPO and KTO over layer streaming. Layer streaming keeps the frozen base out of VRAM and feeds it to the GPU one decoder layer at a time. It used to support supervised fine-tuning only; now it runs the preference losses too. DPO's reference model is free. DPO needs a reference to compare against, and a second copy of the model would double memory and defeat the whole point. Soup uses the same streamed base with its adapters switched off — one set of weights, one stream. Measured on an RTX 3050 4 GB: streamed DPO peaked at 0.914× the supervised-fine-tuning peak. Forcing a real second model in the same test cost +730 MB — exactly one copy of the weights. KTO is not reference-free , however it is usually described: it picks its reference the same way DPO does, so it gets the same treatment. ORPO and SimPO genuinely are. Bit-exact against a normal, non-streamed run of the same loss — 0.0 difference, the bar every release in this series has to clear. The VRAM pre-flight knows a paired loss is twice the rows , because chosen and rejected go through the model as one tensor. Honest cost: the reference is free in memory , not in time — DPO reads the layer stack 1.52× as often per step as supervised fine-tuning does. grpo / ppo stay excluded on purpose: generation re-reads every layer per token, which is exactly what streaming cannot amortise. Still BETA. # soup.yaml — then just `soup train --config soup.yaml` training : stream_layers : true # base streams out of VRAM; only the adapter trains quantization : 4bit # NF4 — ~4x smaller store, so 8B fits a 4 GB card batch_size : 4 # v0.72.3: bigger batches amortise the weight read stream_source : auto # RAM when it fits, NVMe disk when it does not Trained with stream_layers: true on v0.72.0? That adapter is inert — its tensors were saved under keys with an extra .inner. segment, so every loader returned the untuned base. Fixed in v0.72.1; re-run or re-save. Check with: python -c "from safetensors.torch import load_file; print([k for k in load_file('adapter_model.safetensors') if '.inner.' in k][:3])" Previous release — v0.71.40, soup reward synth (generate a reward verifier from your data) Point soup reward synth at a JSONL of reference outputs and it infers a deterministic verifier, writes a readable / committable .py reward function, and — the part nobody else does — refuses to emit one that can't tell your references from bad answers (four families: numeric / json_schema / regex / tool_call ; a mandatory calibration report is the moat). Reward ensembles ( reward_fn: "accuracy,format" ) also train now. (#311) soup reward synth references.jsonl -o reward.py --output-report calib.json Previous release — v0.71.39, CI for weights not prompts (emit + provenance-bind the ship verdict) soup ship 's verdict became emittable, committable, and provenance-bound: --emit-evidence makes a run replay into an identical verdict, eval.ship in soup.yaml + --config makes the gate policy reviewable, and --config binds evidence to the exact recipe that produced it (stale evidence → exit 3). soup ship --push owner/repo#N posts the SHIP / DON'T-SHIP card on the PR. Previous release — v0.71.38, The gate grows teeth (real leg-2 regression gate) soup ship 's regression leg became real: a fixed, extraction-based scorer over seven bundled, offline suites (MCQ · arithmetic · tool-calling · JSON validity · safety/refusal). A tune that wins your task but quietly breaks tool-calling now gets a DON'T SHIP . Zero new deps. soup ship --base ./base --adapter ./my-lora --task-eval my_task.jsonl # exit 0 = SHIP · 2 = DON'T SHIP · 3 = bad flags · 1 = runtime error Previous release — v0.71.33, soup draft (measure speculative decoding) soup draft measure reports a draft model's acceptance rate + real plain-vs-assisted tok/s (exit 0/2/1 for CI); soup draft distill distils your target into a dense tiny draft, auto-wired into soup serve --auto-spec . The honest result on a small same-family pair: distillation didn't move acceptance (69.3% → 69.3%) and assisted decoding was a net slowdown — which is exactly the number you want before shipping speculative decoding. soup draft measure --target ./my-tuned-model --draft HuggingFaceTB/SmolLM2-135M-Instruct \ --prompts prod-prompts.jsonl # -> acceptance %, real tok/s, ship-or-not Full history: CHANGELOG.md · GitHub Releases . Quick Start 1. Install # Light core: CLI + config + data tools, no PyTorch pip install soup-cli # Add the training stack (torch, transformers, peft, trl, datasets, …) pip install " soup-cli[train] " # Everything (train + serve + ui + data) in one shot pip install " soup-cli[all] " # Or from GitHub (latest dev) pip install git+https://github.com/MakazhanAlpamys/Soup.git The full extras table ( fast , mlx , serve , eval , ui , vision , audio , …) lives in docs/models.md . Use double quotes around the extra. They are the only spelling that works in every shell — cmd.exe , PowerShell, bash, and zsh. Older tutorials and videos (including some of ours) show the single-quoted pip install 'soup-cli[train]' . That is bash / zsh / PowerShell syntax, and it fails on Windows cmd.exe , which has no single-quote quoting and hands the quotes straight to pip: ERROR: Invalid requirement: "'soup-cli[train]'": Expected package name at the start of dependency specifier If you hit that, swap the ' for " — pip is rejecting a literal quote character, nothing is wrong with the package. (Dropping the quotes entirely works on Windows too, but zsh then reads [train] as a glob and fails.) 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, AWQ, GPTQ, BitNet) and deployment options live in docs/serving-and-export.md . 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 . 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+, rsL