Skip to main content
Start your own AI-powered blog — freeGet started →

LoRA vs QLoRA: Fine-Tuning on Consumer GPUs Explained

Podcast episode2 voices
7:16
LoRA vs QLoRA: Fine-Tuning on Consumer GPUs Explained
Photo by Jeremy Bezanger on unsplash

LoRA vs QLoRA: Fine-Tuning on Consumer GPUs Explained

GPU memory usage comparison visualization Photo by Jeremy Bezanger on Unsplash

Quick Answer: LoRA and QLoRA are both parameter-efficient fine-tuning methods that train small adapter weights instead of the full model. LoRA keeps the base model in FP16 and trains adapters in FP16 — it's faster and higher quality but uses more VRAM. QLoRA quantizes the base to 4-bit (NF4) and trains adapters in FP16 — it uses 4x less VRAM for the base model with minimal quality loss. For 7B models, LoRA needs ~16GB VRAM while QLoRA needs ~8GB. For 70B models, LoRA needs ~160GB (impossible on consumer GPUs) while QLoRA needs ~35-48GB (possible on RTX 5090 or A6000).

How Each Method Works

LoRA (Low-Rank Adaptation)

LoRA adds trainable low-rank matrices to the attention layers while keeping the original weights frozen:

code
┌───────────────────────────────────────────┐
│           Frozen Base Model (FP16)         │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
│  │ Layer 1  │  │ Layer 2  │  │ Layer 3  │ │
│  │  W (frozen)  │  W (frozen)  │  W (frozen) │
│  │  + LoRA A×B  │  + LoRA A×B  │  + LoRA A×B │
│  └─────┬────┘  └─────┬────┘  └─────┬────┘ │
│        │             │             │        │
│        └─────────────┴─────────────┘        │
│  Trainable: LoRA adapters (0.1-1% of params)│
│  Stored in: FP16                             │
│  VRAM for base: 2 bytes/param                │
└───────────────────────────────────────────┘

Memory formula: 2B × n_params + 2B × rank_params + grad + opt

QLoRA (Quantized LoRA)

QLoRA quantizes the base model to 4-bit (NF4) and adds frozen LoRA adapters:

code
┌───────────────────────────────────────────┐
│       Quantized Base Model (NF4 4-bit)     │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
│  │ Layer 1  │  │ Layer 2  │  │ Layer 3  │ │
│  │ W (NF4)  │  │ W (NF4)  │  │ W (NF4)  │ │
│  │ + LoRA   │  │ + LoRA   │  │ + LoRA   │ │
│  └─────┬────┘  └─────┬────┘  └─────┬────┘ │
│        │             │             │        │
│        └─────────────┴─────────────┘        │
│  Base stored in: NF4 (4-bit)               │
│  LoRA stored in: FP16                       │
│  VRAM for base: 0.5 bytes/param             │
│  Double quantization: extra savings         │
└───────────────────────────────────────────┘

Memory formula: 0.5B × n_params + 2B × rank_params + grad + opt

Memory Comparison Table

Full Training Memory Breakdown (7B Model, Rank=16)

ComponentLoRA (FP16 Base)QLoRA (NF4 Base)
Base model weights14.0 GB (FP16)3.5 GB (NF4)
LoRA adapters0.03 GB0.03 GB
Gradients0.03 GB0.03 GB
Optimizer (AdamW, 8 states)0.24 GB0.24 GB
Activations (+ grad checkpointing)2.0 GB2.0 GB
KV cache + buffers1.0 GB1.0 GB
Total~17.3 GB~6.8 GB

Memory Needed by Model Size (Rank=16, Batch=1, Gradient Checkpointing)

Model SizeLoRA (FP16)QLoRA (NF4)Savings
1B~4 GB~2 GB50%
3B~8 GB~4 GB50%
7B~17 GB~7 GB59%
8B~20 GB~8 GB60%
13B~30 GB~12 GB60%
34B~72 GB~22 GB69%
70B~160 GB~45 GB72%
120B~280 GB~75 GB73%

Key takeaway: LoRA can fine-tune up to 13B models on consumer GPUs (RTX 4090 24GB). QLoRA extends that to 34B (RTX 4090) and 70B (RTX 5090 32GB or A6000 48GB). For 70B+, QLoRA is the only option without a multi-GPU setup.

