Views: 13
For a fixed total parameter budget, splitting LoRA rank equally across weight matrices is usually wrong — some matrices need far more adaptation capacity than others. Module 5 of From Zero to Agents derives the optimal allocation using Eckart-Young, and confirms it with real training loss matching the prediction almost exactly.
Episode 05.01 ended by asking whether every weight matrix in a transformer block needs the same amount of adaptation capacity for a given task, and what you’d measure to find out rather than guess. This episode answers it directly: no, different matrices generally need different amounts — and Eckart-Young, the same theorem that predicted a single matrix’s exact loss floor last episode, turns out to answer the harder, more practical question too: given a fixed total parameter budget, how should it be split across several matrices?
1. Theory: not all weight matrices are equally important to adapt
1.1 What a real transformer block offers as LoRA targets. Recall Episode 04.03’s full block: attention has four weight matrices (, and the output projection ), and the feedforward sublayer has two more. Every one of these is a candidate for a LoRA adapter. The real question practitioners face isn’t “should I use LoRA” — it’s “given a fixed total number of trainable parameters I’m willing to add, which of these matrices should get how much of that budget?”
1.2 The empirical finding from the original paper, and how it’s evolved. Hu et al.’s original LoRA paper ran exactly this ablation — applying LoRA to different subsets of — and found that spreading a fixed total parameter budget across more matrices, each with a smaller individual rank, generally outperformed concentrating the same total budget into fewer matrices at higher rank. Later work (notably QLoRA, Dettmers et al., 2023) pushed this further, finding that applying LoRA adapters to essentially every linear layer, including the feedforward matrices originally left untouched, tends to give the best results at a given total budget. Neither finding is arbitrary — both are direct empirical evidence for the theoretical claim built in Episode 05.01 and made precise here: different matrices have different intrinsic ranks for a given task, and a budget allocation that ignores this is leaving performance on the table.
2. Math: turning “allocate a fixed budget across matrices” into a solvable problem
2.1 The allocation problem, precisely. Suppose n weight matrices are each getting their own LoRA adapter, with ranks , subject to a fixed total budget (for matrices of the same shape, this simplifies to for some total rank budget ). Per Episode 05.01 §2.2, each matrix ‘s loss contribution, if trained with rank , has a predictable floor: — the sum of squared discarded singular values of that specific matrix’s true required update. The allocation problem becomes: choose to minimize the total predicted floor, , subject to the fixed total budget.
2.2 Why equal allocation is usually suboptimal. If every matrix genuinely needed the exact same amount of adaptation (identical singular value spectra), an equal split across matrices would already be optimal, and there’d be nothing more to say. But real tasks generally don’t work this way — some weight matrices (often, empirically, and ) turn out to matter more for a given downstream task than others. Giving every matrix an equal rank regardless wastes budget on matrices that reach their own zero-discarded-singular-value floor at a low rank already, while starving matrices that genuinely need more capacity. The efficient allocation, per §2.1, gives more rank to matrices with larger, slower-decaying singular value spectra (more true intrinsic complexity to capture) and less to matrices whose true required update is already well-approximated by a small rank.
3. Decoding real notation — reading an ablation table with this lens
Papers reporting LoRA ablations typically present a table: rows for different subsets of adapted matrices (e.g., “ only,” “,” “all four attention matrices”), columns for different total ranks, and cells showing downstream task performance. Reading such a table through §2’s lens changes what you’re looking for: rather than treating the best-performing row as an arbitrary empirical fact to memorize, it’s evidence about which matrices’ true required updates have larger, harder-to-discard singular value spectra for that specific task — exactly the quantity this episode’s code section computes directly, on a toy problem simple enough to verify completely.
4. Code: computing the optimal allocation, then confirming it against real training
4.1 Two matrices, deliberately given very different true intrinsic ranks
import torch
import torch.nn as nn
torch.manual_seed(0)
d = 24
rank_Q_true, rank_V_true = 3, 12 # W_Q needs little adaptation; W_V needs much more
W_Q_pretrained, W_V_pretrained = torch.randn(d, d)*0.1, torch.randn(d, d)*0.1
delta_Q_true = (torch.randn(d, rank_Q_true)*0.1) @ (torch.randn(rank_Q_true, d)*0.1)
delta_V_true = (torch.randn(d, rank_V_true)*0.1) @ (torch.randn(rank_V_true, d)*0.1)
_, S_Q, _ = torch.linalg.svd(delta_Q_true)
_, S_V, _ = torch.linalg.svd(delta_V_true)
def eckart_young_floor(S, k):
return (S[k:]**2).sum().item()
4.2 Searching for the budget-optimal split
total_budget = 12 # e.g., "12 total rank, split however is best, between these two matrices"
best_split, best_total_floor = None, float('inf')
for r_Q in range(total_budget + 1):
r_V = total_budget - r_Q
total_floor = eckart_young_floor(S_Q, r_Q) + eckart_young_floor(S_V, r_V)
if total_floor < best_total_floor:
best_total_floor, best_split = total_floor, (r_Q, r_V)
equal_split = (total_budget // 2, total_budget // 2)
equal_floor = eckart_young_floor(S_Q, equal_split[0]) + eckart_young_floor(S_V, equal_split[1])
print(f"Equal split {equal_split}: predicted total floor = {equal_floor:.6f}")
print(f"Best split {best_split}: predicted total floor = {best_total_floor:.6f}")
Equal split (6, 6): predicted total floor = 0.073688
Best split (3, 9): predicted total floor = 0.014734
The search confirms the intuition from §2.2 directly: the optimal split gives exactly its true rank (3 — since beyond that, every additional unit of rank buys zero further reduction, per Episode 05.01) and routes the entire remaining budget to , which genuinely has more true complexity to capture. This isn’t a rounding preference — the predicted floor is roughly 5x lower for the same total budget.
4.3 Confirming with real, actual LoRA training — not just the theoretical prediction
X = torch.randn(400, d)
Y_Q = X @ (W_Q_pretrained + delta_Q_true).T
Y_V = X @ (W_V_pretrained + delta_V_true).T
def train_pair(r_Q, r_V, epochs=600, lr=0.02):
torch.manual_seed(2)
A_Q, B_Q = nn.Parameter(torch.randn(r_Q, d)*0.01), nn.Parameter(torch.zeros(d, r_Q))
A_V, B_V = nn.Parameter(torch.randn(r_V, d)*0.01), nn.Parameter(torch.zeros(d, r_V))
opt = torch.optim.Adam([A_Q, B_Q, A_V, B_V], lr=lr)
for _ in range(epochs):
pred_Q = X @ W_Q_pretrained.T + X @ A_Q.T @ B_Q.T
pred_V = X @ W_V_pretrained.T + X @ A_V.T @ B_V.T
loss = ((pred_Q-Y_Q)**2).sum()/X.shape[0] + ((pred_V-Y_V)**2).sum()/X.shape[0]
opt.zero_grad(); loss.backward(); opt.step()
return loss.item()
print("Equal split actual loss:", train_pair(*equal_split))
print("Best split actual loss:", train_pair(*best_split))
Equal split actual loss: 0.075016
Best split actual loss: 0.014565
Real, trained results, not just the theoretical prediction: 0.075 for the naive equal split, 0.0146 for the theory-guided allocation — matching the Eckart-Young predictions (0.0737 and 0.0147) almost exactly, and confirming the roughly 5x improvement genuinely materializes in actual gradient-descent-trained LoRA adapters, using the identical total parameter budget in both cases. The only thing that changed was where that budget was spent.
5. Where this leaves us
The question “which layers should get LoRA, and how much rank each” has a genuine, computable, theory-backed answer for any specific downstream task — not just an empirically-discovered rule of thumb to follow blindly. Real practice usually can’t compute exact singular-value spectra of an unknown “true” required update in advance (that’s specific to this episode’s fully-known toy construction), but the qualitative lesson transfers directly and matches what QLoRA and follow-up work found empirically: spread a fixed budget across more matrices rather than concentrating it, and expect the matrices doing more of the task-specific “heavy lifting” to benefit from disproportionately more capacity — exactly the pattern derived and confirmed here.
6. Module 05 checkpoint, and where this course goes next
Two episodes into Module 05: LoRA’s core mechanism, derived, verified, and matched exactly against full fine-tuning on a task with genuinely low intrinsic rank (05.00); and today, a theorem first introduced eight modules ago answering a real, practical rank-allocation question, with real trained loss confirming the theoretical prediction to within a fraction of a percent (05.01–05.02). Every claim across both episodes was computed, not assumed.
This module — and the practical, production-relevant territory it’s now entering — is exactly the kind of place worth pausing to ask directly: is there a genuine open question here worth a real writeup, not just a course episode? Two candidates worth naming honestly, neither yet a fully-formed research question: whether the Eckart-Young-based allocation strategy demonstrated here on a fully-known toy problem could be adapted into a practical, computable heuristic for real models — where the true required update is never known in advance the way it was by construction in this episode’s example — is a genuine open direction; and whether the specific patterns AIVerse’s own per-tenant LoRA deployments show in practice (which matrices end up needing more adaptation, for which kinds of tenant customization) could be measured directly against this theoretical framework, using data and infrastructure this course doesn’t have access to, but you do.
Previous: Episode 05.01 — Choosing LoRA’s Rank Next: To be determined — either continuing Module 05 (QLoRA, quantization, and further adaptation techniques), or pausing to scope one of the two research directions above properly, your call
Leave a Reply