The Open Model EcosystemLesson 52 of 71

Contents

The Open Model Ecosystem

Lesson 52 of 71. Sign in to keep your place.

Module 6 · Adapting a model to your work

What a LoRA actually is

9 min read

The same tune, two budgetsFull fine-tuning an 8BEvery weight updatedGradients and optimiser state for 8 billionAround 110 GB of training memoryA 16 GB copy of the model per variantSeveral datacentre GPUsLoRA at rank 16, all linear layersBase frozen, and B starts at zeroAbout 40 million trainable, half a per centFits on a free notebook GPUA 40 to 200 MB adapter per variantKeep fifty of themOne 4096 by 4096 projection is 16.8 million parameters fully tuned, or 131,072 at rank 16. Print thetrainable percentage on every run: if it is far from what you expected, the target modules are wrongand the next three hours will produce nothing.
One 4096 by 4096 projection is 16.8 million parameters fully tuned, or 131,072 at rank 16. Print the trainable percentage on every run: if it is far from what you expected, the target modules are wrong and the next three hours will produce nothing.

Figure

The trick, in one equation

Full fine-tuning updates every weight matrix W by some ΔW of the same shape. LoRA observes that the useful part of ΔW is empirically low-rank, and represents it as a product of two thin matrices:

Text
W' = W + (alpha / r) * B @ A

W : d x k   (frozen, never updated)
B : d x r   (initialised to zeros)
A : r x k   (initialised randomly)
r : the rank, typically 8 to 64

The base weights never move. Only A and B are trained. Because B starts at zero, the model begins identical to the base and departs from it gradually, which is why LoRA training is stable.

The arithmetic that makes it cheap

Take one 4096 × 4096 projection. Full tuning updates 16.8 million parameters. At rank 16, LoRA trains 4096 × 16 plus 16 × 4096 = 131,072 parameters, which is 0.8% of the matrix.

Across a whole 8B model, targeting all the linear layers at rank 16 gives roughly 20 to 40 million trainable parameters, half a percent of the model. Those are the only parameters needing gradients and optimiser state, which is where the memory saving comes from, and it is why a fine-tune that would need several datacentre GPUs fits on a free notebook.

The saved adapter is 40 to 200 MB, against 16 GB for a full copy. You can keep fifty of them.

The three settings that matter

Rank r. The capacity of the update.

  • 8 to 16: format, tone, style, output structure. Most tasks are here.
  • 32 to 64: a new task, a domain with its own vocabulary, a substantial behavioural shift.
  • 128 and above: approaching full fine-tuning in capacity, and usually a sign that continued pretraining is the real requirement.

Higher rank is not safer. It has more capacity to overfit a small dataset and it forgets more.

Alpha. A scaling factor: the update is multiplied by alpha / r. The convention alpha = 2 * r is a reasonable default. What matters is that alpha and rank move together: raising rank while keeping alpha fixed quietly reduces the effective strength of your update, which is a common cause of a tune that seems to do nothing.

Target modules. Which matrices get adapters. The original paper adapted only the query and value projections. Later practice found that targeting all linear layers (query, key, value, output, and the three feed-forward matrices) works measurably better for the same parameter budget, because most of the model’s parameters are in the feed-forward stack.

Python
from peft import LoraConfig, get_peft_model
cfg = LoraConfig(
    r=16, lora_alpha=32, lora_dropout=0.05,
    target_modules=["q_proj","k_proj","v_proj","o_proj",
                    "gate_proj","up_proj","down_proj"],
    task_type="CAUSAL_LM",
)
model = get_peft_model(base, cfg)
model.print_trainable_parameters()   # trainable: 41,943,040 || all: 8,072,204,288 || 0.52%

Print that last line every time. If the trainable percentage is far from what you expected, your target modules are wrong, and the run will produce nothing useful over several hours.

Merging, or not

An adapter can be folded into the base weights permanently:

Python
merged = model.merge_and_unload()      # W += (alpha/r) B A
merged.save_pretrained("./my-model")

Merged models load anywhere and need no adapter support. Unmerged adapters stay tiny, can be swapped at request time, and let one base serve many tunes. The choice belongs to the shipping lesson at the end of this block; for now, keep the adapter, because you can always merge later and cannot easily unmerge.

Why it works at all

The honest answer is empirical. The observation is that fine-tuning updates for a specific task have low intrinsic rank: the model is being nudged within a subspace it already occupies rather than being taught something structurally new. This is consistent with the other findings in this block: LoRA is excellent at form and weak at knowledge, because form is a nudge and knowledge is not.

Where LoRA underperforms full tuning is precisely where you would predict: large amounts of new material, a new language, a new modality. For those, the answer is not a higher rank; it is continued pretraining, several lessons from here.

Every lesson here is open. Your progress is recorded once you pass the Maths You Actually Need exam.

Check your understanding

A team raises LoRA rank from 16 to 64 to give the adapter more capacity, leaving lora_alpha at 32. The tuned model changes less than before rather than more. Why?

Pick the one you would defend. Your answer stays private.

Next lesson · 9 minFitting a fine-tune in 16 GB

Stuck on something?Ask Anjali, your AI tutor

How to cite: Addaly (2026) ‘What a LoRA actually is’, in The Open Model Ecosystem. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/open-models/lora-mechanics (Accessed: 13 October 2026).