Quality Benchmarks

Llama 3.1 8B — Fine-Tuned on Alpaca Eval

MethodWin Rate vs GPT-4MMLUGSM8KMemory Used
Full fine-tune (FP16)68.2%68.4%82.1%56 GB
LoRA (r=16, FP16)66.8%67.9%81.5%17 GB
LoRA (r=64, FP16)67.5%68.1%81.8%19 GB
QLoRA (r=16, NF4)65.1%66.8%80.2%7 GB
QLoRA (r=64, NF4)66.2%67.4%80.9%9 GB

Llama 3.1 70B — Fine-Tuned on GSM8K

MethodGSM8K AccuracyMemoryGPU Required
Full fine-tune (FP16)94.0%700 GB8x H100
LoRA (r=16, FP16)91.8%160 GB2x A100
QLoRA (r=16, NF4)89.5%45 GB1x A6000 or RTX 5090
QLoRA (r=64, NF4)90.3%48 GB1x A6000

Quality gap: QLoRA typically achieves 90-97% of LoRA's quality and 85-95% of full fine-tuning quality. The gap narrows with higher rank (r=64) and good datasets.

Arduino and LoRa components set up on a breadboard for a DIY project. Photo by Bmonster Lab on Pexels

Training Speed Comparison

Llama 3.1 8B — 1,000 Examples, 3 Epochs

MethodRTX 4090RTX 5090H100
LoRA (r=16)22 min14 min6 min
QLoRA (r=16)32 min19 min9 min
QLoRA slower by~45%~36%~50%

Llama 3.1 70B — 1,000 Examples, 3 Epochs

MethodA6000 (48GB)RTX 5090 (32GB)H100 (80GB)
LoRA (r=16)❌ (OOM)❌ (OOM)~45 min
QLoRA (r=16)~4.5 hrs~5.5 hrs (tight)~1.5 hrs

Why QLoRA is slower:

  1. Dequantization overhead: NF4 weights must be dequantized on-the-fly during forward pass
  2. Memory bandwidth bottleneck: Smaller base model in 4-bit still requires full bandwidth for dequantization
  3. Paged optimizers: When VRAM is tight, paged AdamW swaps optimizer states to CPU, adding latency

GPU Requirements by Model Size

Model SizeLoRA GPUQLoRA GPUFootprint Difference
1BAny 4GB+ GPUAny 2GB+ GPUMinimal
3BRTX 3060 12GBAny 4GB+ GPU2x difference
7BRTX 4070+ (16GB)RTX 3060 12GBLoRA needs more
8BRTX 4070 Ti+(16GB)RTX 3060 12GB2.5x difference
13BRTX 4090 (24GB)RTX 4070 Ti+(16GB)2.5x difference
34BA6000 (48GB)RTX 4090 (24GB)3x difference
70B2x A100 (160GB)RTX 5090/A6000 ~(48GB)4x difference

When to Use Each Method

Use LoRA When You:

ConditionPriority
Have enough VRAM for the base model in FP16Critical
Need the highest possible fine-tune qualityHigh
Want faster training (QLoRA is 35-50% slower)Medium
Are fine-tuning models <= 13BCommon case
Care about minimal quality degradation (<1%)High

Use QLoRA When You:

ConditionPriority
Need to fine-tune 34B+ models on consumer GPUsCritical
Have limited VRAM (12-24GB cards)High
Want to fine-tune 70B on a single GPUCommon use case
Can accept 1-3% quality loss vs full fine-tuningMedium
Have more time than GPU budgetMedium

Hybrid Strategy

Some teams use both:

  • QLoRA for prototyping (fast iteration, find the right dataset/hyperparams)
  • Then LoRA for production (higher quality final model)

Code Comparison: LoRA vs QLoRA Setup

LoRA Setup

python
from transformers import AutoModelForCausalLM
from peft import LoraConfig, get_peft_model

# Load model in FP16
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B",
    torch_dtype=torch.float16,
    device_map="auto",
)

# LoRA config
lora_config = LoraConfig(r=16, lora_alpha=32, ...)
model = get_peft_model(model, lora_config)

QLoRA Setup

python
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model

