Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

~12 min readICLR 2022

LoRA,
explained.

To teach a giant model a new skill, the old way meant retraining every one of its billions of weights — a data-center-sized job you had to repeat for every task. This 2021 paper found you can freeze the whole giant, bolt on two tiny matrices, and train only those. Same quality, a fraction of the cost, and zero slowdown at inference.

The delta

What changes against full fine-tuning: almost nothing is trained

Rungs 1 to 3 all end in a gradient step on every parameter, which means the cost of adapting a model scales with the model. What actually makes that expensive is not the weights — it is the optimiser state, since Adam keeps two running averages per trainable parameter, so a full fine-tune needs roughly three times the model's memory before activations. Adapting a large model therefore needs the hardware that trained it.

The change rests on an observation about what a fine-tune actually does: the update to a weight matrix has much lower rank than the matrix itself — adaptation moves the model within a small subspace. So do not represent the update as a full matrix. Freeze the pretrained weights and learn the update as a product of two thin matrices, one projecting down to a small rank and one back up. Only those two are trained, so the optimiser state shrinks with the rank rather than the model.

Stated in units: trainable parameters fall by up to four orders of magnitude, and with them the optimiser memory and the size of what you have to store per task. Two consequences matter as much as the saving. First, because the learned update is a product that can be added into the frozen weights before serving, there is no extra inference latency at all — which is what distinguishes this from adapter layers that insert extra depth into the network and pay for it on every token forever. Second, many task adapters can share one copy of the base model in memory, so a fleet serving a hundred fine-tunes needs one model and a hundred small deltas.

01

The cost of full fine-tuning

Fine-tuning is how you adapt a pretrained model to your task — your tone, your domain, your format. The classic recipe updates every weight in the model. For a model with billions of parameters, that means holding a full copy in GPU memory, optimizing all of it, and saving a fresh multi-gigabyte model per task.

Want ten specialized variants? Ten full copies. It's like rebuilding an entire skyscraper from the foundation up every time you want to repaint one room. Prohibitive for anyone without a data center — and wasteful even for those who have one.

02

The insight: adaptation is low-rank

The authors started from a hunch that turned out to be the whole paper: the change a model needs to learn a new task is far simpler than the model itself. Formally, the weight update has low intrinsic rank — it lives in a tiny subspace, not the full billion-dimensional space.

If the update is that simple, you don't need to represent it with a giant dense matrix. You can capture it with something much smaller — and that's the opening LoRA walks through.

03

How it works: freeze, then decompose

Take a weight matrix W in the pretrained model. LoRA freezes it — never touches it again — and adds a side path: two small matrices, A and B, whose product represents the update. The layer now computes W·x + B·A·x, and only A and B are trained.

The LoRA side path
Wfrozen · d×d + B · Atrained · rank r = adapted output

The trick is the shape. If W is d×d, then A is r×d and B is d×r, where the rank r is tiny — often just 4 or 8. Their product is the same size as W, but they contain a few thousand times fewer numbers to train.

04

The math, worked with real numbers

Watch a rank-1 update describe a whole matrix. Say training produced these two tiny factors for a 4-dimensional layer (B starts as zeros — so LoRA begins as a no-op — and learns; A starts random):

Look at the rows of ΔW: they're 1×, 2×, 0×, and 1× the same row A. Eight stored numbers just described a sixteen-number update — that's what rank 1 means. The matrix can only push inputs toward one direction, but the paper's bet is that fine-tuning barely needs more than a few such directions. The bet pays off brutally well at real widths (d = 4096, one layer):

UpdateTrainable numbersvs full ΔW
Full ΔW (4096×4096)16,777,216100%
LoRA r = 18,1920.05%
LoRA r = 432,7680.20%
LoRA r = 865,5360.39%
LoRA r = 64524,2883.13%

The forward pass adds the side path, scaled by α/r so different ranks train with similar learning-rate behavior:

Applied to GPT-3 175B (rank 4, attention Wq and Wv only), this cut trainable parameters 10,000× and the per-task checkpoint from 350 GB to 35 MB.

05

The best part: no inference tax

Earlier efficient-tuning methods bolted extra layers into the network, which made every prediction a little slower forever. LoRA sidesteps this entirely. After training, you can merge the small update back into the original matrix — W' = W + B·A — producing a model that is byte-for-byte the same size and speed as the original.

Worth knowing

This is LoRA's quiet superpower: all the training savings, none of the inference cost. The deployed model is indistinguishable in size and latency from a fully fine-tuned one.

How it works

How LoRA is set up and trained, step by step

LoRA changes which parameters are trained; once the update is merged, a request runs through exactly the original computation. The recipe below is the one the paper used on RoBERTa, DeBERTa, GPT-2 and, at the largest scale, GPT-3 175B.

adapted in the GPT-3 runs Wq Wk Wv Wo MLP: frozen x W₀ (frozen) d × k, no gradient A: r × k (random) B: d × r (zeros) B·A·x α/r W₀·x + h only A and B are trained serve: W = W₀ + (α/r)·B·A
The frozen path and the trainable side path. Only A and B receive gradients; merging (α/r)·BA into W₀ removes the side path at serving time.
  1. Choose which matrices to adapt. Each Transformer layer has four attention projections, Wq, Wk, Wv and Wo, and two MLP matrices. The paper adapts attention weights only and freezes the MLP blocks, LayerNorm and biases, for simplicity and parameter efficiency, leaving the rest to future work. The headline GPT-3 runs adapt Wq and Wv.
  2. Attach two thin factors. For a frozen W0 of shape d × k, add B (d × r) and A (r × k), with r much smaller than d and k. A starts from a random Gaussian and B from zeros, so ΔW = BA is exactly zero at step 0 and training begins from the pretrained model's own behavior.
  3. Scale the side path by α/r. The layer computes h = W0x + (α/r)·BAx. With Adam, tuning α is roughly the same as tuning the learning rate, so the authors set α to the first r they tried and never tuned it. Dividing by r reduces the need to re-tune hyperparameters when you change the rank.
  4. Train only A and B. Gradients still flow backward through W0 to reach earlier layers, but no gradient is kept for W0 itself and the optimizer stores no state for it. On GPT-3 175B this cut training memory from 1.2 TB to 350 GB and gave about a 25% training speedup over full fine-tuning, because most weight gradients are never computed.
  5. Spend a fixed budget across matrices. At 18M trainable parameters on GPT-3, about 35 MB in fp16, the budget buys rank 8 on one matrix type, rank 4 on two or rank 2 on all four, in all 96 layers. Spreading it over Wq and Wv, or over all four, beat concentrating it in one (results below).
  6. Deploy by merging, or keep adapters apart. After training, W = W0 + (α/r)·BA is computed once and served like the original model, with no added latency. Switching tasks means subtracting one BA and adding another. The paper names the catch: once merged, one batch cannot easily mix requests for different tasks; keeping adapters unmerged allows per-request adapters at some cost in speed.

What the learned update looks like

The authors probed ΔW directly. Adapters trained at r = 8 and r = 64 on the same task share their top singular direction strongly, with normalized subspace similarity above 0.5 for both Wq and Wv, while the other directions of the rank-64 adapter look mostly like noise: evidence that the useful update is genuinely very low rank. ΔW also correlates with W0 more than a random matrix would, but it amplifies directions that W0 does not emphasize, and by a large factor, about 21.5 for r = 4 in the layer they examined.

Later work revisited two defaults. Rank-stabilized LoRA (rsLoRA) argued that α/r shrinks updates too much at large ranks and proposed α/√r instead, and DoRA splits each weight into a magnitude and a direction and applies the low-rank update to the direction. Serving systems such as S-LoRA and Punica keep thousands of adapters unmerged and batch requests across them with custom kernels, which works around the batching limit.

The math

The LoRA forward pass and its parameter count

