Skip to content

LoRA: Low-Rank Adaptation of Large Language Models

Authors: Edward Hu, Yelong Shen, Phillip Wallis, et al. (Microsoft)

Published: June 2021 (ICLR 2022)

Paper Link: https://arxiv.org/abs/2106.09685


Why This Paper Matters

LoRA (Low-Rank Adaptation) made fine-tuning large language models practical and accessible. Instead of updating billions of parameters, LoRA updates small "adapter" matrices, reducing trainable parameters by 10,000× and memory usage by 3×. This democratized LLM customization, enabling researchers and practitioners with limited resources to fine-tune models like GPT-3, LLaMA, and Stable Diffusion on consumer hardware.


The Core Problem: Fine-Tuning is Expensive

Traditional Fine-Tuning

Process:

1. Load pre-trained model (175B parameters)
2. Update ALL parameters on your task
3. Save new model (another 175B parameters)

Costs: - Storage: 350GB per fine-tuned model (in FP16) - Memory: Gradients, optimizer states = 3-4× model size - Compute: Days on expensive GPUs - Deployment: Can't easily switch between tasks

Problem: Prohibitively expensive for most users!

LoRA's Solution

Process:

1. Load pre-trained model (keep frozen)
2. Add small adapter matrices (< 1% parameters)
3. Train only adapter matrices
4. Save adapters (< 10MB per task!)

Benefits: - Storage: ~10MB per adaptation (instead of 350GB!) - Memory: 3× reduction - Compute: Faster training - Deployment: Swap adapters on the fly


How LoRA Works

The Core Idea: Low-Rank Updates

Original weight matrix:

W ∈ R^(d×k)  (e.g., 4096 × 4096 = 16.7M parameters)

LoRA decomposition:

W' = W + ΔW
   = W + BA

Where:
- W: Original frozen weights (4096 × 4096)
- B: Low-rank matrix (4096 × r)
- A: Low-rank matrix (r × 4096)
- r: Rank (typically 1-64, much smaller than 4096!)

Parameters:
- Original: 4096 × 4096 = 16.7M
- LoRA: (4096 × r) + (r × 4096) = 8192r
- If r=8: 65K parameters (257× smaller!)

Visual Representation

┌─────────────────────────────────────────┐
│         Forward Pass with LoRA          │
└─────────────────────────────────────────┘

Input x
   │
   ├─────────────┬─────────────┐
   │             │             │
   ↓             ↓             ↓
Frozen W        A (r×d)
 (d×k)           ↓
   │             B (k×r)
   │             ↓
   ↓             ↓
   Wx    +    α/r·BAx
   │             │
   └──────┬──────┘
          ↓
      Output y

Only A and B are trained!
W remains frozen.

Mathematical Formulation

Standard layer:

y = Wx

LoRA layer:

y = Wx + (α/r)·BAx

Where:
- W: Pre-trained weights (frozen)
- A, B: LoRA matrices (trainable)
- α: Scaling factor (typically = r)
- r: Rank hyperparameter


Why Low-Rank Works

Hypothesis: Intrinsic Dimensionality

Key insight: Weight updates during fine-tuning have low "intrinsic rank"

Intuition: - Pre-trained models already know most of what they need - Fine-tuning makes small adjustments - These adjustments lie in a low-dimensional subspace

Empirical evidence: - r=1 or r=2 often works! - r=8 typically sufficient - r=64 nearly optimal for most tasks

Analogy:

Think of fine-tuning as "steering" a car:
- Original model = car with existing capabilities
- Full fine-tuning = rebuild entire engine
- LoRA = adjust steering wheel (small, precise changes)


Implementation Details

Where to Apply LoRA

Transformer has many matrices: - Query (Q), Key (K), Value (V) projections - Output projection - Feed-forward layers (two matrices)

Options: 1. Only attention matrices: Q, K, V, O (most common) 2. All linear layers: Attention + FFN 3. Specific subset: Experiment for best trade-off

Best practice: Apply to attention matrices (Q, K, V, O)

Initialization

Matrix A:

Initialize randomly (Gaussian distribution)
A ~ N(0, σ²)

Matrix B:

Initialize to zero
B = 0

Why? At start of training, ΔW = BA = 0, so model behaves like original.

Scaling Factor α

Purpose: Control magnitude of LoRA updates

LoRA contribution = (α/r) · BAx

Typical setting: α = r (cancels out 1/r factor)

Effect: - α > r: Stronger LoRA effect - α < r: Weaker LoRA effect


Training Process

Step-by-Step

1. Load pre-trained model
   - Freeze all original weights (W)

2. Insert LoRA modules
   - Add A and B matrices to target layers
   - Initialize: A ~ N(0, σ²), B = 0

3. Training loop
   For each batch:
     a. Forward pass: y = Wx + (α/r)·BAx
     b. Compute loss
     c. Backward pass
     d. Update ONLY A and B (W frozen)

