LoRA Trainable Parameter Savings
Problem Statement
Given the shapes of the weight matrices you intend to adapt, a LoRA rank and a layer count, report how many parameters LoRA trains, how many full fine-tuning would train, and what percentage that is.
Background
LoRA freezes a pretrained weight W of shape (d_out, d_in) and learns a low-rank update instead:
W′=W+rα​BA,B∈Rdout​×r,A∈Rr×din​
So the trainable count per adapted matrix is
r⋅din​+r⋅dout​=r(din​+dout​)
against d_out * d_in for full fine-tuning. The scaling factor alpha/r is a constant applied at forward time - it adds no parameters, and neither A nor B carries a bias.
The saving is dramatic because the ratio is r(d_in + d_out) / (d_in * d_out). For a 4096x4096 projection at r = 8 that is 8 * 8192 / 16777216, about 0.39%. Multiply by the number of transformer layers you target and by how many matrices per layer you adapt (typically q_proj and v_proj) and the whole adapter for a 7B model still fits in a few megabytes - which is what makes it possible to fine-tune a VLM on a single consumer GPU and to serve dozens of task adapters over one shared base model.
Your Task
Implement:
def lora_stats(layer_shapes, rank, num_layers=1):
layer_shapes is a list of [d_out, d_in] pairs listing the matrices adapted inside one layer; that set repeats num_layers times.
Return [lora_params, full_params, percent] where the first two are ints and percent is 100 * lora_params / full_params rounded to 4 decimals.
Input Format
- layer_shapes - list of [d_out, d_in] integer pairs
- rank - positive integer r
- num_layers - positive integer
Output Format
[int, int, float]
Sample
print(lora_stats([[4096, 4096], [4096, 4096]], 8, 32))
Output:
[4194304, 1073741824, 0.3906]
Example:
print(lora_stats([[4096, 4096], [4096, 4096]], 8, 32))
[4194304, 1073741824, 0.3906]
Each 4096x4096 matrix costs 8*(4096+4096) = 65536 LoRA parameters against 16777216 full ones. Two matrices per layer over 32 layers gives 4194304 versus 1073741824 - 0.3906%.
Constraints:
- LoRA adds
r * (d_in + d_out)parameters per adapted matrix - no bias terms - The
alpha / rscaling factor contributes NO parameters full_paramscounts only the adapted matrices, not the whole model- Both counts are multiplied by
num_layers - Round
percentto 4 decimals; return the counts as plain Python ints
1. Background Knowledge
Low-Rank Adaptation (LoRA) is a parameter-efficient fine-tuning technique designed to reduce the memory and compute requirements of adapting large pretrained models. Instead of updating all weights in a model, LoRA freezes the original pretrained weight matrix W and injects trainable low-rank decomposition matrices. The update is formulated as W′=W+rα​BA, where B and A are low-rank matrices. This approach assumes that the update to the weights during fine-tuning has an "intrinsic low rank," meaning the changes can be captured by a small number of dimensions.
The core mathematical insight is that a full rank update to a matrix of shape (dout​,din​) requires dout​×din​ parameters. In contrast, the LoRA update uses two matrices: A of shape (r,din​) and B of shape (dout​,r). The total number of trainable parameters for this single matrix becomes r⋅din​+r⋅dout​. Since r is typically much smaller than din​ and dout​ (e.g., r=8 vs d=4096), the parameter count drops dramatically. The scaling factor α/r is applied during inference or training but does not add trainable parameters.
In Vision-Language Models (VLMs) and Large Language Models (LLMs), this technique is applied to specific linear layers, such as query and value projections in attention mechanisms. By repeating this pattern across multiple layers, the total adapter size remains small (often just a few megabytes) compared to the billions of parameters in the base model. This allows for efficient storage of multiple task-specific adapters on a single GPU without reloading the entire base model weights.
2. Algorithm Approach
The problem requires calculating three values: the total number of trainable parameters in the LoRA adapter, the total number of parameters if full fine-tuning were used, and the percentage of parameters saved.
The approach is purely arithmetic based on the dimensions provided:
- Iterate through the list of matrix shapes provided for a single layer.
- Calculate the LoRA parameter count for each matrix using the formula r(din​+dout​).
- Calculate the full fine-tuning parameter count for each matrix using the formula din​×dout​.
- Aggregate these counts by multiplying by the number of layers (num_layers).
- Compute the percentage as 100×Full ParamsLoRA Params​.
This is a straightforward accumulation pattern where you sum up contributions from each component (each matrix in each layer) to get the global totals.
3. Step-by-Step Strategy
- Initialize Accumulators: Create variables total_lora_params and total_full_params initialized to 0. These will store the cumulative counts.
- Loop Through Shapes: Iterate over each [d_out, d_in] pair in the layer_shapes list.
- Extract d_out and d_in from the pair.
- Calculate the LoRA parameters for this specific matrix: lora_count = rank * (d_in + d_out).
- Calculate the full parameters for this specific matrix: full_count = d_out * d_in.
- Add lora_count to total_lora_params and full_count to total_full_params.
- Scale by Layers: Since the layer_shapes list represents the matrices adapted inside one layer, and this pattern repeats for num_layers, multiply both total_lora_params and total_full_params by num_layers.
- Calculate Percentage: Compute the percentage using the formula:
- Round the Result: Round the percent value to 4 decimal places as required.
- Return Results: Return a list containing [total_lora_params, total_full_params, percent]. Ensure the first two are integers and the last is a float.
4. Common Pitfalls
- Confusing Dimensions: Ensure you correctly identify d_out and d_in from the input list [d_out, d_in]. Swapping them in the LoRA formula r(din​+dout​) yields the same result due to commutativity, but swapping them in the full fine-tuning formula dout​×din​ also yields the same result. However, conceptually, it is important to track which is which.
- Forgetting num_layers: The input layer_shapes defines the matrices per layer. A common mistake is calculating the stats for just one layer and forgetting to multiply by num_layers.
- Rounding Errors: Use Python's round() function with ndigits=4 to ensure the percentage is formatted correctly. Floating-point arithmetic can sometimes lead to tiny precision errors, but round() handles the final output requirement.
- Integer vs. Float Division: In Python 3, division / always returns a float. Ensure that the final percentage is a float, but the parameter counts remain integers.
- Alpha Parameter: Do not include the scaling factor α in the parameter count. It is a hyperparameter applied during computation, not a trainable weight.
5. Time & Space Complexity
- Time Complexity: O(N), where N is the number of matrix shapes in layer_shapes. The algorithm iterates through the list once, performing constant-time arithmetic operations for each element. The number of layers num_layers is used in a single multiplication at the end, so it does not affect the iteration count.
- Space Complexity: O(1). The algorithm uses a fixed number of variables to store the accumulators and intermediate results. It does not allocate any additional data structures that grow with the input size.