Parameter-Efficient Fine-Tuning: LoRA

Full fine-tuning updates every weight in a model — billions of numbers — which needs enormous memory and produces a full-size copy per task. LoRA sidesteps all of it with one insight: the change a model needs for a task is "low-rank," so you can train a tiny pair of matrices instead of the whole model. It's the technique that put fine-tuning within reach of anyone with a single GPU.

The last post placed supervised fine-tuning at the center of applied work. This post covers the technique that made SFT practical for most people: LoRA (Low-Rank Adaptation), the leading form of parameter-efficient fine-tuning (PEFT). Before LoRA, fine-tuning a large model meant the resources to update all its weights; after LoRA, you can fine-tune a big model on modest hardware and ship tiny per-task adapters. Understanding why it works — and why it barely costs quality — is essential to modern fine-tuning.

The problem with full fine-tuning

Full fine-tuning updates all of a model’s weights during training. For a large model this is brutally expensive in a specific way — memory:

This put fine-tuning out of reach for most teams and made multi-task deployment (a different fine-tune per customer or task) prohibitively expensive. PEFT exists to break this — to fine-tune by training only a small number of new parameters while leaving the original model frozen.

The LoRA insight: updates are low-rank

LoRA rests on an elegant observation. When you fine-tune a model, the change to its weight matrices — the difference between the fine-tuned and original weights — turns out to have low intrinsic rank. In plain terms: the adjustment a task requires is far simpler (has far less independent information) than the full weight matrix it modifies. You don’t need to change the weights in billions of independent ways; the useful change lives in a small subspace.

That means you can approximate the weight update with a product of two much smaller matrices. Instead of learning a full update matrix ΔW (huge), you learn two thin matrices A and B whose product BA approximates it:

Full fine-tuning:   W_new = W + ΔW          (ΔW is full-size, all trained)

LoRA:               W_new = W + B·A          (W frozen; only A and B trained)
                    where A and B are small (low "rank" r):
                    if W is d×d, A is r×d and B is d×r, with r ≪ d

The rank r is small (often just a handful to a few dozen), so A and B together have orders of magnitude fewer parameters than W. You freeze the original weights entirely and train only these tiny matrices. That’s the whole idea — and it works remarkably well because the update genuinely is low-rank.

Why LoRA is a big deal

The consequences of training two small matrices instead of the whole model are dramatic:

The knobs: rank and where to apply it

LoRA has a couple of choices that shape the trade-off:

The practical guidance: start with a modest rank and the common target matrices (the defaults in libraries like Hugging Face PEFT are well-chosen), and only increase rank if the task needs more capacity. Most tasks don’t need large ranks — the low-rank insight is that a little goes a long way.

LoRA in practice

Using LoRA is straightforward with modern tooling (Hugging Face PEFT and similar):

LoRA is the reason fine-tuning went from “needs a cluster” to “runs on one GPU,” and its adapter model is why serving many fine-tuned variants became cheap. But there’s still the memory of the base model itself to hold during training — and the next technique, QLoRA, shrinks that too, pushing fine-tuning of even large models onto a single consumer GPU.

Key takeaways

Further reading

Sources & References

Parameter-efficient fine-tuning library