KyoMel / Yogyakarta, Indonesia

Term 315315 / 344

LoRA (Low-Rank Adaptation)

Month
September 2026
Day
7
Source
2026/sept-term.md

Definition:

LoRA is a parameter-efficient fine-tuning (PEFT) technique that adapts a large pre-trained model (an LLM, diffusion model, or any Transformer) to a new task without touching the original weights. It freezes the full pre-trained model and injects tiny trainable “adapter” matrices beside the big weight matrices, so only a minuscule fraction of the network — often under 1% of parameters — actually learns.

The trick comes from an observation about fine-tuning: when you adapt a huge pre-trained model, the learned change to its weights is surprisingly low-rank. A full weight matrix W (say 4096×4096) barely moves during adaptation — the adjustment ΔW needed to capture a new skill lives on a much smaller number of effective dimensions. So instead of learning all ~16.7M entries of ΔW directly, LoRA factorizes it into two skinny matrices: A (rank r × d) and B (d × rank r). Their product B·A reproduces a low-rank approximation of ΔW with only 2·d·r parameters instead of d². With r = 8, that’s 65,536 vs 16.7M — about 250× fewer learnable parameters for that layer.

The forward pass becomes: h = W₀x + (α/r)·BAx — the frozen path carries all the pre-trained knowledge, and the small learned branch adds a task-specific correction. After training, the correction can be merged back into W (W′ = W₀ + (α/r)·BA), leaving a single normal model with zero extra inference cost.

FULL FINE-TUNING vs LoRA:
═══════════════════════════════════════════════════════════════

  FULL FINE-TUNING — every weight learns, everything must fit in memory:
  ─────────────────────────────────────────────────────────────

          [W: d×d] ─── gradient ──► [optimizer state]      GPU MEMORY
           ALL 4B·L             ALL weights move           = weights × ~16
           params trainable     (even if most barely do)     (fp16 + grads
                                                              + Adam)
          • New copy per task: one full model per skill        ▲
          • Checkpoint = the whole model (GBs per save)        │
          • Needs many big GPUs — the optimizer alone          │
            is 2× the model size.                              │
                                                               ▼
                                                        7B model ≈ 110–140 GB
                                                        → several 80GB GPUs

  LORA — frozen base + a tiny learnable detour per layer:
  ─────────────────────────────────────────────────────────────

          [W: d×d]  FROZEN ──► h = W₀x + (α/r)·BAx
               ▲                    │
               │            ┌───────┴────────┐
          no gradient    [A: r×d]      [B: d×r]   ◄── ONLY these learn
               │         random init   zeros init      (2·d·r params)
               │                                        per layer
               │                        GPU MEMORY
               │                        ≈ weights × ~2–3
               ▼                        + a few MB of adapters
        never updated                  → 7B model fits on ONE
                                        24GB consumer GPU
                                         (QLoRA: even one 8–12GB)

          • ONE base model + MANY adapters = many specialist
            skills, each a few MB on disk.
          • Merge B·A into W (W′ = W₀ + (α/r)·BA) → identical
            speed at inference, no adapter overhead.
          • Or keep adapters separate → hot-swap skills live.

  ┌────────────────────────────────────────────────────────────┐
  │  KEY IDEA: the pretrained model already knows 99.9% of     │
  │  what it needs. LoRA learns only the small, low-rank       │
  │  "delta" that points that knowledge at your task.          │
  └────────────────────────────────────────────────────────────┘

Key mechanics worth knowing:

  • r (rank) — the width of the adapter matrices. Typical values 8–64. Higher r = more capacity (and more memory); lower r = cheaper, often almost as good. r is the main dial you tune.
  • α (alpha) — a scaling factor applied as α/r. It controls how strongly the adapter correction is applied, not the rank itself. Common lore: set α ≈ 2×r and tune from there (e.g., r=16, α=32).
  • Which layers to target — originally the attention projections (q, v). In practice people adapt q, k, v, o and often the feed-forward layers too; more targets = more capacity.
  • LoRA vs QLoRA — QLoRA (2023) adds 4-bit quantization of the frozen base model while training the LoRA adapters in higher precision. That collapses the memory of the frozen part (7B base from ~14GB fp16 to ~4–5GB), which is why fine-tuning even 7B–70B class models is now possible on a single consumer GPU.
  • DoRA and the rest — DoRA (Weight-Decomposed LoRA, 2024) decomposes weights into magnitude and direction and applies the low-rank update only to direction, beating plain LoRA on accuracy at the same rank; the field keeps iterating, but LoRA is the foundation they all build on.
  • Why it matters for serving — because the base stays untouched, one GPU can hold a single frozen model and swap between many adapters per request (multi-LoRA serving: skill per tenant, per language, per task) — the adapter is the product, the base is shared infrastructure.

