LoRA Under the Hood โ€” Why 7 Billion Weights Fit on Your GPU

Lesson 3: LoRA Under the Hood โ€” Why 7 Billion Weights Fit on Your GPU

You cannot reason about memory budgets, rank choices, or why your 8GB card can fine-tune a 7B model without understanding what LoRA actually does to a weight matrix. Fortunately the maths is one equation.

First, why full fine-tuning is so brutal

Training a model means storing more than the model. For a 7B-parameter model in half precision (bf16, 2 bytes per number), a full fine-tune needs:

weights (bf16) 7e9 ร— 2 bytes = 14 GB
gradients (bf16) 7e9 ร— 2 bytes = 14 GB
Adam optimizer state 7e9 ร— 2 tensors ร— 4 bytes = 56 GB
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
static footprint โ‰ˆ 84 GB
plus activations, buffers, fragmentation + 10โ€“40 GB

Adam keeps two fp32 numbers (a running mean and a running variance) for every single trainable parameter. That line item alone is four times the size of the weights themselves. This is why a 7B full fine-tune is conventionally run across eight 80GB A100s with sharding (ZeRO/FSDP) โ€” or, on newer hardware, a few H100s. The optimiser state, not the model, is what forces the cluster.

The key insight: almost all of that cost is proportional to the number of trainable parameters. Make only 0.6% of the model trainable and the gradient and optimiser line items shrink by ~160ร—, while the frozen weights stay at a comfortable 14 GB.

What LoRA actually does

For a targeted weight matrix Wโ‚€ of shape d ร— k (frozen), LoRA adds a trainable low-rank update:

W = Wโ‚€ + ฮ”W = Wโ‚€ + (ฮฑ / r) ยท BยทA

Wโ‚€ : d ร— k, frozen โ€” never updated, stored in bf16 or 4-bit NF4
A : r ร— k, trainable โ€” initialised randomly (Gaussian)
B : d ร— r, trainable โ€” initialised to zero, so ฮ”W starts at exactly 0
r : the rank โ€” the inner dimension you choose (8, 16, 32, 64โ€ฆ)
ฮฑ/r : the scaling factor that keeps update magnitude stable as r changes

B initialised to zero matters more than it looks: at step 0 the model's behaviour is exactly the base model's. Training only ever departs from a known-good starting point, which is why LoRA is so much more forgiving than full fine-tuning.

LoRA DECOMPOSITION Wโ‚€ d ร— k frozen ยท 4-bit or bf16 ~0% trainable Wโ‚€ + B d ร— r trainable ยท A r ร— k trainable = W d ร— k base + ฮ”W merged at export A big frozen matrix plus two small trainable ones. With r = 16 and d = k = 4096: 16,777,216 frozen params โ†’ 131,072 trained (0.78%)

The trainable-parameter maths, concretely

For a d ร— k matrix, LoRA trains r ร— (d + k) parameters instead of d ร— k. Take Llama-class 7B (hidden size 4096, MLP intermediate 11008, 32 layers) targeting all seven linear projections per layer at r = 16:

ModuleBase shapeBase paramsLoRA params (r=16)
q_proj / k_proj / v_proj / o_proj4096 ร— 409616,777,216 each131,072 each
gate_proj / up_proj / down_proj4096 ร— 1100845,088,768 each241,664 each
Per layer (7 modules)โ€”202,389,5041,249,280
ร— 32 layersโ€”6,476,464,12839,976,960 โ‰ˆ 40M

That's ~0.6% of the model trainable. Note how the MLP projections dominate โ€” which is exactly why attention-only LoRA recipes lose quality: adapting just q_proj and v_proj touches about 8% of the parameters that full-rank adaptation of all linear layers would.

The optimiser-state consequence: 40M trainable parameters means about 0.32 GB of Adam state, not 56 GB. Gradients for those parameters: ~0.08 GB. The frozen 6.5B weights carry no gradients and no optimiser state at all. This is why the memory cliff disappears.

QLoRA: the 4-bit trick that made this mainstream

LoRA still needs the frozen weights resident, and at bf16 a 7B base is 14 GB. QLoRA (Dettmers et al., 2023) adds three things:

IngredientWhat it doesEffect
NF4 quantisationStores frozen weights in 4-bit NormalFloat โ€” a data type whose levels are placed at the quantiles of a normal distribution, which fits normally-distributed weights better than plain 4-bit integers.14 GB โ†’ ~3.5 GB for a 7B base
Double quantisationQuantises the quantisation constants themselves.Saves a further ~0.4 GB per billion params
Paged optimisersPages optimiser state to CPU memory on spikes instead of OOM-ing.Tolerates longer sequences during a batch

Because the frozen base is 4-bit and only the adapters are trained in bf16, gradients flow through the dequantised forward pass while the stored weights stay compressed. That is how a 7B model trains inside a single 16 GB free-tier GPU, and how the QLoRA paper demonstrated a 65B model on one 48 GB card.

Pick your acronym by VRAM, not by hype:
LoRA (bf16 base) โ€” best quality, needs ~2ร— the base model in VRAM before activations.
QLoRA (4-bit base) โ€” ~4ร— less VRAM, ~1% quality cost, some step-time cost. The default when hardware is the constraint.
Full fine-tune โ€” only when you have the cluster and need the last few percent.

Target modules, and the honest default

# The classic 2023 recipe โ€” adapt attention only
target_modules = ["q_proj", "v_proj"]

# The modern default โ€” adapt every linear layer (attention + MLP)
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
                  "gate_proj", "up_proj", "down_proj"]

# In practice you rarely enumerate them: both Unsloth and PEFT accept
# a wildcard and figure it out per architecture.
target_modules = "all-linear"

"all-linear" is what you should reach for. It roughly triples the trainable parameters versus q,v only while adding a fraction of a percent to the total โ€” and the quality gap is consistently worth it for behaviour-changing tasks.

๐Ÿช™ Token angle: the rank choice is a cost decision, not just a quality one. Trainable parameters set your optimiser memory and your merged-artefact size and (mildly) your step time. Going from r=16 to r=64 quadruples the adapter and its optimiser state for a gain you often can't measure. Start at r=16, and only climb if your eval set says the model is underfitting โ€” a distinction Lesson 9 teaches you to diagnose properly.

๐Ÿ“บ Watch:

๐Ÿง  Knowledge Check

1. Why does a full fine-tune of a 7B model need ~84 GB of non-activation memory, while QLoRA needs a small fraction of that?

2. In W = Wโ‚€ + (ฮฑ/r)ยทBยทA, why is B initialised to zero?

3. You double the LoRA rank from 16 to 32 but keep alpha at 16. What happens to the effective update scale?

Further Reading