M1: Why Fine-Tune M2: Dataset Prep M3: LoRA M4: RLHF M5: Evaluation M6: Deployment Capstone
Intermediate Read time: 16 min Module 3 of 6

LoRA and QLoRA: Fine-Tuning Without a Supercomputer

In 2021, fine-tuning a 7-billion-parameter model required 8 high-end GPUs and roughly $4,000 for a single training run. By 2023, the same model could be fine-tuned on a single consumer GPU for under $10. LoRA made this possible. This module explains exactly how.

Module 3 of 6 — Fine-Tuning LLMs

By the end of this module, you will be able to explain what LoRA does to a weight matrix, choose appropriate rank and alpha values for your task, and understand how QLoRA extends LoRA with quantization to further reduce memory use.

The Problem: Full Fine-Tuning is Expensive

A language model stores its knowledge in weight matrices. A 7-billion-parameter model has roughly 7 billion numbers, each stored as a 16-bit float. That's about 14 GB just to store the weights. During full fine-tuning, you also need gradients and optimizer states — typically 3-4x the model size. Full fine-tuning a 7B model needs roughly 56–80 GB of GPU memory.

Consumer GPUs typically have 8–24 GB. Enterprise GPUs (A100) have 40–80 GB and cost thousands of dollars per hour on cloud services. Full fine-tuning is simply out of reach for most teams.

The Insight Behind LoRA

Hu et al. (arXiv:2106.09685) made a key observation: when you fine-tune a pre-trained model on a downstream task, the changes to the weight matrices have a low intrinsic rank. In plain terms, the meaningful changes during fine-tuning live in a much smaller mathematical space than the full weight matrix.

Instead of updating the full weight matrix W (which might be 4096 × 4096 = 16 million parameters), LoRA adds two small matrices A and B, where the product B×A approximates the weight update. If rank r = 8, then A is 4096×8 and B is 8×4096 — totaling only 65,536 parameters instead of 16 million. That's a 244x reduction.

LoRA low-rank adaptation: original weights plus low-rank adapter input x W (frozen) d x d params A d x r B r x d + Wx + BAx output original path (frozen) LoRA adapter (trainable, rank r) directional illustration — Hu et al. arXiv:2106.09685
Key concept

LoRA freezes the original model weights and adds small trainable matrices alongside them. Only these small matrices are updated during training. At inference time, they are merged back into the original weights — so the final model runs at full speed with no added latency.

The Two LoRA Hyperparameters You Control

Rank (r)

Rank controls how many parameters the adapter matrices contain. Higher rank means more expressive adapters but more memory and compute. Common values are 4, 8, 16, and 32. Start with r=8 for most tasks. Use r=16 or r=32 for complex reasoning or multi-task fine-tuning.

Alpha (α)

Alpha scales the LoRA update relative to the original weights. The effective learning rate of the adapter is proportional to α/r. A common rule of thumb is to set α = 2×r. If r=8, use α=16. This keeps the adapter update magnitude stable as you change rank.

QLoRA: Adding Quantization to LoRA

QLoRA (Dettmers et al., arXiv:2305.14314) extends LoRA by storing the frozen base model weights in 4-bit precision instead of 16-bit. This cuts the base model's memory use by 4x with minimal quality loss on most tasks.

The combination is powerful: with QLoRA, a 7B model that would normally need 56 GB can be fine-tuned on a single 12 GB consumer GPU. This is what made fine-tuning accessible to individual researchers and small teams.

Numbers with source Dettmers et al. (arXiv:2305.14314) showed that QLoRA fine-tuning of a 65B parameter model could match full fine-tuning quality while requiring only a single 48 GB GPU instead of multiple A100s. The paper introduced the NF4 (NormalFloat4) quantization format specifically optimized for normally distributed neural network weights.
LoRA Parameter Explorer Interactive

Adjust model size and LoRA rank to see memory savings and trainable parameter counts.

7B
8
from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
import torch

