Navigation
πŸ‹οΈ Training SystemsΒ· 8 min read

QLoRA

Quantized base model + FP16 adapters β€” fine-tuning 70B models on a single 48GB GPU with 4-bit quantization.

qloraquantizationfine-tuningnf4bitsandbytesconsumer-gpu

QLoRA combines 4-bit quantization of the base model with standard LoRA adapters, enabling fine-tuning of models 4-8Γ— larger than what fits in GPU memory with standard approaches.


1. How QLoRA Works

ComponentPrecisionMemory per param
Base model weightsNF4 (4-bit)0.5 bytes
LoRA adapters (A, B)BF162 bytes
Adapter gradientsBF162 bytes
Adapter optimizer (Adam)FP3212 bytes per adapter param

Key Innovation: NF4 (Normal Float 4-bit)

NF4 is a 4-bit quantization format optimized for normally-distributed weights:

  • Quantization levels are placed at the quantiles of a normal distribution
  • Information-theoretically optimal for neural network weights (which are approximately Gaussian)
  • Better than uniform INT4 for the same bit budget

Double Quantization

QLoRA also quantizes the quantization constants themselves:

  • Block size 64: one FP32 scale per 64 weights β†’ 0.5 bits/weight overhead
  • Double quantize: quantize those scales to FP8 β†’ 0.125 bits/weight overhead
  • Total: 4 + 0.125 = 4.125 bits per weight

2. Memory Budget

ModelBF16QLoRA (NF4)Fits on 24 GB?Fits on 48 GB?
7B14 GB3.8 GB + adaptersβœ…βœ…
13B26 GB7.1 GB + adaptersβœ…βœ…
70B140 GB38 GB + adaptersβŒβœ…
405B810 GB220 GB + adapters❌❌

QLoRA makes 70B fine-tuning feasible on a single A6000 (48 GB) β€” impossible with standard LoRA (needs at least 14 GB for BF16 base).


3. Implementation

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

# Load base model in 4-bit
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",          # NF4 format
    bnb_4bit_compute_dtype=torch.bfloat16,  # compute in BF16
    bnb_4bit_use_double_quant=True,     # double quantization
)

model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-70b-hf",
    quantization_config=bnb_config,
    device_map="auto",
)

# Add LoRA adapters (these are in BF16)
lora_config = LoraConfig(
    r=16, lora_alpha=32,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                     "gate_proj", "up_proj", "down_proj"],
    lora_dropout=0.05,
    task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)

The Backward Pass Through Quantization

During backward, gradients must flow through the quantized base weights:

  1. Quantized weights (NF4) are dequantized to BF16 on-the-fly
  2. Forward/backward computed in BF16
  3. Only adapter gradients are computed and updated
  4. Base weights remain frozen in NF4

No training of base model β€” gradient only flows through adapters. The NF4 base acts as a fixed feature extractor.


4. Quality Considerations

Method7B quality (MMLU)70B qualityMemory (70B)
Full fine-tune (BF16)BaselineBaseline1,120 GB
LoRA r=16 (BF16 base)~98% of full~98%140 GB
QLoRA r=16 (NF4 base)~96% of full~97%38 GB

The ~1-2% quality gap comes from the quantized base: the feature extractor is slightly degraded. For most practical applications, this is acceptable.

When QLoRA Is NOT Enough

  • Tasks requiring very precise base model outputs (e.g., code generation with exact syntax)
  • When the domain shift from pretraining is very large
  • When you need the best possible quality and have enough hardware

For serving fine-tuned models efficiently, see SFT Pipelines.


Self-Check

  • Explain NF4 and why it’s better than uniform INT4 for neural network weights
  • Calculate QLoRA memory for a 70B model with r=16 adapters
  • Explain double quantization and its memory savings
  • Implement QLoRA with bitsandbytes and PEFT
  • Identify the quality tradeoff vs standard LoRA

References