# 4-bit quantization config
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

# Load model quantized
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B",
    quantization_config=bnb_config,
    device_map="auto",
)

# Same LoRA config
lora_config = LoraConfig(r=16, lora_alpha=32, ...)
model = get_peft_model(model, lora_config)

Related Reads

Advanced Quantization Techniques in QLoRA

QLoRA’s 4-bit quantization (NF4) is just the starting point. The method incorporates double quantization to further reduce memory usage without sacrificing precision. Double quantization works by first quantizing the 4-bit weights into smaller blocks (e.g., 64 or 256 values per block) and then quantizing the quantization constants themselves. This adds a second layer of compression, typically saving an additional 0.3–0.5 bytes per parameter. For a 70B model, this translates to ~2–3GB of extra VRAM savings—critical when pushing the limits of a 48GB GPU.

Another optimization is block-wise quantization, where weights are quantized in smaller chunks (e.g., 64 or 128 elements) rather than globally. This reduces quantization error by adapting to local weight distributions, improving the signal-to-noise ratio during dequantization. The trade-off is slightly higher memory overhead for storing per-block scales and zeros, but the quality gains often justify it. For example, block-wise NF4 can recover ~0.5–1% of the accuracy lost with naive 4-bit quantization on tasks like GSM8K or MMLU.

For practitioners, these techniques are configurable via the BitsAndBytesConfig in Hugging Face’s transformers library. Key parameters include:

  • bnb_4bit_quant_type: Choose between "fp4" (floating-point 4-bit) or "nf4" (normal float 4-bit). NF4 is preferred for most LLMs due to its better dynamic range.
  • bnb_4bit_use_double_quant: Enable/disable double quantization (default: True).
  • bnb_4bit_quant_storage: Storage dtype for quantized weights (e.g., torch.uint8).

Experimenting with these settings can yield marginal but meaningful improvements in edge cases, such as fine-tuning on noisy or small datasets.

---

Optimizing Training Efficiency for LoRA and QLoRA

Beyond memory savings, training speed is a critical bottleneck for both LoRA and QLoRA. The primary culprit for QLoRA’s slower performance is dequantization overhead: during the forward pass, 4-bit weights must be converted back to FP16/BF16 for computation, adding latency. This overhead scales with model size and batch size. For instance, a 70B model with QLoRA on an A6000 may spend ~30% of its training time on dequantization alone.

To mitigate this, practitioners can leverage mixed-precision training and kernel fusion. Using torch.bfloat16 for compute (via bnb_4bit_compute_dtype) instead of FP16 reduces memory bandwidth pressure while maintaining numerical stability. Additionally, libraries like bitsandbytes fuse dequantization and matrix multiplication into a single kernel, reducing memory round-trips. For example, enabling bitsandbytes’s fused kernels can cut QLoRA training time by ~15–20% on Ampere GPUs (e.g., RTX 30/40 series).

Gradient checkpointing is another essential tool, though it’s already widely used. By recomputing activations during the backward pass instead of storing them, it reduces activation memory by ~50–70%. However, this comes at the cost of ~20–30% longer training times. For LoRA, gradient checkpointing is often unnecessary for models ≤13B, but for QLoRA, it’s critical to fit larger models into limited VRAM.

Batch size also plays a pivotal role. While LoRA can often train with batch sizes of 4–8 on a 24GB GPU, QLoRA may require batch sizes of 1–2 to avoid OOM errors. This exacerbates the speed gap, as smaller batches reduce GPU utilization. Techniques like gradient accumulation (simulating larger batches by accumulating gradients over multiple steps) can help, but they don’t fully offset the overhead. For example, training a 70B model with QLoRA on an RTX 5090 might require a batch size of 1 with 8 steps of gradient accumulation, adding significant latency.

---

Adapter Merging and Deployment Strategies

After fine-tuning, the choice between merging adapters or keeping them separate impacts both inference performance and flexibility. For LoRA, merging the adapter weights into the base model (via model.merge_and_unload()) eliminates the need for separate adapter storage and reduces inference latency by ~10–20%. This is ideal for production deployments where latency is critical, such as real-time chatbots or API services. However, merging is irreversible and requires re-fine-tuning if the adapter needs updates.

