LoRA and its descendants: LoRA+, QLoRA, VeRA, DoRA
LoRA freezes a pretrained model and learns a small low-rank correction to each weight matrix, so fine-tuning touches a fraction of a percent of the parameters. This guide explains LoRA from the original paper, then the four follow-ups the video covers: different learning rates for the two matrices, a 4-bit frozen backbone, shared random matrices, and separate magnitude and direction.
Start here (no maths needed)
Teaching a huge AI a new skill normally means changing all of its billions of numbers. LoRA leaves those numbers alone and adds a tiny "correction" next to them. The correction is so small that anyone with one good graphics card can teach a big model something new.
Think of it like this
You have a thick printed encyclopaedia, and you want it to cover your local football club. Reprinting every page would be silly. Instead, you stick a few sticky notes on the right pages. The book stays exactly as it was, the notes are cheap, and you can peel them off and stick on a different set for your cousin's chess club. LoRA's corrections are those sticky notes: small, removable and swappable, while the big book stays untouched.
How it works
- Freeze the big model: none of its numbers will change.
- Next to each big table of numbers, add two thin tables. Multiplied together, they make a correction the same size as the big table, but they hold far fewer numbers.
- Start the correction at zero, so at first the model behaves exactly as before.
- Train only the thin tables on your new examples.
- When you're done, you can add the correction into the big table, so using the model is no slower than before.
Why robots care
Robot "brains" built from vision-language models are big. Each new robot, gripper or kitchen needs a bit of extra teaching. With LoRA, a lab can teach one shared model many robots by keeping one small set of sticky notes per robot, and train each set on a single graphics card.
Try it
- Draw a 6×6 grid and fill it using a rule: cell value = (row number) × (column number). You only need 12 numbers (6 rows and 6 columns) to make all 36. That's a "rank 1" table, the same trick LoRA uses.
- In the demo further down, move Rank r and watch the LoRA line rise while full fine-tuning stays flat. Then change Model size and compare the Full fine-tune, LoRA and QLoRA memory.
Words to know
- Fine-tuning
- Training an already-trained model a little more on new examples.
- Frozen
- Kept fixed during training; its numbers don't change.
- Rank
- Roughly, how many independent patterns a table of numbers contains. Low rank means it can be built from a few patterns.
- Adapter
- A small trainable add-on attached to a frozen model.
- Quantisation
- Storing numbers with fewer bits, to save memory.
Going deeper: everything below is the full version, with some maths. On a first read, skim the headings and the "Why it matters for robotics" box, and come back for the rest later.
1. Watch
Chapter guide
Timestamps come from the video's description. The right-hand column is this course's reading guide, written from the chapter titles; it doesn't describe what the video shows frame by frame.
| Time | Chapter | What to look for |
|---|---|---|
| 0:00 | Why Fine-Tuning Huge Models Is Expensive | The memory bill of full fine-tuning (weights, gradients, optimiser states) and the cost of storing one full copy per task. |
| 1:32 | LoRA: Low-Rank Adaptation | The update ΔW = BA, the shapes of A and B, and why the merged model has no extra inference cost. The description also mentions reading LoRA as a compact key-value memory; see section 2. |
| 6:34 | LoRA Initialization | Which matrix starts at zero and which starts random, and what would go wrong with the other choices. |
| 9:34 | LoRA+: Different Learning Rates for A and B | Why one learning rate for both matrices is a poor fit when the model is wide. |
| 10:21 | QLoRA: Quantizing the Frozen Backbone | NF4, blockwise quantisation and double quantisation. Count the bits per parameter at each step. |
| 16:23 | VeRA: Fewer Trainable Adapter Parameters | Random matrices shared by all layers, with only scaling vectors trained. |
| 18:26 | DoRA: Separating Magnitude and Direction | Splitting each weight column into a length and a direction, and applying LoRA only to the direction. |
| 21:23 | Outro | Try the self-check below. |
2. The ideas, explained
Why full fine-tuning is expensive
Full fine-tuning with mixed-precision Adam needs about 16 bytes per parameter for weights, gradients and optimiser states (see Video 01), so a 7B model needs about 112 GB before activations. It also produces a full-size copy of the model for every task. The LoRA paper's motivating example is GPT-3 175B, where keeping one fine-tuned 175B copy per task is impractical.
LoRA: a low-rank update
Hu et al. (2021) hypothesised that the change a model needs during adaptation has low "intrinsic rank". So for a frozen weight W₀ of shape d × k, they learn the update as a product of two thin matrices:
h = W₀·x + ΔW·x = W₀·x + (α / r)·B·A·x
B: d × r, A: r × k, r ≪ min(d, k)
trainable per matrix: r·(d + k) instead of d·k
d = k = 4096, r = 16: 131,072 instead of 16,777,216 (0.78%)
after training: W = W₀ + (α/r)·B·A → merge, no extra inference latency
Only A and B get gradients and optimiser states, so the 12 bytes per parameter of Adam state apply to a tiny set. The paper applied LoRA to the attention projections and reported, against GPT-3 175B fine-tuned with Adam, about 10,000 times fewer trainable parameters and 3 times less GPU memory, with quality on par with or better than full fine-tuning on the models tested. Unlike adapter layers inserted in series, a merged LoRA adds no inference latency. Keeping adapters unmerged has its own benefit: one base model can serve many adapters, swapping them per request.
A key-value reading. Write A's rows as a₁…a_r and B's columns as b₁…b_r. Then B·A·x = Σᵢ (aᵢ · x) bᵢ: each rank-one component checks how well the input matches a "key" aᵢ and adds the matching "value" bᵢ. Seen this way, LoRA is a small learned memory of r key-value pairs added to each layer, which is a useful intuition for what rank buys: more pairs, more distinct corrections.
Initialisation
LoRA initialises A with random Gaussian values and B with zeros, so BA = 0 and training starts from exactly the pretrained model. Both can't be zero: with B = 0 and A = 0, the gradient of each with respect to the loss is zero (each gradient is multiplied by the other matrix), and nothing would ever train. With A random and B zero, B's gradient is non-zero from the first step, and A starts receiving gradients once B moves. The α/r scaling means that when you change r, you don't have to retune the learning rate as much.
LoRA+: different learning rates for A and B
Hayou, Ghosh and Yu (2024) analysed LoRA as the model width grows and showed that using the same learning rate for A and B doesn't allow efficient feature learning in wide models. Their fix is one line: give B a larger learning rate than A, with a fixed ratio.
η_B = λ · η_A, λ > 1 (a well-chosen ratio; the paper tunes it)
The paper reports 1–2% better performance and up to about 2× faster fine-tuning at the same compute cost as LoRA.
QLoRA: a 4-bit frozen backbone
Since the backbone is frozen, it never needs a gradient update, only a forward and backward pass through it. Dettmers et al. (2023) store it in 4 bits and backpropagate through the dequantised weights into BF16 LoRA adapters. Three ideas make it work:
- NF4 (4-bit NormalFloat). Pretrained weights are roughly normally distributed. NF4 places its 16 levels at quantiles of a normal distribution, so each level is used about equally often. The paper calls it information-theoretically optimal for normally distributed weights.
- Blockwise quantisation. Weights are split into blocks of 64, and each block is scaled by its own absolute maximum, so one outlier only spoils its own block.
- Double quantisation. Those per-block scales are themselves quantised (to 8 bits, in blocks of 256), cutting their overhead.
quantise a block: c = max|w|, q = NF4_index(w / c)
dequantise: w ≈ c · NF4_value[q]
overhead of block scales, per parameter:
plain: 32 bits / 64 = 0.5 bits
double: 8/64 + 32/(64·256) ≈ 0.127 bits
backbone ≈ (4 + 0.127) / 8 ≈ 0.52 bytes per parameter
A third trick, paged optimisers, uses unified memory to move optimiser states to CPU memory during spikes so a long sequence doesn't crash the run. The paper reports fine-tuning a 65B model on a single 48 GB GPU while preserving full 16-bit fine-tuning task performance. How 4-bit and 8-bit formats behave at inference time is covered in Module 09 · Quantisation & hardware.
VeRA: shared random matrices
Kopiczko, Blankevoort and Asano (2023) noticed that even LoRA adapters add up when you store one per user or task. VeRA uses a single pair of random low-rank matrices A and B, frozen and shared across all layers, and learns only two small scaling vectors per layer:
ΔW = Λ_b · B · Λ_d · A Λ_b = diag(b), b ∈ ℝ^d Λ_d = diag(d), d ∈ ℝ^r
trainable per layer: d + r (vs r·(d + k) for LoRA)
Because the trainable cost grows with r only through the short vector, VeRA can afford a much higher rank than LoRA. The random matrices can be regenerated from a seed, so they don't need storing. The paper reports performance matching LoRA with far fewer trainable parameters on GLUE, E2E, image classification and instruction tuning of 7B and 13B models.
DoRA: magnitude and direction
Liu et al. (2024) compared how full fine-tuning and LoRA change each weight column's length (magnitude) and direction, and found different patterns. DoRA makes the two separately trainable:
W = m · (W₀ + B·A) / ‖W₀ + B·A‖_c
m ∈ ℝ^(1×k): one trainable magnitude per column, initialised to ‖W₀‖_c
‖·‖_c: the norm of each column
trainable per layer: k + r·(d + k)
LoRA updates the direction; the magnitude vector is cheap. After training, everything can be merged back into one matrix, so there's no extra inference cost. The paper reports DoRA consistently outperforming LoRA when fine-tuning LLaMA, LLaVA and VL-BART on commonsense reasoning, visual instruction tuning and image/video-text tasks.
Interactive: trainable parameters and fine-tuning memory
Top: trainable parameters for one d × k weight matrix as the rank grows (log scales). Dashed: full fine-tuning. Solid accent: LoRA. Dotted: DoRA (LoRA plus one magnitude per column). Solid muted: VeRA (two scaling vectors). The marker is your rank. Below: model-state memory for a whole model, assuming every weight matrix has this shape ratio and gets an adapter.
3. Where the course goes deeper
| Topic | Course page |
|---|---|
| LoRA in practice for preference tuning (DPO, KTO, ORPO) | Module 08 · Alignment & preference |
| Quantisation formats (FP8, INT8, INT4, NVFP4) and hardware | Module 09 · Quantisation & hardware |
| Memory per parameter, ZeRO and FSDP (the alternative to LoRA when you need full fine-tuning) | Video 01 · ZeRO & FSDP, Module 08 · Distributed training |
| Post-training pipeline: SFT, preference tuning, RL | Module 07 · Post-training |
| Making small models for deployment by distillation | Module 04 · Distilling small models |
4. Why it matters for robotics
- Adapting a VLA to a new robot. The OpenVLA paper (Kim et al., 2024) shows its model can be fine-tuned on consumer GPUs with low-rank adaptation and served with quantisation without hurting downstream success rate. That's the standard path for a lab with a new arm and a few hundred demonstrations.
- One base model, many embodiments. Unmerged adapters let one backbone serve several robots or tasks, each with its own small adapter file, instead of a full copy each.
- QLoRA lowers the hardware bar. A 4-bit backbone means a 7B-class VLA can be fine-tuned on a single workstation GPU, which matters for university labs and hobbyists.
- Limits: a new robot often needs new input or output layers (a different action space, new sensors), which LoRA on existing matrices doesn't cover; those layers are usually trained in full. And when the embodiment differs a lot from the pretraining data, low rank can be too restrictive; check full fine-tuning as a baseline.
Self-check
How many trainable parameters does LoRA add to a 4096 × 11008 matrix at rank 8?
r·(d + k) = 8 × (4096 + 11008) = 120,832, compared with 45,088,768 for the full matrix (about 0.27%).
Why is B initialised to zero and A to random values, rather than both to zero?
B = 0 makes the update zero at the start, so training begins from the pretrained model. If both were zero, each matrix's gradient would be multiplied by the other (zero) matrix and neither would ever move.
Why does a merged LoRA model have no extra inference latency, unlike serial adapter layers?
After training, W₀ + (α/r)BA is a single matrix of the original shape, so the forward pass is unchanged. Serial adapters add extra layers that must run in sequence.
What do NF4 and double quantisation each save in QLoRA?
NF4 stores each frozen weight in 4 bits with levels matched to a normal distribution. Double quantisation compresses the per-block scale factors, cutting their overhead from 0.5 to about 0.127 bits per parameter.
VeRA learns only d + r parameters per layer. What makes that possible?
The low-rank matrices A and B are random, frozen and shared across all layers (and can be regenerated from a seed). Only two scaling vectors per layer are trained.
What does DoRA's magnitude vector do that LoRA can't easily do?
It lets each column's length change independently of its direction. LoRA ties the two together in one additive update; DoRA trains the magnitude directly and uses LoRA for the direction.
References
The video's description lists no references. Primary papers cited in this guide:
- LoRA: Low-Rank Adaptation of Large Language Models (Hu et al., 2021)
- LoRA+: Efficient Low Rank Adaptation of Large Models (Hayou, Ghosh, Yu, 2024)
- QLoRA: Efficient Finetuning of Quantized LLMs (Dettmers et al., 2023)
- VeRA: Vector-based Random Matrix Adaptation (Kopiczko, Blankevoort, Asano, 2023)
- DoRA: Weight-Decomposed Low-Rank Adaptation (Liu et al., 2024)
- OpenVLA: An Open-Source Vision-Language-Action Model (Kim et al., 2024)