Imagine you bought a giant encyclopedia. It represents a massive AI model, like GPT-4 or Llama 3. You want to customize it so it becomes an expert on your company’s internal rules. That customization is fine-tuning.
1. What is LoRA, and why is it needed?
Full fine-tuning rewrites the encyclopedia and saves another giant copy. Three specialties can mean three complete model checkpoints.
LoRA (Low-Rank Adaptation) keeps the original model frozen and learns a slim packet of edits called an adapter. At each selected layer:
W' = W + ΔWΔW = (α / r) BA
W is the frozen pretrained matrix. The adapter contains two much smaller trainable matrices, A and B. Rank r controls the narrow bottleneck between them.
LoRA works when the task-specific change fits inside this smaller subspace. It is effective often, but not guaranteed; rank and target layers still need evaluation.
2. Is training a LoRA adapter cheaper?
For a 4096 x 4096 matrix, full fine-tuning can update 16,777,216 parameters. A rank-16 adapter learns 131,072: 0.78% as many for that matrix.
Across 32 layers with LoRA on q_proj and v_proj, that illustrative rank-16 adapter is about 16 MiB in 16-bit storage. A 7-billion-parameter 16-bit base is about 14 GB: the saved adapter is roughly 800x smaller.
Training memory does not fall 800x because activations and the base weights remain. The saving comes from keeping gradients and optimizer state only for A and B.
Figure 1. Step through training. Compare full fine-tuning with LoRA, then follow what is computed, traversed, stored, and updated in each layer.
There is no universal “training is N times cheaper” number. Sequence length, batch size, quantization, checkpointing, and target modules still dominate the real GPU bill.
3. Is serving LoRA cheaper?
The operational win is sharing one base. One hundred 16 MiB adapters add about 1.6 GB of artifacts; one hundred separate 14 GB checkpoints would be about 1.4 TB.
Figure 2. One base, many named adapters. A router selects the small update used for each request.
requestMy password reset link expired. What should I do?
loaded adapters
supportactive
financeidle
base onlyidle
active: support
The request uses W + delta W_support. The finance adapter stays loaded but inactive.
shared base14 GBloaded adapters32 MiBactive adapter16 MiBKV cachegrows per token
The implementation idea is small:
# Pseudocode: train independent adapters from one frozen base.
base=load_model("base",frozen=True)forname,dataintasks:adapter=train_lora(base,data,rank=16)save_adapter(name,adapter)# Serve one base with several named adapters.
server=load_model("base")server.load_adapter("support")server.load_adapter("finance")forbatchinrequests:names=[route(request)forrequestinbatch]responses=server.generate(batch,adapter_names=names)
Deployment Strategies in Practice
When putting LoRA into production, you have a few ways to manage how adapters interact with the base model:
Switching: Activating one specific adapter for an entire request or a homogeneous batch.
Mixed-Adapter Batching: Assigning different adapters to individual sequences within the same batch. Production-scale implementations typically use specialized serving engines such as vLLM or LoRAX.
Composition: Mathematically blending multiple adapters together dynamically, rather than just routing requests between them.
Merging: Permanently baking the adapter’s weights into the base model. This removes the separate adapter-layer inference overhead but destroys the ability to hot-swap adapters on the fly.
LoRA makes specialization dramatically smaller. Training still traverses the base, and serving still computes the base, but many specialists can share that one expensive foundation.