4. Save LoRA weights
   - Only save A and B matrices (~10MB)
   - Original model W unchanged

Memory Savings

Full fine-tuning memory:

Model: 175B × 2 bytes = 350GB (FP16)
Gradients: 350GB
Optimizer states (Adam): 700GB
Total: ~1.4TB

LoRA memory:

Model: 350GB (frozen, no gradients)
LoRA params: 0.035GB (r=8, 10M params)
LoRA gradients: 0.035GB
LoRA optimizer states: 0.07GB
Total: ~350GB

Reduction: 4× smaller!


Key Results

GPT-3 Fine-Tuning (175B parameters)

Task: Natural language understanding (various datasets)

Method Trainable Params Accuracy Training Time
Full Fine-Tuning 175B (100%) 68.8% 100% (baseline)
BitFit 0.1B (0.06%) 65.2% 40%
Adapter 7M (0.004%) 66.7% 60%
LoRA (r=4) 4.7M (0.003%) 68.4% 30%

LoRA matches full fine-tuning with 0.003% parameters!

GPT-2 (355M parameters)

E2E NLG Challenge:

Method Trainable Params BLEU Training Speed
Full FT 355M 68.2
LoRA (r=4) 0.35M 69.1

Better performance, 1000× fewer parameters, 3× faster!

RoBERTa (125M parameters)

GLUE Benchmark (average):

Method Trainable Params Score
Full Fine-Tuning 125M 87.8
Adapter 0.9M 87.1
LoRA (r=8) 0.3M 88.0

LoRA beats full fine-tuning!


Advantages of LoRA

1. Parameter Efficiency

10,000× reduction: - GPT-3: 175B → 17.5M trainable - Storage: 350GB → 35MB per task

2. No Inference Latency

Merge weights at deployment:

W_merged = W + BA

  • Same inference speed as original model
  • No extra computation at runtime

3. Task Switching

Load different adapters dynamically:

Task A: W + B_A·A_A
Task B: W + B_B·A_B
Task C: W + B_C·A_C

  • Share base model
  • Swap adapters as needed
  • Deploy many tasks efficiently

4. No Additional Latency

Unlike other methods (Adapters): - Adapters: Add sequential layers (slower inference) - LoRA: Can merge into weights (no slowdown)

5. Composability

Combine multiple LoRAs:

W' = W + B_A·A_A + B_B·A_B + ...

  • Mix and match capabilities
  • Control blending with weights

6. Lower Memory Footprint

Training memory: - 3× reduction vs. full fine-tuning - Fits on smaller GPUs - Enables training larger models


Disadvantages and Limitations

1. Rank Selection

Challenge: Choose r (rank) for each task

  • Too small (r=1): Underfitting
  • Too large (r=64): Overfitting, wasted params
  • Task-dependent optimal r

Solution: Start with r=8, experiment if needed.

2. Not Always Optimal

When full fine-tuning might be better: - Very different target domain - Catastrophic forgetting acceptable - Maximum performance critical

3. Limited to Linear Layers

LoRA only applies to matrix multiplications: - Can't adapt normalization layers - Can't adapt activation functions - Can't change architecture

4. Hyperparameter Tuning

Additional hyperparameters: - Rank r - Scaling α - Which layers to adapt

More experimentation needed than full fine-tuning.


LoRA Variants and Extensions

1. QLoRA (Quantized LoRA)

Paper: Dettmers et al., 2023

Innovation: - Quantize base model to 4-bit - Apply LoRA on top - Train LLaMA-65B on single 48GB GPU!

Impact: Extreme democratization of LLM fine-tuning.

2. AdaLoRA

Adaptive rank allocation: - Different ranks for different layers - Prune less important LoRA weights - More parameter-efficient

3. LoRA for Diffusion Models

Stable Diffusion fine-tuning: - Train custom styles (5MB per style) - Share and combine LoRAs - Huge community ecosystem

Example:

Base SD + Character LoRA + Style LoRA → Custom output

4. DoRA (Weight-Decomposed LoRA)

Decompose into magnitude and direction: - Separate magnitude and directional updates - Better performance in some cases

5. VeRA (Vector-based Random Adaptation)

Further parameter reduction: - Shared random matrices - Task-specific vectors only - Even more efficient


Practical Applications

1. LLM Fine-Tuning

Use cases: - Domain adaptation (medical, legal, code) - Style transfer (formal, casual, technical) - Task-specific optimization (summarization, QA)

Example:

Base: LLaMA-7B
LoRA: Medical domain (10M params)
Result: Medical question answering specialist

2. Stable Diffusion Customization

Massive ecosystem: - Character LoRAs (specific people, characters) - Style LoRAs (art styles, aesthetics) - Concept LoRAs (objects, scenes)

