Skip to content
learn
  1. Learn
  2. Artificial Intelligence
  3. Training, Fine-tuning and Alignment

AI AI-06.06

Parameter-efficient fine-tuning: LoRA and QLoRA

LoRA freezes the base model and trains small low-rank adapters; QLoRA keeps the base in 4-bit. Rank, alpha, merging and serving many adapters.

Intermediate 14 min Last reviewed

The big idea

Full fine-tuning updates every weight in the model. For a 7-billion-parameter model trained with Adam in mixed precision, the weights, gradients and optimizer state alone need over 100 GiB, before counting activations. And every fine-tuned variant is another full-size copy of the model to store and serve.

LoRALow-Rank Adaptation: fine-tuning that freezes the pretrained weights and learns a small low-rank update (two thin matrices B and A) added to selected weight matrices. takes a different route. It freezes the original weights and learns a small correction next to selected weight matrices, written as the product of two thin matrices. Only those thin matrices are trained: often well under 1 percent of the parameters. QLoRALoRA fine-tuning on top of a base model stored in 4-bit precision, which cuts the memory for the frozen weights by about four times compared with 16-bit. goes further and stores the frozen base model in 4-bit precision, so a large model fits on a single GPU.

Think of renovating a rented flat. You are not allowed to knock down walls (the frozen weights), but you can add furniture, rugs and lamps (the adapters) that change how the place feels. The changes are small, cheap, and easy to take out again, and the next tenant can bring their own set into the same flat.

How it works

A LoRA adapter beside a frozen weight matrix An input vector x goes through two paths. The main path multiplies by the frozen pretrained weight matrix W, which is large and dim. The side path multiplies by A, a thin matrix that squeezes x down to r numbers, then by B, which expands them back to full size; both glow because they are the only trained parts. The two results are added, with the side path scaled by alpha over r, to give the output h. r numbers x alpha / r x d numbers W (frozen) d x d A r x d B d x r + h output Figure 1. The frozen weight W does the main work. A small adapter, A (d to r) then B (r to d), glowing, adds a learned correction scaled by alpha / r. Only A and B are trained.

The low-rank update

A layer normally computes . LoRA adds a trainable update :

is and frozen. is and is , where the rankIn LoRA, the inner dimension r of the two adapter matrices. It caps how complex the learned weight update can be; common values range from 4 to 64. is small, typically 4 to 64. For and , the full matrix has entries, while and together have : 128 times fewer.

Why should a low-rank update be enough? Fine-tuning adapts a model that already knows a lot; the change needed for a new task or style seems to live in a small number of directions. LoRA bets on that, and in practice it usually holds.

Initialisation and alpha

starts at zero and at small random values, so and training starts from exactly the pretrained model. The scaling factor alphaA LoRA hyperparameter that scales the adapter's output by alpha / r, controlling how strongly the adapter changes the layer. divided by sets how strongly the adapter speaks. Many people set to or and tune the learning rate instead.

Which matrices to adapt

The original paper adapted the attention query and value projections. Today it is common to adapt all attention projections and often the MLP matrices too, which helps on harder tasks at a modest cost in parameters.

QLoRA: a 4-bit base

The frozen weights do not need to be updated, so they do not need high precision for training to work. QLoRA stores them in a 4-bit format and dequantises them on the fly during the forward and backward passes, while the adapters train in 16-bit. Memory for the base drops by about 4 times compared with 16-bit, which is what makes fine-tuning very large models on one GPU possible ( Quantization for inference covers quantization).

Merging and serving adapters

After training you can merge the adapter into the weights, , and serve a normal model with no extra latency. Or keep adapters separate: an adapter is a small file (megabytes rather than gigabytes), so one base model in GPU memory can serve many fine-tuned variants, swapping or batching adapters per request. This is how platforms host many customer fine-tunes cheaply.

Try it

This interactive needs JavaScript. The diagrams and text above cover the same idea.

