Hugging Face

QLoRA on a Consumer GPU: 4-bit Fine-Tuning

4 min read

QLoRA is LoRA on top of a frozen 4-bit base model. bitsandbytes stores the base weights in 4-bit (NF4), and only the small LoRA adapter matrices are trained. Reach for it when you want to tune a roughly 7B model but 16-bit weights plus training overhead don’t fit on your single 16GB or 24GB card.

Not sure fine-tuning is the right tool? Read fine-tuning vs RAG vs prompting first. For plain LoRA without quantization, see LoRA fine-tune a small model. Get your data into shape with prepare a dataset for fine-tuning.

Why it fits in VRAM

The Transformers bitsandbytes docs say 4-bit quantization reduces memory use by 4x. The base is frozen, so gradients and optimizer state exist only for the adapters. The PEFT docs say QLoRA can tune a 65B model on a single 48GB GPU, and the Transformers docs give the example of nested quantization tuning Llama-13b on a 16GB T4 at sequence length 1024, batch size 1, and 4 accumulation steps. Your own peak depends on model, sequence length, and batch size, so measure it.

NF4 on NVIDIA needs a Pascal (GTX 10-series) or newer GPU.

pip install --upgrade transformers accelerate bitsandbytes peft trl datasets
import torch
from datasets import load_dataset
from peft import LoraConfig, prepare_model_for_kbit_training
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from trl import SFTConfig, SFTTrainer

model_id = "Qwen/Qwen2.5-7B-Instruct"

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.bfloat16,
)

model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=bnb_config)
model = prepare_model_for_kbit_training(model)

peft_config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    target_modules="all-linear",
    bias="none",
    task_type="CAUSAL_LM",
)

dataset = load_dataset("trl-lib/Capybara", split="train[:1000]")

args = SFTConfig(
    output_dir="qlora-out",
    per_device_train_batch_size=1,
    gradient_accumulation_steps=16,
    learning_rate=1e-4,
    max_length=1024,
    gradient_checkpointing=True,
    bf16=True,
    num_train_epochs=1,
    logging_steps=10,
)

trainer = SFTTrainer(
    model=model,
    args=args,
    train_dataset=dataset,
    peft_config=peft_config,
)
trainer.train()
trainer.model.save_pretrained("qlora-adapter")

SFTTrainer wraps the quantized model with your peft_config, so you don’t call get_peft_model yourself. The learning rate of 1e-4 follows the TRL tip that adapters usually take a higher rate than full fine-tuning. The Capybara dataset is conversational, so the trainer applies the model’s chat template. The saved folder holds the adapter; share it with Hugging Face upload.

VRAM & pitfalls


Next: LoRA fine-tune a small model | Prepare a dataset for fine-tuning | Fix CUDA out of memory

Questions people ask

Can I train the 4-bit weights themselves?

No. The Transformers docs state that 8 and 4-bit training is only supported for training extra parameters, which is why QLoRA trains LoRA adapters on top of a frozen 4-bit base.

Do I still need prepare_model_for_kbit_training?

The PEFT quantization guide says to call it to preprocess a quantized model for training, so the example below does. It also turns on gradient checkpointing by default.

Open the full interactive version (with copy buttons) ↗

← All guides