# Optional: load base model in 4-bit (QLoRA)
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",           # NormalFloat4 — optimal for LLM weights
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_use_double_quant=True        # nested quantization saves ~0.4 bits extra
)

model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b-hf",
    quantization_config=bnb_config,       # remove this line for standard LoRA
    device_map="auto"
)

# Configure LoRA
lora_config = LoraConfig(
    r=8,                                  # rank: start here, raise to 16/32 if needed
    lora_alpha=16,                        # scaling: rule of thumb = 2 * r
    target_modules=["q_proj", "v_proj"], # apply LoRA to attention projections
    lora_dropout=0.05,
    bias="none",
    task_type=TaskType.CAUSAL_LM
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Trainable params: 4,194,304 || All params: 6,742,618,112 || Trainable: 0.06%

Which Layers Should LoRA Target?

LoRA can be applied to any weight matrix in the model. The most common choice is the attention projection matrices: q_proj (query) and v_proj (value). Some practitioners also add k_proj and o_proj. Adding more target modules increases the number of trainable parameters, which can improve performance but also increases memory and training time.

A good default for instruction fine-tuning: target q_proj and v_proj with rank 8. If you see poor performance after 2 epochs, try adding k_proj and o_proj, or increase rank to 16.

Try this — no setup required Calculate LoRA parameter savings for a layer you care about. Take any weight matrix size (e.g., 4096 × 4096 for a medium-sized attention layer). Calculate: full params = 4096 × 4096 = 16,777,216. LoRA params at rank 8 = (4096 × 8) + (8 × 4096) = 65,536. That's a 256x reduction for that layer. Now multiply by the 32 attention layers in a 7B model. The savings compound quickly.

Knowledge check

Q1: You are fine-tuning a model on a complex legal reasoning task. You started with rank 8 and results are mediocre. What should you try next?

A) Reduce rank to 4 — less is more
B) Increase rank to 16 or 32 and add more target modules
C) Switch to full fine-tuning immediately
D) Reduce the dataset size — less data trains faster

Q2: What does the alpha parameter control in LoRA?

A) The number of adapter layers added to the model
B) The scaling factor that controls how much the LoRA update influences the final output
C) The quantization precision used for the base model weights
D) The dropout rate applied during training
After training, do you need to keep the LoRA adapter files separate from the base model? Think first. +
No. After training, you can merge the LoRA adapter weights back into the base model: W_new = W + B×A. The merged model has exactly the same size as the original and runs at the same speed with no adapter overhead. You only keep the adapter files separate if you want to maintain multiple adapters for different tasks on the same base model, which is a valid production pattern.

Before you go

Reflect: If LoRA achieves similar quality to full fine-tuning while using far fewer parameters, what does that tell you about how much of a model's capacity is actually used for any given task?

Share this insight
"LoRA fine-tunes a 7-billion-parameter model by updating only 0.06% of its parameters. The insight is that most of what changes during fine-tuning lives in a tiny mathematical subspace."

You might also like

Want help choosing the right LoRA configuration for your model and task?

Book a free 30-min call with Arjun

References

  1. Hu, E. J. et al. (2021). LoRA: Low-Rank Adaptation of Large Language Models. arXiv:2106.09685.
  2. Dettmers, T. et al. (2023). QLoRA: Efficient Finetuning of Quantized LLMs. arXiv:2305.14314.
  3. Mangrulkar, S. et al. (2022). PEFT: State-of-the-Art Parameter-Efficient Fine-Tuning. Hugging Face GitHub.
  4. Dettmers, T. et al. (2022). LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. arXiv:2208.07339.
  5. Aghajanyan, A. et al. (2020). Intrinsic Dimensionality Explains the Effectiveness of Language Model Fine-Tuning. arXiv:2012.13255.
  6. Chen, L., Zaharia, M., Zou, J. (2023). FrugalGPT. arXiv:2310.11409.
← Module 2: Dataset Prep Module 4: RLHF →
Was this helpful?