h = W0x + ΔWx = W0x + (α / r) · BAx, B ∈ ℝ^d×r, A ∈ ℝ^r×k, r ≪ min(d, k)
trainable parameters per adapted matrix = r · (d + k), against d · k for full fine-tuning
  • W0 — the pretrained weight, frozen; it receives no gradient update and no optimizer state.
  • A — the down-projection to r dimensions, Gaussian-initialized; B — the up-projection back to d, zero-initialized, so ΔW starts at exactly zero.
  • r — the rank: the number of independent directions the update can use.
  • α / r — a fixed scale. α was set equal to the first r tried, so at that rank the scale is exactly 1.

For GPT-3, with d = k = 12,288 and rank 4 on Wq and Wv in all 96 layers: 2 · 96 · 4 · 24,576 ≈ 18.9M trainable parameters, about 0.01% of 175B. At the extremes, r = min(d, k) lets BA represent any update, so LoRA becomes as expressive as fully fine-tuning that matrix, while r = 1 confines the update to a single direction. On Wq plus Wv, rank 1 was already within half a point of rank 8 on both WikiSQL and MultiNLI.

The evidence

What LoRA was tested on, and what it showed

The paper compared LoRA with full fine-tuning, BitFit (bias-only tuning), prefix-embedding and prefix-layer tuning, and adapter layers, on RoBERTa and DeBERTa (GLUE), GPT-2 (E2E NLG and other table-to-text tasks) and GPT-3 175B (WikiSQL, MultiNLI-matched and SAMSum). The GPT-3 results carry the argument:

GPT-3 175B methodTrainable parametersWikiSQL acc. (%)MNLI-m acc. (%)
Full fine-tuning175,255.8M73.889.5
BitFit14.2M71.391.0
Prefix-layer tuning20.2M70.189.5
Adapter layers40.1M73.291.5
LoRA4.7M73.491.7
LoRA37.7M74.091.6

The which-matrices ablation fixed the budget at 18M parameters and moved it around:

AdaptedRankWikiSQLMultiNLI
Wq870.491.0
Wk870.090.8
Wv873.091.0
Wo873.291.3
Wq, Wk471.491.3
Wq, Wv473.791.3
all four273.791.7

Putting the whole budget into Wq or Wk was clearly worse; Wq and Wv at rank 4, or all four at rank 2, matched or beat every single matrix at rank 8. More rank did not help either: on Wq and Wv, rank 64 scored 73.5 on WikiSQL against 73.8 at rank 8 and 73.4 at rank 1. The weak results belong to the baselines. Prefix-embedding tuning reached only 63.1 on WikiSQL, and on GPT-2 medium at batch size 1, two adapter designs added 20.7% and 30.3% latency, the cost LoRA's merge avoids.

Since then

What the authors admit, and what happened next

The paper is clear that the rank is chosen empirically — there is no principle that tells you what it should be — and that surprisingly small ranks suffice, which is evidence for the low-rank premise and also a result nobody predicted the size of. It also reports adapting only the attention projections rather than every matrix, again on measurement rather than derivation, and does not claim parity with full fine-tuning in all cases.

The limitation the field settled on later is a sharpening of exactly that premise. Low-rank updates are excellent at teaching a model a style, format or task interface, and comparatively weak at installing substantial new knowledge — which follows from the hypothesis rather than contradicting it: if adaptation is genuinely confined to a small subspace, then anything requiring a large change is out of reach by construction. The practical rule that emerged — reach for this to change behaviour, reach for continued pretraining to change what the model knows — is a consequence of the paper being right, not wrong.

How it held up: it is the default way anyone fine-tunes anything. The multiple-adapters-one-base pattern became a serving architecture, the variants that followed mostly adjust how the rank is allocated across layers, and the next rung removes the one cost this one leaves standing — that the frozen base still has to fit in memory at full precision.

SOLVE IT YOURSELF

LoRA in 25 lines: decompose, adapt, merge

