What you need
- A 24GB NVIDIA GPU (RTX 3090/4090) — or rent one by the hour for ~2€ total (see RunPod vs Vast.ai)
- Linux or Windows + WSL2, Python 3.10+, ~40GB free disk
- A dataset: 500–5,000 examples of the behavior you want (we build it in step 1)
Step 1 — Build the dataset (the part that decides everything)
One JSON object per line (train.jsonl), in chat format:
The rules that matter more than any hyperparameter:
- Consistency beats volume. 800 examples with identical formatting/tone beat 20,000 noisy ones. Every inconsistency in the data becomes a behavior the model learns.
- Show the target behavior, don't describe it. The assistant turns must BE what you want to get.
- Hold out 30–50 examples in a separate eval.jsonl — never trained on, used in step 6.
Step 2 — Install Unsloth (5 min)
Unsloth wraps the PEFT/TRL stack with heavy optimizations — roughly 2× faster and lighter on VRAM than vanilla setups, which is what makes a 14B comfortable on 24GB.
Step 3 — The training script (train.py)
The knobs, decoded: r=16 is the adapter capacity (8–32 is the sane range; higher learns more and forgets more), 2 epochs is the default for behavior tuning, and effective batch = 2×8 = 16 via gradient accumulation. This configuration uses roughly 16–20GB of VRAM.
Step 4 — Train and read the loss (30–90 min)
- Healthy run: loss starts ~1.5–2.5 and declines smoothly toward ~0.5–1.0.
- Loss flat from the start → data format problem (usually the chat template mapping).
- Loss near zero → memorization; the model will parrot training examples. Fewer epochs.
Step 5 — Load into Ollama (5 min)
The script already exported a merged Q4_K_M GGUF into out_gguf/. Create a Modelfile:
Your model now works everywhere Ollama does — including the Open WebUI setup from our first tutorial and the vLLM server (use the merged FP16 export + AWQ for that path).
Step 6 — Evaluate honestly (the step everyone skips)
- Run your held-out eval.jsonl questions against BOTH the base model and your fine-tune. Count: target behavior achieved? format correct?
- Then ask both models 10 GENERAL questions unrelated to your domain — if the tuned model got notably worse at them, that's catastrophic forgetting: retrain with fewer epochs or lower r.
- Keep the eval set forever: rerun it on every future retrain and base-model upgrade.
Troubleshooting
- CUDA out of memory — lower max_seq_length to 1024, then batch size to 1 (raise gradient_accumulation_steps to keep effective batch ~16).
- Model answers in the wrong format after tuning — inconsistent training data, or you served it with a different system prompt than the one trained on. Align them.
- It parrots training examples verbatim — overfitting: 1 epoch, or more diverse data.
- No 24GB GPU at hand — the whole tutorial runs identically on a rented cloud GPU: a 1–2 hour session costs a couple of euros.