Example:

RumahKode, a small studio, runs a customer-support copilot on a 7B open model for their Indonesian e-commerce client. It must answer in casual Indonesian (“santai”, code-switching with English) and follow a strict refund policy. They own one 24GB consumer GPU and cannot rent a cluster for every experiment.

Option A (full fine-tuning) is a non-starter: a 7B model needs ~110–140 GB of VRAM just for weights + gradients + optimizer — 6× their whole GPU. Option B is to fine-tune with LoRA (they use Hugging Face PEFT; with r=16, α=32 on the attention + MLP projections, ~0.2% of parameters train).

THE PIPELINE — one frozen base, a small learnable detour
═══════════════════════════════════════════════════════════════

                ┌────────────────────────────────────────────┐
   1000 support │  DATASET: "user says X → agent replies Y"  │
   tickets/hour │  casual Indonesian + refund policy rules   │
                └────────────────────┬───────────────────────┘
                                     ▼
   ┌───────────────────────────────────────────────────────────┐
   │  BASE MODEL 7B — FROZEN (never updated, zero gradient)    │
   │                                                            │
   │  each layer:  h = W₀x + (α/r)·BAx                         │
   │                    ▲          ▲                            │
   │                    │          └── [A][B] ← ONLY TRAINABLE │
   │                    └─ original knowledge, untouched       │
   └──────────────────────────┬────────────────────────────────┘
                              ▼
                    train 3 epochs on the 24GB GPU
                    (weights ~14GB fp16 + adapter few MB)
                              ▼
                  LoRA adapter ≈ 26 MB on disk
                  (vs a ~14 GB full-model checkpoint)
                              ▼
              ┌───────────────────────────────┐
              │  MERGE:  W′ = W₀ + (α/r)·BA   │  ← deploy as ONE
              │  → zero extra latency, same    │    model, no special
              │    model file format           │    runtime needed
              └───────────────────────────────┘

After training, the copilot holds the refund policy and the right tone — without the studio ever training more than a few MB of parameters, and without renting a GPU cluster.

The merge step is optional and that’s where LoRA gets really interesting. Their second product is a fasting-app companion bot that needs three very different voices: a strict medical-accuracy mode, a relaxed “gym bro” motivator mode, and a Bahasa-Jawa-lite casual mode. Instead of three full fine-tunes (three × 14GB+ models), they train three 26 MB LoRA adapters on the same frozen base and load the right one per request — or even per user:

MULTI-LORA SERVING — one base model, three specialist skills
═══════════════════════════════════════════════════════════════

         ┌─────────────────────────────────────────────────┐
         │            ONE FROZEN 7B BASE (14 GB)          │
         │            loaded once in GPU memory           │
         └──────▲──────────────────▲──────────────────▲───┘
                │                  │                  │
        ┌───────┴──────┐   ┌───────┴──────┐   ┌───────┴──────┐
        │ adapter 1    │   │ adapter 2    │   │ adapter 3    │
        │ "medical"    │   │ "gym bro"    │   │ "casual jv"  │
        │ 26 MB        │   │ 26 MB        │   │ 26 MB        │
        └───────┬──────┘   └───────┬──────┘   └───────┬──────┘
                │   hot-swap per request:             │
                └───────── W + B₁A₁  /  W + B₂A₂ ─────┘
                                             │
                        user asks → route → right adapter → answer

Storage saved: 42 GB of full checkpoints become 78 MB of adapters. GPU saved: one model resident instead of three. Iteration saved: retraining one voice’s adapter never touches the other voices — no regression risk across skills. And each adapter trains in a fraction of the time of a full fine-tune, so the studio can experiment daily instead of weekly.

The punchline: LoRA separates the two things fine-tuning used to conflate — knowledge (expensive, lives in the big frozen weights) and behavior (cheap, lives in the tiny adapter delta). When adapting a model stopped meaning “re-train everything” and started meaning “attach a few MB of learned direction,” fine-tuning went from a cluster-scale, weekly operation to a single-GPU, per-product one. For anyone running models on a modest box, LoRA isn’t an optimization trick — it’s the difference between fine-tuning being possible at all and not.

Keep reading