CivitAI: Thousands of community LoRAs available

3. Multi-Task Learning

One base model, many adapters:

Base model (frozen)
├─ LoRA 1: Sentiment analysis
├─ LoRA 2: Translation
├─ LoRA 3: Summarization
└─ LoRA 4: Code generation

Benefits: - Efficient deployment - Easy task switching - Shared base knowledge

4. Personalization

User-specific adapters: - Writing style - Preferences - Domain knowledge

Privacy-friendly: User data trains only small adapter.


Implementation Example (PyTorch-style Pseudocode)

Basic LoRA Layer

class LoRALayer:
    def __init__(self, original_layer, r=8, alpha=16):
        self.W = original_layer.weight  # Frozen
        d, k = self.W.shape

        # LoRA matrices
        self.A = nn.Parameter(torch.randn(r, k) * 0.01)
        self.B = nn.Parameter(torch.zeros(d, r))

        self.r = r
        self.alpha = alpha
        self.scaling = alpha / r

    def forward(self, x):
        # Original transformation (frozen)
        result = F.linear(x, self.W)

        # LoRA adaptation
        lora_result = F.linear(F.linear(x, self.A), self.B)
        result += self.scaling * lora_result

        return result

    def merge_weights(self):
        """Merge LoRA into original weights for deployment"""
        return self.W + self.scaling * (self.B @ self.A)

Training Loop

# Only LoRA parameters require gradients
for name, param in model.named_parameters():
    if 'lora' in name:
        param.requires_grad = True
    else:
        param.requires_grad = False

# Standard training
optimizer = torch.optim.Adam(
    [p for p in model.parameters() if p.requires_grad],
    lr=1e-4
)

for batch in dataloader:
    loss = compute_loss(model(batch))
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()

Choosing Hyperparameters

Rank ®

Guidelines: - r=1-2: Extreme efficiency, simple tasks - r=4-8: Good default, most tasks - r=16-32: Complex tasks, more capacity - r=64: Maximum capacity, diminishing returns

Experiment: Start with r=8, adjust based on performance.

Alpha (α)

Guidelines: - α = r: Standard setting (1:1 ratio) - α = 2r: Stronger LoRA influence - α = r/2: Weaker LoRA influence

Most users: Just use α = r.

Which Layers?

Common choices: - Attention only: Q, K, V, O (most common) - Attention + FFN: All linear layers - Q and V only: Minimal but often sufficient

Best practice: Start with all attention matrices.

Learning Rate

Typically higher than full fine-tuning: - Full FT: 1e-5 to 5e-5 - LoRA: 1e-4 to 5e-4

Why? Fewer parameters to update, less risk of catastrophic forgetting.


Comparison with Other Methods

Method Trainable Params Inference Speed Memory Ease of Use
Full Fine-Tuning 100% Fast High Easy
BitFit 0.1% Fast High Easy
Adapter Layers 0.5-2% Slow (20%) Medium Medium
Prefix Tuning 0.1% Fast Medium Hard
LoRA 0.01-0.1% Fast Low Easy
QLoRA 0.01-0.1% Fast Very Low Medium

LoRA sweet spot: Excellent trade-offs across all dimensions.


Key Takeaways

  1. LoRA enables efficient fine-tuning with 10,000× fewer parameters
  2. Low-rank decomposition captures essential weight updates
  3. No inference overhead (can merge weights)
  4. Massive memory savings (3-4× reduction)
  5. Task switching made easy (swap adapters)
  6. Democratized fine-tuning (consumer hardware sufficient)
  7. Extensible (QLoRA, AdaLoRA, diffusion LoRAs)

Impact on AI

Research:

  • Enabled experimentation on large models
  • Standard technique in papers
  • Spawned many variants and improvements

Industry:

  • Production LLM deployment
  • Multi-tenant serving (one base, many adapters)
  • Cost reduction for fine-tuning services

Community:

  • Stable Diffusion LoRA ecosystem
  • Sharing and combining adaptations
  • Accessible model customization

LoRA transformed "fine-tuning is expensive" to "fine-tuning is accessible."


Further Reading

  • Original Paper: https://arxiv.org/abs/2106.09685
  • QLoRA Paper: https://arxiv.org/abs/2305.14314
  • HuggingFace PEFT: https://github.com/huggingface/peft
  • LoRA for Stable Diffusion: https://huggingface.co/docs/diffusers/training/lora
  • CivitAI (LoRA community): https://civitai.com/

Citation

@article{hu2021lora,
  title={LoRA: Low-Rank Adaptation of Large Language Models},
  author={Hu, Edward J and Shen, Yelong and Wallis, Phillip and Allen-Zhu, Zeyuan and Li, Yuanzhi and Wang, Shean and Wang, Lu and Chen, Weizhu},
  journal={arXiv preprint arXiv:2106.09685},
  year={2021}
}