The whole method, running in your browser through WebAssembly: build ΔW = B·A from the worked example, run the layer with the side path, then merge the update into W and prove the merged layer gives byte-identical outputs — the "no inference tax" claim, verified live. Change B, A, or the rank and the equivalence must keep holding.

YOUR TASK

Write the body of merged_forward. Everything else in the file is already written; this is the function the paper is about. Press ▶ Run to check it against the 5 assertions below — they start red.

WHAT IT HAS TO SATISFY — 5 ASSERTIONS
  1. BA matches the worked example
  2. Merged layer == side-path layer
  3. Zero B means zero change
  4. The rank-1 update B.A has the full weight shape (4x4)
  5. A zero A factor also makes LoRA a perfect no-op
CPython · WebAssembly
06

One base model, many hats

Because each task's LoRA is just a few megabytes of A and B, you can keep one frozen base model and a shelf of tiny adapters — one for legal writing, one for code, one for your brand voice — and swap them in and out on the fly. Ten specialized models become one big model plus ten postage-stamp files, instead of ten skyscrapers.

08

Why it still matters

LoRA turned fine-tuning from a big-lab luxury into something a solo developer can do on a rented GPU — and it became the default of the whole parameter-efficient fine-tuning (PEFT) family. Its descendant QLoRA quantizes the frozen base to 4-bit and trains adapters on top, letting you fine-tune enormous models on a single consumer card.

Every "custom fine-tune," every marketplace of swappable model adapters, every hobbyist tuning a model at home traces back to one observation in this paper: the update you need is small, so train something small.

Questions

Questions people ask about LoRA

How should I set LoRA rank and alpha?

Start small. In the LoRA paper, ranks 1 to 8 on the query and value projections of GPT-3 175B performed as well as rank 64, and the top direction learned at rank 8 was largely the same one learned at rank 64. The adapter's output is multiplied by alpha divided by rank; the authors set alpha equal to the first rank they tried and never tuned it, since with Adam alpha acts much like the learning rate. A common practice today is alpha equal to rank or twice rank. If you raise the rank a lot, consider rank-stabilized LoRA, which scales by alpha over the square root of rank, because alpha over rank shrinks large-rank updates too much.

Which modules should LoRA target?

The original paper adapted only attention projections and froze the MLP blocks, LayerNorm and biases. At a fixed budget on GPT-3 175B, adapting the query and value projections at rank 4 scored 73.7 on WikiSQL, while spending the same budget on the query projection alone at rank 8 scored 70.4, and on the key projection alone 70.0, so spreading rank across matrices beat concentrating it. Later work pushed further: the QLoRA paper found that adapters on every linear layer, including the MLP, were needed to match full 16-bit fine-tuning on large instruction-tuning runs. Many practitioners now target all linear layers when quality matters more than adapter size.

Why is LoRA's B matrix initialized to zero?

So that training starts exactly at the pretrained model. LoRA adds the product of two matrices, B times A, to each frozen weight. A is drawn from a random Gaussian so gradients have somewhere to flow, and B starts at zero, which makes the product, and therefore the change to the model, exactly zero at step zero. The first forward pass reproduces the base model, and the update grows from nothing as B learns. If both were random, the model would begin with a random perturbation of every adapted layer, damaging what pretraining learned before any task signal arrived. Both at zero would not work either, because then neither matrix would receive a gradient.

Can one batch serve requests for different LoRA adapters?

Not if the adapters are merged. Merging computes the frozen weight plus the scaled product BA once, which removes all extra inference cost but bakes one task into the weights, and the LoRA paper itself notes that inputs for different tasks then cannot easily share a forward pass. The alternative is to keep the base shared and apply each request's A and B on the side, which costs a little extra compute per token. Serving systems such as S-LoRA and Punica do this at scale, keeping thousands of adapters available and batching requests that use different ones with custom GPU kernels, so a single copy of the base model can serve many fine-tunes at once.

Does LoRA match full fine-tuning?

