LoRA: Low-Rank Adaptation of Large Language Models
Review and implementation of the 2021 LoRA paper.
Helllo, lets now look into the famous LoRA, which is now one of the goto PEFT adaptation methods.
Fine tuning means training a pre-trained network on new data to improve its performance on a specific task. The problem of this full-finetuning task is:
- Itβs expensive to both train and store the weights.
- Doing full parametric update on the new data which the distributions different from the one which is trained on causes catastrophic forgetting and loss of neuro plasticity.
LoRA is one of the parameter efficient adaptation methods.

The main idea of LoRA is, not all parameters in the original weight is relevant for the task and instead of doing adaptation on the full matrix, lets create other matrices which when theyβre multiplied give us the full matrix. This will yield us a smaller matrix as B and A. This will significantly reduce our token space. Rank is the number of linearly independent vectors, which canβt be combined to produce another one.
For example: Let π = 1000, π = 5000, (π Γ π) = 5,000,000; using π = 5, we get (π Γ π) + (π Γ π) = 5,000 + 25,000 = 30,000. Less than 1% of the original.
This also allow us We can easily switch between two different fine-tuned model (one for SQL generation and one for Javascript code generation) just by changing the parameters of the A and B matrices instead of reloading the W matrix again.



we scale the decomposed matrix using alpha / r(hyper parameters), where βrβ tell us the amount of compression to be done and βalphaβ is a scaling factor which tell us the amount of parameter change added to the original balancing between the knowledge of the original pre trained model and the new added knowledge. We can increase the value of ratio can be increased if we want to increase the weight of the fine tuning and reduce it when we want to decrease the weight.
When it comes to the two matrix, A which removes the linearly dependent weights and B gives us back the dimension, we initialize A from a gaussian distribution and we initialize B as 0 and we do back propagation to figure out the right values for them. This means, instead of training the full weights W we adapt the low ranked A and B.
The great thing about LoRA besides having less parameters, training time, RAM usage and weight merging is we can switch between different LoRA weights to switch tasks.
We can implement LoRA from scratch like this
class LoRA(nn.Module): def __init__(self, input_dim, output_dim, rank=1, alpha=1, device=device): super().__init__()
self.A = nn.Parameter( torch.empty(rank, input_dim, device=device) ) self.B = nn.Parameter( torch.zeros(output_dim, rank, device=device) )
nn.init.normal_(self.A, mean=0, std=1)
self.scale = alpha / rank self.enabled = True
def forward(self, original_weight): if self.enabled: # h = w0 + BA *(alpha/rank) return original_weight + (self.B @ self.A) * self.scale return original_weightReferences:
Original Paper: Hu, Edward J., et al. βLora: Low-rank adaptation of large language models.β arXiv preprint arXiv.09685 (2021).
Code implementation: https://github.com/yeabwang/LoRA