Guide
Fine-tuning Open-Weight Models: LoRA, QLoRA, and Production Export
Fine-tuning adapts open-weight models (Llama, Qwen, DeepSeek) to domain-specific tasks without retraining from scratch. This guide covers LoRA and QLoRA memory optimization, tooling stacks (Unsloth, Axolotl), GPU setup, and exporting trained weights to GGUF/EXL2 formats for local serving via Ollama or vLLM.
Editorial review
AI tools, model releases, pricing, licenses, and platform terms can change quickly. Verify the official source before production or commercial use.
Why fine-tune open-weight models?
Frontier models (Claude, GPT-4, Gemini) are expensive and closed. Open-weight models (Llama 3.1, Qwen 2.5, DeepSeek) are free, auditable, and can run locally. But they are often trained on generic data and underperform on specialized tasks: customer support tone, technical documentation writing, domain-specific code generation, or niche classification tasks.
Fine-tuning takes a pre-trained open-weight model and updates its weights using your task-specific data. Done well, a fine-tuned 7B model can outperform a generic 70B model on your specific use case — at a fraction of the compute cost and with zero data leaving your infrastructure.
When NOT to fine-tune: If your data is tiny (under 100 examples), prompt engineering or in-context learning often works better. If you need reasoning on novel problems, frontier models still win. If your data is proprietary and model IP matters, fine-tuning is a way to keep training in-house.
Core architectures: LoRA vs QLoRA vs full fine-tuning
Full fine-tuning updates every parameter in the model. It is memory-intensive and slow. LoRA (Low-Rank Adaptation) trains only small adapter matrices, dramatically cutting memory use. QLoRA quantizes the base model first, then applies LoRA, enabling fine-tuning on consumer hardware.
| Approach | LoRA | QLoRA (Quantized LoRA) | Full Fine-tuning |
|---|---|---|---|
| Memory use | 20-24 GB VRAM (7B model) | 8-12 GB VRAM (7B model with 4-bit quant) | Lowest (1-2 GB for large models) |
| Training speed | Fast | Slower (~20-30% penalty) | Very slow on CPU; acceptable on large GPU |
| Final model quality | Highest (more weight updates) | Nearly identical to LoRA | Lower (restricted to subset of weights) |
| Use case | Research, abundant VRAM | Consumer GPU, single machine | Specialized, domain-specific adaptation |
| Tooling maturity | Established | Production-ready (Unsloth, Axolotl) | Niche, experimental |
Practical recommendation: Start with QLoRA on an 8GB NVIDIA GPU (RTX 3060, RTX 4060, or similar). If you have 24GB+ VRAM, LoRA is slightly faster. Full fine-tuning is rarely necessary for open-weight models unless you are doing research or have unlimited compute.
Memory optimization techniques
Gradient checkpointing
Gradient checkpointing recomputes intermediate activations during backpropagation instead of storing them. It trades ~20-30% training speed for ~30-40% memory savings. Essential when VRAM is tight.
gradient_checkpointing_enable() in Unsloth or set gradient_checkpointing: true in Axolotl config.
Flash Attention 2
Flash Attention is an IO-aware attention algorithm that speeds up training and cuts memory use by ~20%. Most modern libraries (Unsloth, transformers) auto-enable it. Enable explicitly in Axolotl with use_flash_attention: true.
4-bit quantization (QLoRA)
Load the base model in 4-bit using bitsandbytes. Roughly 75% memory savings vs. loading in float16. Apply LoRA adapters on top. When merged post-training, the fine-tuned model retains full precision weights or can be re-quantized.
Batch size and gradient accumulation
Smaller batch size = lower VRAM but noisier training. Gradient accumulation simulates a larger batch by accumulating gradients over multiple steps before updating. Example: batch_size=2, gradient_accumulation_steps=4 simulates batch=8.
Tooling stack: Unsloth vs Axolotl
Two dominant frameworks for fine-tuning open-weight models on consumer hardware: Unsloth for speed and simplicity, Axolotl for production pipelines.
| Aspect | Unsloth | Axolotl | Recommendation |
|---|---|---|---|
| Setup complexity | Simple: pip install unsloth | More complex: config files, multi-GPU | Lighter for quick experiments |
| Training speed | 2-5x faster than stock HF | Baseline (no optimization) | Unsloth wins |
| Data handling | Manual (numpy, pandas) | Built-in (datasets, preprocessing) | Axolotl more robust |
| Distributed training | No (single GPU) | Yes (multi-GPU, multi-node) | Axolotl for scale |
| Logging & checkpointing | Basic (manual save) | Built-in (WandB, tensorboard) | Axolotl more complete |
| Best for | Single-GPU quick training | Production fine-tuning pipeline | Use case dependent |
Unsloth quickstart
Install unsloth, load a model with FastLanguageModel.from_pretrained(), define a training loop using HuggingFace Trainer, and export with model.save_pretrained(). Typical 7B fine-tune on 8GB GPU takes 1-3 hours depending on data size.
Axolotl quickstart
Write a YAML config file specifying model, dataset, LoRA rank, batch size, and checkpointing strategy. Run axolotl train config.yaml. Axolotl handles data loading, logging (WandB), multi-GPU orchestration, and checkpoint management. Steeper setup curve but scales to large teams and datasets.
Compute environment setup
Local hardware (single GPU)
An NVIDIA GPU with 8GB+ VRAM (RTX 3060, RTX 4060, A2000, L4) is sufficient for QLoRA fine-tuning. Install CUDA, cuDNN, and PyTorch with CUDA support. A multi-GPU setup (2x RTX 4090) speeds training but requires distributed training setup (Axolotl handles this).
Cloud GPU instances (RunPod, Lambda Labs, Vast.ai)
For larger models or frequent fine-tuning, rent GPU instances on-demand. RunPod offers competitive hourly pricing for NVIDIA GPUs (RTX 4090, A100) and pre-configured PyTorch pods. Upload your data, spin up an instance, fine-tune in 1-2 hours, download the adapter weights, and power down. Cost: $0.20–$1.50/hour depending on GPU tier.
Explore RunPod for on-demand GPU infrastructure →
Environment setup (local or cloud)
- Create a venv and install dependencies:
pip install unsloth[colab-new] xformers bitsandbytes - Prepare your dataset: CSV or JSON with "text" or "instruction"/"input"/"output" fields. Minimal: 100+ examples of your domain-specific task.
- Load model and configure LoRA rank: Typical rank=16, lora_alpha=32. Higher rank = more learnable parameters but more memory.
- Train: 1–5 epochs depending on data size. Monitor loss and validation accuracy.
Data preparation and training
Data format
Unsloth accepts any HuggingFace-compatible dataset. Axolotl prefers instruction/input/output format (alpaca-style). Example row:
{"instruction": "Summarize in technical terms", "input": "User feedback on latency...", "output": "Summary: ..."}Training hyperparameters
- Learning rate: 1e-4 to 5e-4. Lower for domain-specific fine-tuning. Start conservative; tune down if loss diverges.
- Batch size: 2-4 on 8GB GPU, 8-16 on 24GB. Smaller batch = more gradient noise but more VRAM-efficient.
- Epochs: 1-3 depending on dataset size. One epoch on 1K examples = ~1 hour. Validation loss plateaus quickly; avoid overfitting.
- Warmup steps: 10% of total steps. Prevents gradient shock early in training.
Validation and evaluation
Hold out 10% of your data for validation. Monitor validation loss and perplexity. After training, sample predictions and compare against a baseline model. Use task-specific metrics (BLEU, F1, ROUGE) if applicable.
Export: Merging adapters and quantizing for inference
Step 1: Merge LoRA adapters into the base model
After training, you have two separate checkpoints: frozen base weights (e.g., Llama 3.1 7B) and trained LoRA adapters (~50 MB). Merging combines them into a single model file.
merged_model = model.merge_and_unload() # Unsloth / transformersThe merged model is now a standard HuggingFace model checkpoint ready for quantization or direct serving.
Step 2: Quantize to GGUF or EXL2
Quantization compresses model weights to lower precision (e.g., 4-bit) for inference. Two formats:
| Format | GGUF | EXL2 | GPTQ |
|---|---|---|---|
| Format | GGML binary | GPTQ superset with grouped quantization | vLLM native format |
| Quantization levels | Q4_K_M, Q5, Q6, Q8 (common) | Q4_0, Q4_1, Q5_0, Q5_1 (legacy) | Q4 is most common |
| Local inference runtime | Ollama, llama.cpp | llama.cpp, Ollama (recent) | vLLM (cloud-friendly) |
| File size (7B model) | 3.5-5 GB (Q4) | 3.5-5 GB (Q4) | 3-4 GB (Q4) |
| Speed (inference) | Moderate | Fast with optimized kernel | Fast (batched inference) |
| Best for | Local desktop/laptop | High-throughput local serving | Cloud/scalable inference |
For local serving (Ollama, llama.cpp): Convert to GGUF using llama.cpp/convert.py merged_model/ --outfile model.gguf. Then quantize: quantize model.gguf model-q4.gguf Q4_K_M.
For vLLM serving (cloud-friendly): Use ExLlama2 quantization. Slightly faster inference with similar quality at Q4.
Step 3: Serve locally with Ollama or vLLM
Place the quantized model file (GGUF or EXL2) in Ollama's model directory, create a Modelfile, and run ollama run my-model. Or use vLLM with python -m vllm.entrypoints.openai_api_server --model my-model for OpenAI-compatible inference.
Common pitfalls and best practices
- Overfitting on small datasets: 1 epoch is often enough. Use early stopping. Validate on held-out data.
- Loss diverges: Learning rate too high. Reduce by 2–5x. Ensure warmup steps are set.
- Merged model performs worse than base: Low-quality training data or insufficient epochs. Curate data carefully; balance quality over quantity.
- CUDA out of memory (OOM): Reduce batch size, enable gradient checkpointing, or use 4-bit quantization (QLoRA).
- Quantized model quality drops too much: Use Q5 or Q6 instead of Q4. Test on your specific task.
- Inference is slow: Ensure VRAM-resident inference (model fits entirely in GPU memory). Check batch size during serving.
End-to-end example: Fine-tuning Llama 3.1 8B for customer support
You have 500 customer support exchanges and want a fine-tuned model that responds in your company's tone.
- Prep data: Format as instruction/input/output JSON. 450 train, 50 validation.
- Set up Unsloth: pip install, load Llama 3.1 8B with 4-bit quantization.
- Configure LoRA: rank=16, lora_alpha=32, lora_dropout=0.05.
- Train: 3 epochs, batch=2, lr=2e-4, gradient_accumulation=2. Runs in ~4 hours on RTX 4090 or ~8 hours on RTX 3060.
- Merge and quantize: Merge LoRA adapters, convert to GGUF, quantize to Q4_K_M.
- Serve: Load in Ollama, hit it with your test support tickets. Evaluate on response quality and tone.
- Iterate: If quality is low, curate data further or increase epochs. If overfitting, reduce epochs or add validation regularization.
Summary
Fine-tuning open-weight models is now accessible to individual developers and small teams. QLoRA on an 8GB GPU opens the door to domain-specific model adaptation without frontier model costs or data leaving your infrastructure. Unsloth enables fast, simple fine-tuning. Exporting to GGUF or EXL2 makes inference efficient locally or at scale.
Start with small-scale experiments: 100–200 examples, 3 epochs, QLoRA on a consumer GPU. Validate on a held-out test set. Iterate on data quality. Once your fine-tuned model outperforms the base model on your task, it is ready for production serving.
FAQ
What is the difference between LoRA and QLoRA?
LoRA (Low-Rank Adaptation) trains learnable adapter weights alongside frozen base model weights. QLoRA extends this by quantizing the base model to 4-bit before LoRA training, cutting memory use by ~75% compared to full LoRA. QLoRA is ideal for consumer GPUs; LoRA is better when you have abundant VRAM and want slightly faster training.
How much VRAM do I need to fine-tune a 7B model?
With QLoRA and gradient checkpointing: 8-12 GB VRAM on an NVIDIA GPU. With LoRA (no quantization): 20-24 GB. Full fine-tuning of a 7B model: 40+ GB. Start with QLoRA on an 8GB GPU; if you hit OOM, reduce batch size or use gradient accumulation.
Should I use Unsloth or Axolotl for fine-tuning?
Unsloth is a lightweight library optimized for fast, memory-efficient fine-tuning. Axolotl is a heavier framework with more built-in features (data handling, logging, multi-GPU). Use Unsloth for quick experiments on a single GPU; use Axolotl for production pipelines with complex data and distributed training.
What is gradient checkpointing and why does it matter?
Gradient checkpointing trades compute for memory by recomputing intermediate activations during backpropagation instead of storing them. It cuts memory use by ~30-40% with a ~20-30% training speed penalty. Essential when your VRAM is tight.
Can I fine-tune on CPU only?
Yes, but it is extremely slow. A 7B model fine-tuning step on CPU takes hours. Use GPU if available. If GPU is unavailable, consider smaller models (3B, 1B) or quantize more aggressively to reduce memory footprint.
What does it mean to merge and quantize adapter weights?
After LoRA training, you have frozen base weights + learnable adapter weights. Merging combines them into a single model checkpoint. Quantizing then compresses that merged model to GGUF or EXL2 format for efficient local inference (4-bit, etc.). The merged model can then run on Ollama or vLLM.
How do I know if my fine-tuned model is good?
Evaluate on a held-out test set using task-specific metrics (BLEU for translation, F1 for classification, human eval for chat). Compare against the base model and a frontier model (Claude, GPT-4) on the same task. Fine-tuning is successful if your domain-specific performance improves materially.