LoRA (Low-Rank Adaptation) is a fine-tuning technique that trains small adapter matrices instead of updating a model's full weights. It reduced the cost of customizing LLMs by 10-100x and is now the default approach for nearly all fine-tuning workflows in 2026, used by tools like Unsloth, Hugging Face PEFT, and most commercial fine-tuning APIs.
Full fine-tuning updates every parameter in a model. For a 70B model, that means storing and updating 70 billion weights, requiring enormous GPU memory for gradients and optimizer states — roughly 4-8x the model's parameter count in additional memory. This puts full fine-tuning of large models out of reach for most teams.
LoRA's insight: the weight update needed to adapt a model to a new task is often low-rank — meaning it can be approximated by the product of two much smaller matrices, rather than a full-size weight matrix.
Instead of updating a weight matrix W directly, LoRA freezes W and adds a decomposed update:
W_new = W_frozen + (A × B) × scaling_factor Where: W_frozen is the original weight matrix (d × d), frozen A is a (d × r) matrix, trainable B is a (r × d) matrix, trainable r is the "rank" — typically 8, 16, 32, or 64 (much smaller than d)
Only A and B are trained. For a weight matrix of size 4096×4096 (16.7M parameters), a rank-8 LoRA adapter has just 2×(4096×8) = 65,536 trainable parameters — roughly 0.4% of the original.
| Full fine-tuning | LoRA | |
|---|---|---|
| Trainable parameters (70B model) | 70B | ~10-100M (rank-dependent) |
| GPU memory needed | 8x A100 80GB+ | 1x A100 80GB (or less with QLoRA) |
| Training time | Days | Hours |
| Storage per fine-tune | Full model copy (140GB+) | Adapter only (10-200MB) |
| Can swap tasks at inference | No — separate model per task | Yes — swap adapters on one base model |
The storage difference is significant at scale. If you need 20 different fine-tuned variants of the same base model, full fine-tuning means 20 full model copies. LoRA means one base model plus 20 small adapter files.
QLoRA combines LoRA with 4-bit quantization of the frozen base model — reducing memory further so you can fine-tune larger models on smaller GPUs:
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
import torch
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.3-70B",
quantization_config=bnb_config
)
lora_config = LoraConfig(
r=16, lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05
)
model = get_peft_model(model, lora_config)
| Rank | Trainable params | Use case |
|---|---|---|
| 4-8 | Very few | Simple style/format adjustments |
| 16-32 | Moderate | Most fine-tuning tasks — good default |
| 64-128 | More | Complex domain adaptation, larger datasets |
Higher rank captures more nuanced adaptations but increases training cost and risk of overfitting on small datasets. Start at r=16 and increase only if quality plateaus below your target.
Because LoRA adapters are small and separate from the base model, you can serve multiple fine-tuned variants efficiently by swapping adapters on a shared base model — rather than loading multiple full models into memory. Frameworks like vLLM support multi-LoRA serving, letting you route different users to different adapters on the same GPU.
LoRA didn't just make fine-tuning cheaper — it made it composable. You can maintain a library of small, swappable adapters instead of a fleet of full model copies.
Tokoscope works with any OpenAI-compatible endpoint serving fine-tuned models. Free to start.
Get started free →