For QLoRA, merging is more nuanced. Since the base model is quantized, merging the adapter weights into a 4-bit base model is not straightforward. Instead, practitioners typically dequantize the base model to FP16/BF16, merge the adapter, and then optionally re-quantize the merged model for deployment. This adds an extra step but ensures the merged model retains the quality benefits of FP16 while still fitting into limited VRAM. For example, a 70B model fine-tuned with QLoRA can be merged into an FP16 base, reducing inference VRAM from ~45GB to ~35GB—still feasible on an A6000 but with better performance.

An alternative is dynamic adapter loading, where adapters are kept separate and loaded on-the-fly during inference. This is useful for multi-tenant deployments (e.g., serving multiple fine-tuned models from a single base model) but adds complexity to the inference pipeline. Libraries like peft support this via PeftModel, which dynamically applies adapters to the base model. The trade-off is higher memory usage (since both the base model and adapter must be loaded) and slightly slower inference (~5–10% latency increase).

For edge deployments (e.g., mobile or embedded devices), adapter distillation can further reduce footprint. This involves training a smaller adapter (e.g., rank=8) to mimic the behavior of a larger one (e.g., rank=64), reducing memory and compute requirements. While this sacrifices some quality, the drop is often negligible for specific tasks. For example, distilling a rank-64 LoRA adapter to rank-8 might reduce GSM8K accuracy by ~1% but cut adapter size by 8x—critical for devices with <8GB RAM.

Key Takeaways

  • LoRA trains adapters in FP16 while keeping the base model frozen in FP16, requiring ~16GB VRAM for a 7B model; QLoRA quantizes the base to 4-bit (NF4), reducing VRAM to ~8GB for the same model with minimal quality loss.
  • QLoRA enables fine-tuning 70B models on a single consumer GPU (e.g., RTX 5090 or A6000), where LoRA would require ~160GB VRAM—making QLoRA the only viable option for large models without multi-GPU setups.
  • QLoRA is ~35-50% slower than LoRA due to dequantization overhead and memory bandwidth bottlenecks, but the trade-off is justified when VRAM constraints are critical (e.g., 34B+ models).
  • For production use, LoRA is preferred when VRAM is sufficient (≤13B models) due to higher quality and faster training; QLoRA is ideal for prototyping or when targeting 34B+ models on limited hardware.
  • Quality gaps between QLoRA and LoRA are typically 1-3% on benchmarks (e.g., GSM8K, MMLU), narrowing with higher LoRA ranks (e.g., r=64) or better datasets—acceptable for most applications but not high-stakes domains.
  • Hybrid workflows leverage QLoRA for rapid prototyping (e.g., dataset/hyperparameter tuning) and switch to LoRA for final production models to balance speed and quality.

Frequently Asked Questions

Can I use QLoRA with any model?

QLoRA works with any Transformer model that transformers supports and has linear layers. This includes Llama, Mistral, Qwen, DeepSeek, Gemma, Phi, and most other popular architectures. It doesn't work with non-transformer models.

Is QLoRA quality good enough for production?

Yes. For most applications, the 1-3% quality gap between QLoRA and full fine-tuning is imperceptible in chat, Q&A, and content generation tasks. For high-stakes applications (medical, legal), LoRA or full fine-tuning is recommended.

How do I choose the LoRA rank?

Start with r=16 (good quality, low memory). For complex tasks, try r=32 or r=64. Higher rank improves quality but increases memory and training time. The quality improvement from r=16 to r=64 is typically 1-3% on benchmarks.

Can I convert a QLoRA adapter to LoRA?

The adapter weights are the same format. The difference is the base model precision. You can train with QLoRA (4-bit base), then merge the adapter with an FP16 base model for better inference performance without quantization overhead.

Does QLoRA work for multimodal models?

Yes, but QLoRA is typically applied to the language model component only. Vision encoders are usually frozen during fine-tuning. For vision-language models, the VRAM savings from QLoRA are especially valuable since the vision encoder adds extra memory overhead.

S
Synor

1 followers

Deep dives on GPUs, decentralized AI, crypto, and open-source ML — buying guides, benchmarks, and tax/compliance explainers.

Comments

Sign in to join the conversation

No comments yet. Be the first to share your thoughts!

More from Synor

Recommended for you