Try this:

  1. At the defaults (a 7-billion-parameter model, hidden size 4,096, rank 16, 4 matrices in each of 32 layers), LoRA trains 16.8M parameters, 0.24 percent of the model. Memory for weights and optimizer state drops from about 104 GiB for full fine-tuning to 13.3 GiB, and to 3.5 GiB with QLoRA.
  2. Raise the rank to 64. Trainable parameters quadruple to 67.1M, yet LoRA memory only grows from 13.3 to 14.0 GiB: the frozen base dominates. That is why QLoRA attacks the base.
  3. Set hidden size 8,192, 80 layers and a 70-billion-parameter model. Full fine-tuning needs over 1,000 GiB; QLoRA needs about 34 GiB, within reach of a single large GPU once activations are added.

The calculator treats every adapted matrix as (MLP matrices are larger, so it slightly undercounts) and uses the usual mixed-precision Adam cost of 16 bytes per trained parameter. Activation memory, which grows with batch size and sequence length, is left out.

LoRA on one matrix in NumPy:

lora.pyruns with python3 + numpy
# LoRA on one weight matrix: learn a low-rank update B @ A, keep W0 frozen.
import numpy as np

rng = np.random.default_rng(0)
d, n = 64, 500
W0 = rng.normal(size=(d, d)) / np.sqrt(d)            # "pretrained" weights
U, V = rng.normal(size=(d, 4)), rng.normal(size=(4, d))
W_task = W0 + 0.3 * U @ V / np.sqrt(d)               # the task needs a rank-4 change
X = rng.normal(size=(n, d))
Y = X @ W_task.T + 0.01 * rng.normal(size=(n, d))    # input -> desired output

def train_lora(r, alpha=8, steps=1000, lr=0.02):
    A = rng.normal(size=(r, d)) * 0.01               # small random
    B = np.zeros((d, r))                             # zero: starts as W0 exactly
    s = alpha / r                                    # LoRA scaling
    for _ in range(steps):
        err = X @ (W0 + s * B @ A).T - Y             # forward with the adapter
        G = err.T @ X / n                            # dLoss/dW (only used via A, B)
        B, A = B - lr * s * G @ A.T, A - lr * s * B.T @ G
    return B, A, s

base_err = np.mean((X @ W0.T - Y) ** 2)
print(f"frozen W0 only:  error {base_err:.4f}   trainable 0")
print(f"full fine-tune:  trainable {d * d:,}")
for r in (1, 4, 8):
    B, A, s = train_lora(r)
    err = np.mean((X @ (W0 + s * B @ A).T - Y) ** 2)
    print(f"LoRA rank {r}:     error {err:.4f}   trainable {B.size + A.size:,}")

# Merging: fold the adapter into the weights. Same outputs, no extra latency.
W_merged = W0 + s * B @ A
x = X[:3]
same = np.allclose(x @ W_merged.T, x @ W0.T + s * (x @ A.T) @ B.T)
print("merged weights give identical outputs:", same)

Output

frozen W0 only:  error 0.3804   trainable 0
full fine-tune:  trainable 4,096
LoRA rank 1:     error 0.2196   trainable 128
LoRA rank 4:     error 0.0001   trainable 512
LoRA rank 8:     error 0.0001   trainable 1,024
merged weights give identical outputs: True

The task needs a rank-4 change to the frozen matrix. A rank-1 adapter cannot express it and the error only falls from 0.380 to 0.220. Rank 4 drives it to the noise floor (0.0001) with 512 trainable numbers instead of 4,096, and rank 8 adds nothing more. The last line confirms that merging the adapter gives exactly the same outputs.

Going deeper

Choosing the rank

Higher rank gives the adapter more capacity but more parameters and more risk of overfitting small datasets. For style, format and narrow tasks, ranks 8 to 16 are a common start. If quality plateaus below full fine-tuning, try adapting more matrices before cranking the rank.

Learning rates and limits

LoRA usually needs a higher learning rate than full fine-tuning. It also tends to forget less of the base model's abilities, because the base is frozen. The flip side: for tasks that need a lot of new knowledge or a big shift in behaviour, such as continued pretraining on a new language, full fine-tuning can still win.

Other parameter-efficient methods

