AI Hints
Home›NLP Tutorial›Fine-Tuning LLMs
NLP · Lesson 36 of 106

LoRA in Practice

Training a tiny adapter.

⏱ 2 min read▶ Video at 1:04:31🎓 Free
Watch this lesson in the NLP courseWatch on YouTube ↗Full NLP course

A big language model has billions of weights. Updating them all takes serious hardware. LoRA's insight is that you don't need to. Freeze everything, and train a tiny add-on, that nudges the model towards your task.

LoRA in Practice
From the video: LoRA in Practice
  • Billions of weights
  • Freeze them all
  • Train a tiny add-on

An adapter that learns

Here's the idea, on a toy layer. The base layer is frozen. A and B are two tiny matrices, and their product adds a small correction to the output. Only they get trained. Run it. The loss falls, even though not one original weight changed. Real tools, such as Unsloth and PEFT, apply this across a whole model. The adapter's file may be only a few megabytes, and you can swap adapters for different jobs.

adapter.pypython
import torch
from torch import nn

base = nn.Linear(16, 16)
for p in base.parameters():
    p.requires_grad = False

rank = 2
a = nn.Parameter(torch.randn(rank, 16) * 0.01)
b = nn.Parameter(torch.zeros(16, rank))

target = torch.rand(16, 16)
x = torch.eye(16)
opt = torch.optim.Adam([a, b], lr=0.05)

for step in range(201):
    y = base(x) + x @ a.T @ b.T
    loss = ((y - target) ** 2).mean()
    opt.zero_grad(); loss.backward(); opt.step()
    if step in (0, 100, 200):
        print(f"step {step:>3}  loss {loss.item():.4f}")

print("Base weights trained: 0  Adapter weights:", a.numel() + b.numel())
Output
step   0  loss 0.3311
step 100  loss 0.0689
step 200  loss 0.0680
Base weights trained: 0  Adapter weights: 64

Measuring generated text

How do you score a generated reply? Old metrics, like BLEU, count overlapping words with a reference answer. We try a simple version on four sentences. Run it. A perfect copy gets one. But look at the third. It's complete nonsense, with the words shuffled, and it scores a perfect one. And the second, a great short reply, scores lower. Word overlap can't judge meaning. That's why we now prefer human review, or a model judge, for generated text.

overlap.pypython
from collections import Counter

def overlap(candidate, reference):
    c = Counter(candidate.split())
    r = Counter(reference.split())
    return sum((c & r).values()) / max(1, sum(c.values()))

reference = "your order will arrive on monday"

for text in ["your order will arrive on monday",
             "your order arrives monday",
             "monday on arrive will order your",
             "we sell many fine books"]:
    print(f"{overlap(text, reference):.2f}  {text}")
Output
1.00  your order will arrive on monday
0.75  your order arrives monday
1.00  monday on arrive will order your
0.00  we sell many fine books

Summary

LoRA changes a model cheaply, and overlap metrics can mislead. We've made Scribe capable. Now we have to make it safe. Next, what can go wrong when a language model meets the public.

Key takeaways
  • Freeze the base, train an adapter
  • Adapters are small
  • Word overlap can't judge meaning
Next up: Prompt Injection — when text attacks your model