On the paper's benchmarks, yes or better. On GPT-3 175B, LoRA with 4.7 million trainable parameters scored 73.4 on WikiSQL and 91.7 on MultiNLI, against 73.8 and 89.5 for full fine-tuning of all 175 billion, and a 37.7 million parameter version scored 74.0 and 91.6. Those are adaptation tasks: new formats and task interfaces. Later studies found limits when the goal is new knowledge or a large shift in capability. A 2024 study titled LoRA Learns Less and Forgets Less found full fine-tuning clearly ahead when training on large amounts of new code and math data, while LoRA preserved more of the base model's abilities outside the target domain.

How are LoRA, rsLoRA and DoRA different?

All three freeze the pretrained weights and learn a low-rank update. LoRA, from this paper, scales the update B times A by alpha divided by rank. Rank-stabilized LoRA, or rsLoRA, changes only that scale, to alpha divided by the square root of rank, arguing that the original factor shrinks updates so much at high ranks that larger adapters stop learning efficiently. DoRA, weight-decomposed low-rank adaptation, splits each pretrained weight into a magnitude and a direction, trains the magnitude directly and applies the low-rank update to the direction, which its authors report brings results closer to full fine-tuning. All three can be merged into the base weights after training.

Check yourself

4 questions to test what you just read

Work each one out before you open it — a number, a prediction or a reason, not a definition. Producing the answer is what makes it stick. Your progress is kept on this device.

A 4096 × 4096 projection gets a rank-16 LoRA adapter. How many trainable numbers is that, and what fraction of the full matrix?

Show answer

r · (d + k) = 16 · (4096 + 4096) = 131,072, against 4096² = 16,777,216 for the full matrix: about 0.78%. The count is linear in rank, so rank 8 would be 65,536, or 0.39%.

At an 18M-parameter budget on GPT-3, Wq alone at rank 8 scored 70.4 on WikiSQL, while Wq and Wv at rank 4 scored 73.7. What does that say about how to spend a fixed budget?

Show answer

Cover more matrices before adding rank. The extra rank on Wq bought almost nothing (Wq alone stays near 70 from rank 4 to rank 64), while touching a second matrix added 3.3 points. Each matrix needs only a few directions, so a fixed budget is better spent on more of them.

You double the rank from 8 to 16 and keep alpha at 16. What happens to the alpha/r multiplier, and why was the multiplier there in the first place?

Show answer

It halves, from 2 to 1. Adam takes steps of roughly fixed size per parameter, so a higher-rank product BA can change faster; dividing by r offsets that, letting a learning rate tuned at one rank roughly carry over to another. rsLoRA later argued the offset should be √r, because dividing by r over-corrects at large ranks.

Compared with the base model, what does a merged LoRA model cost at inference, and what does an unmerged adapter cost?

Show answer

Merged: nothing extra. W0 + (α/r)·BA is one matrix of the original shape, so latency and memory match the base model. Unmerged: two extra thin matrix multiplies per adapted layer, about r·(d + k) multiply-adds per token per matrix, small but nonzero, in exchange for mixing adapters in one batch.

Where this sits

Teaching a base model to follow

A pre-trained model completes text; it does not take instructions. Nine papers on getting from one to the other, from RLHF to a loss with no reward model in it.

  1. 1InstructGPT2022
  2. 2Constitutional AI2022
  3. 3Self-Instruct2023
  4. 4LoRA2022
  5. 5QLoRA2023
  6. 6Direct Preference Optimization2023
  7. 7GRPO2024
  8. 8STaR2022
  9. 9Let's Verify Step by Step2023

All 110 papers, in 19 ladders →

LoRA: Low-Rank Adaptation of Large Language Models · Hu, Shen, Wallis, Allen-Zhu, Li, Wang, Wang, Chen · ICLR 2022 · read the original paper on arXiv → · Vibe Engines · 2026
Finished this one? 0 / 110 Paper Breakdowns done
NextQLoRA Next on the “Teaching a base model to follow” ladder · rung 5 of 9

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More Paper Breakdowns