LoRA is the most widely used PEFTParameter-efficient fine-tuning: methods that adapt a large model by training only a small number of new or selected parameters. method, but not the only one. Prefix and prompt tuning learn a few virtual tokens prepended to the input; adapter layers insert small trainable blocks between layers (and add some latency). Variants of LoRA change how the update is parameterised or initialised.

Trade-offs

OptionWins whenLoses when
Full fine-tuning Maximum capacity for large shifts in behaviour or knowledge Huge memory; a full model copy per variant; more forgetting
LoRA Tiny trainable fraction; small adapter files; mergeable with no latency Capacity limited by rank; still needs the 16-bit base in memory
QLoRA Fits very large models on one GPU Slower steps from dequantisation; slight quality risk from 4-bit base
Prompting only No training at all; instant iteration Limited consistency for format or style; long prompts cost tokens

Common mistakes

  • Forgetting activation memory. The calculator's numbers are a floor; long sequences and big batches add a lot.
  • Using the learning rate from full fine-tuning and concluding LoRA does not work.
  • Expecting LoRA to inject large amounts of new factual knowledge. Use retrieval for facts ( Prompting vs RAG vs fine-tuning: choosing ).
  • Merging an adapter trained on a quantised base into a 16-bit base without checking quality.
  • Picking a huge rank by default. Start small, measure, and adapt more matrices first.

In the wild

Hu et al., 2021 (paper)

LoRA: Low-Rank Adaptation of Large Language Models (opens in a new tab)

As models grow, full fine-tuning that retrains all parameters becomes less feasible. LoRA freezes the pretrained weights and injects trainable rank decomposition matrices into each transformer layer, reducing trainable parameters by up to 10,000 times compared with fully fine-tuning GPT-3 175B. It performed on par with or better than fine-tuning on RoBERTa, DeBERTa, GPT-2 and GPT-3, with higher training throughput and no additional inference latency.

Dettmers et al., 2023 (paper)

QLoRA: Efficient Finetuning of Quantized LLMs (opens in a new tab)

QLoRA backpropagates through a frozen 4-bit quantized model into LoRA adapters, which made it possible to fine-tune a 65 billion parameter model on a single 48 GB GPU while preserving 16-bit fine-tuning performance. It introduced a 4-bit NormalFloat data type, double quantization and paged optimizers to manage memory spikes. The authors fine-tuned over 1,000 models and found that small, high-quality datasets gave state-of-the-art results.

Check yourself

  1. A 2,048 x 2,048 weight matrix gets a rank-8 LoRA adapter. How many trainable parameters does the adapter add?

    Show answer

    B. 32,768 A is 8 x 2,048 and B is 2,048 x 8: 2 x 2,048 x 8 = 32,768, versus 4,194,304 in the full matrix.

  2. Why is B initialised to zero?

    Show answer

    B. So B times A is zero and training starts exactly from the pretrained model With B = 0 the adapter adds nothing at first, so the model starts unchanged and drifts only as training requires.

  3. What does QLoRA change compared with LoRA?

    Show answer

    B. It stores the frozen base model in 4-bit precision The base is frozen, so it can be stored in 4-bit and dequantised on the fly; adapters still train in higher precision.

  4. You need 50 customer-specific fine-tunes of the same base model. Why is LoRA attractive?

    Show answer

    B. Adapters are small, so one base model in memory can serve many variants by swapping adapters Storing and loading megabyte-sized adapters is far cheaper than 50 full copies of the weights.

Recap

  • LoRA freezes the base model and learns a low-rank update B A added to selected weight matrices.
  • Trainable parameters drop to a fraction of a percent; rank r and alpha control capacity and strength.
  • QLoRA stores the frozen base in 4-bit, cutting the dominant memory cost about four times.
  • Adapters can be merged for zero extra latency, or kept separate so one base serves many variants.
  • LoRA is great for behaviour, format and narrow skills; large knowledge shifts may still need full fine-tuning.

Get new lessons by email

Get new posts on backend and AI internals. No spam, unsubscribe anytime.

Your email is only used to send new posts. Privacy