Part I, Optimisation, generalisation, adaptation
A small change to a large matrix
Fine-tuning a pretrained model rarely needs to move every weight. LoRA trains a low-rank correction instead, and loses surprisingly little.
Assumes matrix rank and the singular value decomposition; training a linear layer by gradient descent.
A pretrained language model already knows most of what it needs. Adapting it to a task, such as a style, a domain or a format, usually means nudging its weights, not relearning them. Yet full fine-tuning updates every one of those weights, keeps optimiser state for each, and leaves you with a complete new copy of the model for every task.
Low-rank adaptation, or LoRA, starts from a bet about the shape of that nudge.Hu et al., LoRA: Low-Rank Adaptation of Large Language Models, 2021. The bet builds on earlier evidence that fine-tuning has a low “intrinsic dimension” (Aghajanyan et al., 2020).
The bet
Take one weight matrix in the network, , say a projection inside an attention block. Fine-tuning replaces it with . The bet is that has low rank: most of what it does lies along a handful of directions.
So, rather than learn freely, LoRA writes it as a product of two thin matrices and learns those:
is frozen. Only and receive gradients. A rank- product has entries instead of . For a projection at , that is 65,536 trainable weights in place of 16.8 million, about 0.4%.
How much is lost
How much of the update survives depends on how quickly its singular values fall off. The best any rank- matrix can do is to keep the largest singular directions and drop the rest. This is the Eckart–Young theorem,Precisely: among all matrices of rank at most , the truncated SVD minimises in both the Frobenius and spectral norms. and it is the ceiling a trained can approach but never exceed.
The figure builds an update with a spectrum you control, then shows the best rank- approximation of it.
At the starting settings, rank 4 already captures about nine-tenths of the update, because its spectrum falls quickly. Now slow the fall-off. The update spreads across many directions, the kept fraction drops, and the approximation on the top right loses its structure. LoRA works when the task is close to what the model already does. A task that requires broad change is a poor fit for a low-rank update.
A rising noise floor has a subtler effect. The tail it adds is real energy that rank cannot reach. But in a trained model, much of that tail is noise in the gradient estimates, and leaving it out can act as regularisation.
Details that matter in practice
Initialisation. starts random and starts at zero, so and training begins exactly at the pretrained model.Why not start both at zero? See the second exercise.
The α/r factor. When you change , the size of at a given learning rate changes with it. Dividing by means a learning rate tuned at one rank roughly carries over to another. Later work argues for when is large.Kalajdzievski, 2023, “rank-stabilised” LoRA.
Merging. After training, is an ordinary matrix. Fold it in and inference costs exactly what it did before. Keep it separate instead, and one base model can serve many tasks by swapping small adapters.
Where to put it. The original paper adapted only the query and value projections of attention. Current practice usually adapts every linear layer, at a modest rank.
A minimal layer in PyTorch:
class LoRALinear(nn.Module):
def __init__(self, base: nn.Linear, r=8, alpha=16):
super().__init__()
self.base = base.requires_grad_(False) # W0, frozen
self.A = nn.Parameter(torch.randn(r, base.in_features) / r**0.5)
self.B = nn.Parameter(torch.zeros(base.out_features, r))
self.scale = alpha / r
def forward(self, x):
return self.base(x) + self.scale * (x @ self.A.T @ self.B.T)
The same idea combines with a quantised base model. Storing in four bits and training in higher precision is QLoRA, which makes fine-tuning very large models possible on a single GPU.Dettmers et al., 2023.
A model has 32 layers, each with four attention projections. Count the trainable parameters for LoRA at on all of them, and compare with full fine-tuning of the same matrices. With Adam, roughly how much optimiser state does each need?
Solution
LoRA: M. Full: B. Adam keeps two extra values per trained weight, so about 34M against 4.3B, a factor of 128 fewer.
Suppose and both start at zero. Compute and at initialisation, and explain why training never begins.
Solution
With , and , where . If and , both gradients are zero, and they stay zero. Making random breaks the symmetry. Making zero keeps the starting model unchanged.
In the figure, set the fall-off to its slowest and the noise floor to zero. Find the smallest rank that captures 90% of the update. What does this suggest about choosing for a task far from pretraining?