LoRA Adapter - PyTorch Basics
Implement LoRA Adapter in Python with NumPy. hard exercise with examples, hints, and instant feedback. No setup or signup required.
LoRA Adapter
What you'll build: lora_forward(x, W, A, B, alpha, r), the forward pass of a linear layer with a frozen weight W plus a trainable low-rank update A @ B.
Background
Fine-tuning every weight of a large model is expensive in memory: Adam stores two extra tensors per parameter. LoRA (Hu et al., 2021) freezes the pretrained W and learns a low-rank correction ΔW = (alpha / r) * A @ B with a small rank r. For a 4096x4096 layer and r = 8 that is 2 * 4096 * 8 = 65,536 trainable parameters instead of 16,777,216, about 99.6% fewer. After training, ΔW can be merged into W, so inference costs nothing extra.
Formula
output = x @ W + (alpha / r) * (x @ A) @ B
| Symbol | Shape | Role |
|---|---|---|
x | (batch, in_features) | input |
W | (in_features, out_features) | frozen pretrained weight |
A | (in_features, r) | down-projection (trainable, random init) |
B | (r, out_features) | up-projection (trainable, zero init, so training starts exactly at the pretrained model) |
alpha / r | scalar | scaling, so changing r doesn't require retuning the learning rate |
Expected Return Format
An array of shape (batch, out_features).
Edge Cases and Conventions
- Compute
(x @ A) @ B, notx @ (A @ B): the result is the same, but the first costsO(batch * r * (in + out))while formingA @ BcostsO(in * out * r)and a full-size matrix. alpha = 0gives the base outputx @ W.
Constraints
- Plain matrix products; no loops.
- PyTorch track: the hidden tests check that, with
Wfrozen, autograd gives gradients toAandBonly.
Follow-up
- Which weight matrices of a transformer do people usually adapt with LoRA, and why?
- QLoRA stores
Win 4 bits. Which parts still need full precision?
Examples
Input: lora_forward(np.array([[1.0, 2.0]]), np.eye(2), np.array([[1.0], [1.0]]), np.array([[1.0, 0.0]]), alpha=2, r=1) Output: array([[7., 2.]])
Base x @ W = [1, 2]. Low-rank path: x @ A = [3], times B = [3, 0], times alpha / r = 2 gives [6, 0]. Sum: [7, 2].
Input: x (2, 768), W (768, 768), A (768, 8), B (8, 768), alpha=16, r=8 Output: shape (2, 768)
Same output shape as the frozen layer; only 12,288 of the parameters are trainable.
Practice in Python
import numpy as np
def lora_forward(x, W, A, B, alpha, r):
"""
LoRA forward pass: x @ W + (alpha / r) * (x @ A) @ B.
Args:
x: Input (batch, in_features)
W: Frozen weights (in_features, out_features)
A: Down-projection (in_features, r)
B: Up-projection (r, out_features)
alpha: Scaling factor
r: Rank
Returns:
Output (batch, out_features)
"""
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 6 tests, or use the progressive hints.