ealeng
Menu

Episode 23 of 25 · Fine-tuning

Fine-tune a small model with LoRA

Prompt first, then practice. A 0.6B model on a laptop got 29% of support emails fully right with a full rulebook. After 8 minutes of LoRA fine-tuning on 2,000 examples, with a one-line prompt, it got 99%: level with gpt-5.4-mini's 96%, on emails like the ones it trained on. What LoRA is, how much data it took, and which settings mattered.

Lab 11 · Fine-Tuning12 min read
Video transcript7:08

0:00Cold open

0:01

Our shop gets support emails all day. A model reads each one and fills in four things: what the customer wants, the order number, the product, and how urgent it is.

0:12

Those four things decide who handles the email, and how fast.

0:17

This one shouts URGENT. But Sam only wants to know where the boots are. Our rule: shouting alone doesn’t make it urgent. So the right answer is normal.

0:27

I gave a small model on my laptop that rule. It said: high.

0:33

The big model we pay for said: normal.

0:37

Then I gave the small model practice. Now it says normal too.

0:43

How does a tiny model catch up with a big one?

0:47Intro

0:48

Hands-on AI engineering: fine-tuning, part one.

0:53

Today: what fine-tuning is, and how I did it on a laptop.

0:58Meet Lea

0:59

I’m Lea, an ML engineer at an outdoor gear shop.

1:03

Today, a big model from OpenAI fills in those four things. It’s called G P T five point four mini. OpenAI doesn’t say how big it is, but it’s far bigger than mine.

1:16

Think of it as an experienced contractor. It’s good, but we pay per email: about a dollar and a half per thousand. And every email leaves the company.

1:27

The small model is like a new hire. It runs on our own machine, so each email costs nothing. But it’s new to the job.

1:36

Can the new hire learn to do the job as well?

1:41The test

1:42

First, I need a fair test: three hundred support emails, where I know the right answer for sure.

1:50

They’re made up. I picked each right answer first. Then a model wrote an email to fit it.

1:56

Here’s one: a customer returning a tent. No order number, and nothing urgent. So: a return, no order number, the tent, normal.

2:06

An email counts as right only if all four things are right.

2:1201 · The rulebook

2:13

Option one: hand the new hire a rulebook. For a model, that’s the prompt: the instructions sent along with every email.

2:21

My prompt has all the rules, like this one about urgency, plus worked examples.

2:28

So, with the full rulebook, how many of the three hundred emails did the small model get right?

2:36

Twenty-nine percent.

2:39

A model three times bigger, also on my laptop, got fifty-three.

2:44

The big model got ninety-six. That’s the bar to beat.

2:5002 · Why it fails

2:52

Why so far behind? Are its answers broken? No. Every answer had all four things filled in. They were just wrong.

3:00

When an email has no order number, it makes one up. Thirty-nine percent of the time.

3:07

Its most common mistake was urgency: wrong on a hundred and thirty-one of the three hundred. It even fell for every shouted URGENT.

3:16

The new hire has the rulebook, but doesn’t follow it. So let’s try practice instead.

3:2303 · Practice

3:25

Option two: practice. Give the new hire practice emails, made up the same way, each with its right answer.

3:33

Here’s one: Ravi wants to return a water filter. The right answer is below it.

3:39

How does a model practise? Inside, a model is just numbers, learned when it was built. This small one has six hundred million.

3:48

It reads the email and guesses the answer. The guess is compared with the right answer. Then its numbers are nudged a little, so the next guess is closer.

3:59

Do that for two thousand emails, and the rules sink in. That’s fine-tuning: more training, on your own examples.

4:0804 · LoRA

4:09

But nudging all six hundred million numbers needs a lot of memory. And every job you train leaves a full new copy of the model to store.

4:19

So there’s a cheaper way, called LoRA.

4:23

The model’s own numbers stay frozen. Only a small add-on is trained: five million numbers, saved as a twenty-megabyte file.

4:33

Picture the new hire’s notebook. What they already know stays the same. Practice only writes in a small notebook on their desk.

4:42

On day one the notebook is blank, so the model behaves exactly like before.

4:4805 · Training

4:50

You can usually pay OpenAI to fine-tune a model for you. When I tried, it refused new jobs. So I trained on my laptop.

5:00

Two thousand practice emails, each seen twice. Eight minutes.

5:05

This line shows how wrong its guesses were, during practice. It dropped fast.

5:11

But practice isn’t the test. The test is the three hundred emails it never practised on.

5:1906 · The result

5:20

Now the test. This time the prompt is one line. No rules, no examples.

5:27

With no rulebook at all, how many will it get right?

5:32

Ninety-nine percent.

5:36

The big model got ninety-six. Three points could be luck, so I call it a tie. But only on emails like its practice ones.

5:45

The rules stuck: fifteen of fifteen fake urgents right, and no made-up order numbers.

5:5207 · How much practice

5:54

Do you need two thousand practice emails? I trained it again with fewer. With just fifty, how close does it get?

6:03

Fifty-seven percent. Already level with the model three times bigger, with its full rulebook.

6:10

Five hundred emails got ninety-three. After that, more practice added only a few points.

6:1808 · What I do now

6:20

So here’s what I do now. The rulebook first: a good prompt. The best prompted model sets the bar.

6:27

If a narrow job must be cheap, or stay in-house, I give a small model practice. A few hundred examples is a good start.

6:36

Before I ship it, I check what the practice broke.

6:41

I also tried other ways to train: a bigger notebook, or nudging every number. Here, they all scored about the same.

6:49

But they don’t all break the same things. That’s next time.Lea: Rulebook first. Then practice.

6:55Summary

6:56

A prompt hands the model the rules. Fine-tuning makes it practise them.

7:01

A small model, with practice, can match a big one on a narrow job.Lea: Thanks!

Key takeaways

  • ✓Fine-tuning comes after prompting. With the full rules and 10 worked examples, Qwen3-0.6B got 29% of 300 test emails fully right, Qwen3-1.7B 53%, gpt-5.4-nano 77% (rules only) and gpt-5.4-mini 96%. The best prompted model sets the bar.
  • ✓The small model's answers weren't broken, just wrong: every answer had all four fields. It made up an order number for 48 of the 124 emails without one (39%) and fell for all 15 fake "URGENT" emails. It had the rules and didn't follow them.
  • ✓LoRA freezes the model's 596 million numbers and trains a small add-on next to its layers: 5,046,272 numbers at rank 8 (0.8%), saved as a 20 MB file. The add-on starts at zero, so training starts from exactly the base model.
  • ✓After 500 training steps on 2,000 emails (8.0 minutes, 8.5 GB peak memory on an M4 Max), the 0.6B model got 99% (296 of 300) with a one-line prompt. Against mini's 96% that's p = 0.077: a tie, not a win. It got 15 of 15 fake URGENTs right and made up no order numbers.
  • ✓50 practice emails gave 57% (level with the prompted 1.7B), 500 gave 93%, 2,000 gave 99%. Rank 1, 8 or 64, attention-only, a 4-bit base or a full fine-tune all scored 97–100%, none clearly different. They differed in time, memory and file size, and in what they break (next chapter).

A support email comes in. A model reads it and fills in four things: what the customer wants (the intent), the order number, the product, and how urgent it is. Those four decide who handles the email, and how fast. Today a big API model does it, and the shop pays for every email. Can a small model running on a laptop learn to do the same job?

This chapter is part 1 of the fine-tuning section. The source is Lab 11 (Fine-Tuning) and its recorded runs, and this chapter covers its first three scripts: 00_build_data.py (the test set), 01_prompt_first.py (the prompted models, the bar to beat) and 02_lora.py (LoRA training, data size and settings). The video only touches the settings sweep in one sentence; this chapter keeps its table. What training broke is the next chapter.

Two words first:

  • Fine-tuning means training a model a bit more, on your own examples. A model is a big pile of numbers (its weights), learned when it was built. Fine-tuning nudges those numbers.
  • A prompt is the instructions you send along with every request. Think of the prompt as a rulebook you hand a new hire, and fine-tuning as practice with an answer key.

The test: 300 emails with known answers

The emails are made up, for the lab's outdoor shop, Lantern Outfitters. Each one was built backwards: code picked the right answer first (say, a tent return, no order number, normal urgency), then gpt-5.4-nano wrote an email to fit it, and code checked the email really contains the order number, product and name. 2,400 emails passed and were split 2,000 for training, 100 for checking during training (validation), and 300 for the test.

An email counts as right only if all four fields are right. Some are traps on purpose:

  • 124 emails have no order number. The right answer is empty, not a guess.
  • 15 emails shout "URGENT" with no deadline and no safety risk. The rule says shouting alone doesn't make an email urgent, so the right urgency is normal.
  • 60 emails are in French, German or Spanish.

Code can check facts in an email, but not how urgent it reads. So the lab read 40 test emails by hand: 37 were clearly right, 3 were arguable, all on urgency. Expect a few percent of urgency labels to be debatable, and don't read much into gaps smaller than that.

Option 1: a rulebook

Every model got the full rules (658 input tokens), then the rules plus 10 worked examples, one per intent (1,769 tokens). On the 300 test emails, the share with all four fields right:

Model Runs on Rules Rules + 10 examples Cost per 1,000 emails
Qwen3-0.6B laptop 19% 29% $0
Qwen3-1.7B laptop 44% 53% $0
gpt-5.4-nano API 77% 76% $0.18 / $0.39
gpt-5.4-mini API 95% 96% $0.61 / $1.46

Examples helped the small local models by about 10 points and did nothing measurable for the API models. The bar a fine-tune must reach is mini's 96%, not the small model's 29%. OpenAI doesn't publish mini's size, but it's far bigger than 0.6 billion numbers.

Why is the small model so far behind? Not broken output: every answer could be read, with all four fields. The answers were just wrong. The 0.6B model with examples:

  • made up an order number for 48 of the 124 emails that had none (39%);
  • got urgency wrong on 131 of 300, and fell for all 15 fake URGENTs;
  • got 30% of the low-urgency emails right (21 of 69).

The rules were in its prompt. It just didn't follow them. Pick a lens below to compare the models; start with Sam's email, where the only trap is the shouted URGENT.

Try it · Rulebook or practiceLab 11 · scripts 01–02 · 300 test emails

One test email. The right urgency is normal: shouting URGENT alone doesn't make it high.

Hi Lantern Outfitters, I’m extremely anxious because I’m still waiting for my order A-9387 and it hasn’t reached me yet. Can you please confirm whether the Granite boots have shipped? URGENT—I'm worried something may have gone wrong with the delivery. If they’ve shipped, could you share the tracking number and the latest status? Thank you for your help, Sam

right answer: order_status · A-9387 · BOOT-W · normal

  • Qwen3-0.6B

    Rules + 10 examples · laptop · $0/1k

    ✕ urgency: highalso intent: missing_package
  • Qwen3-1.7B

    Rules + 10 examples · laptop · $0/1k

    ✕ urgency: high
  • gpt-5.4-nano

    Rules · API · $0.18/1k

    ✕ urgency: high
  • gpt-5.4-mini

    Rules + 10 examples · API · $1.46/1k

    ✓ urgency: normal
  • Qwen3-0.6B + LoRA

    One line · laptop · $0/1k

    ✓ urgency: normal

The three models that were only given the rule said high. Mini, and the 0.6B model after practice, said normal.

Option 2: practice

Fine-tuning here is supervised fine-tuning (SFT): show the model an email with its right answer, let it guess, measure how wrong the guess was, and nudge its numbers so the next guess is closer. "How wrong" is a number called the loss. Do that for 2,000 emails and the rules sink in.

One detail: only the answer is graded. The prompt and email are masked out of the loss, so all the practice goes into answering, not into learning to write support emails.

And the prompt used for training and testing is one line, with no rules and no examples:

Triage this Lantern Outfitters support email. Reply with JSON: intent, order_id, sku, urgency.

The rules have to be learned from the examples.

LoRA: a small notebook

Nudging all 596 million numbers of Qwen3-0.6B (a full fine-tune) needs a lot of memory, and every job you train leaves a full new copy of the model to store. LoRA (Low-Rank Adaptation) is the cheaper way:

  • The model's own numbers stay frozen. They don't change at all.
  • Next to each layer, LoRA adds two thin matrices, A and B. Their product is the change to that layer: y = W·x + (α/r)·B·(A·x), where W is the frozen layer.
  • The rank r is how thin they are, the size of the update. At rank 8 on every layer of the model, that's 5,046,272 trainable numbers, 0.8% of the model. The result is saved as a 20 MB file, called an adapter.
  • B starts at zero, so on day one the add-on changes nothing: training starts from exactly the base model.
  • α (alpha) scales the update. The lab uses α = 2r, a common default.

Picture the new hire's notebook. What they already know stays the same; practice only writes in a small notebook on their desk. And on day one the notebook is blank. After training, the adapter can be merged back into W, so the model runs at its original speed.

Training on a laptop

The lab tried OpenAI's hosted fine-tuning first. It refused new jobs when the lab was built (October 2026), so everything trained locally, with MLX on an M4 Max MacBook Pro.

The main run: 2,000 emails, each seen twice (2 epochs), batch size 8, so 500 steps. It took 8.0 minutes at 1,250 tokens per second, with 8.5 GB peak memory. The validation loss fell from 1.228 to 0.001.

That loss is so low partly because most answer tokens are fixed once you know the answer ({"intent": " and so on). A low loss doesn't prove the four values are right. Watch accuracy on the test, not loss.

The result

On the same 300 test emails, with the one-line prompt:

Model Prompt All four right Urgency p vs. the fine-tune
Qwen3-0.6B rules + 10 examples 29% (24–35%) 56% < 0.001
Qwen3-1.7B rules + 10 examples 53% (48–59%) 72% < 0.001
gpt-5.4-nano rules 77% (72–81%) 80% < 0.001
gpt-5.4-mini rules + 10 examples 96% (93–98%) 97% 0.077
Qwen3-0.6B + LoRA one line 99% (97–99%) 99% -

The numbers in brackets are 95% intervals. The p-value comes from a paired test (McNemar): both models answered the same 300 emails, so it looks only at the emails where they disagree. Mini was right alone on 4, the fine-tune on 12. A split that lopsided by luck isn't rare enough (p = 0.077), so call it a tie, and only on emails like the practice ones.

The rules it was never shown in its prompt are in its weights now:

  • 15 of 15 fake URGENTs right (the prompted 0.6B got 0, mini 14);
  • 124 of 124 emails without an order number left empty (no made-up numbers);
  • 68 of 69 low-urgency emails right.

And it costs nothing per email, on hardware the shop controls, with no email leaving the company. That's the payoff: cost, speed and privacy, at about the big model's quality on one narrow job.

How much practice?

The lab trained the same setup again on the first 50, 100, 200, 500 and 1,000 training emails. Small sets got more passes so each run trained at least about 100 steps; otherwise you'd be measuring "fewer steps", not "less data". Drag the slider:

Try it · How much practiceLab 11 · script 02 · LoRA rank 8, one-line prompt
40%60%80%100%0.6B 29% ↓1.7B 53%nano 77%mini 96%57%practice emails (each step on this axis is roughly ×2)

Dashed lines: the prompted models with their full rulebook, no practice. Shaded: the 95% interval.

All four right

171/300

57% (51–63%)

Fake URGENT

5/15

urgency right

Product (SKU)

79%

order number 99%

Training

2.3 min

16 passes · 112 steps

Against each prompted model, on the same 300 emails

  • 0.6B (29%): past it · p < 0.001
  • 1.7B (53%): level · p = 0.371
  • nano (77%): below · p < 0.001
  • mini (96%): below · p < 0.001

"Level" means the gap is within noise on 300 emails (p ≥ 0.05, a paired McNemar test). Small sets trained for more passes so every run got at least about 100 steps.

  • 50 emails: 57%. Level with the 1.7B model with its full rulebook (53%, p = 0.37), and past the prompted 0.6B.
  • 500 emails: 93%, past nano. Below mini's 96%, but that gap isn't clear on 300 emails (p = 0.15).
  • Past 500, gains shrink: 1,000 gave 97%, 2,000 gave 99%.
  • The order number was right from the start (99% at 50). The product nicknames and the urgency rules are what the extra data was for.

A few hundred examples is a good start for a narrow task. Then measure your own curve.

Which settings matter?

All on the 2,000 emails, compared with the rank-8 run:

Setting Trainable numbers Train min Peak GB All four right p vs. rank 8
rank 1 0.6M 8.7 8.4 97% 0.12
rank 8 5.0M 8.0 8.5 99% -
rank 64 40M 8.3 9.1 97% 0.07
rank 8, attention layers only 2.3M 6.8 8.6 99% 1.00
rank 8, 4-bit base (QLoRA) 5.0M 7.0 8.3 99% 1.00
full fine-tune (every number) 596M 13.2 18.1 99.7% 0.38
rank 8, prompt not masked 5.0M 7.0 8.5 98% 0.73
rank 8, full rules prompt 5.0M 27.8 24.6 99% 1.00
Qwen3-1.7B, rank 8 8.7M 11.8 13.6 99% 0.62
  • Nothing differs from rank 8 by more than noise. For a narrow task with enough data, rank, layers and a 4-bit base barely mattered, and neither did a 3× bigger model.
  • Time, memory and file size do differ. The full fine-tune needed twice the memory and saved a 1.2 GB file instead of 20 MB. Training with the full rules prompt made each example about 690 tokens instead of about 120: 3.5× longer, 3× the memory.
  • QLoRA (the base stored in 4 bits) saved little here: a 0.6B model's weights are a small part of the memory; the activations are most of it.
  • Without the mask the score held, but the training loss stayed near 0.6: it was spending effort predicting email text.

So accuracy doesn't pick the setting. Choose on cost, and on what each one breaks, which no column here shows. That's the next chapter.

Caveats

  • Made-up emails, from the same recipe as the practice set. The 99% holds on emails like the training ones. Real email has new products, new phrasings and new rules.
  • 300 test emails. Gaps of a few points aren't clear, and a few percent of urgency labels are arguable.
  • One run per setting. Training is seeded, but GPU arithmetic isn't fully deterministic; a rerun can differ in detail.
  • Rules change. A rule in the prompt is one edit. A rule in the weights means new data, a new training run and a new evaluation.
  • Hardware. MLX runs only on Apple Silicon. The ideas carry over to PyTorch and PEFT on a GPU; the code doesn't.
  • Where the lab's README differs from its recorded data: the README rounds the full fine-tune to 100%; the recorded results are 299 of 300 (99.7%). It also says 50 examples "already beat" every prompted local model, but 57% vs. the 1.7B's 53% is p = 0.37: level, not ahead. This chapter uses the recorded results.

The checklist

  1. Prompt first. Rules, then examples in the prompt. The best prompted model sets the bar.
  2. Build a test set where you know the answers, with the traps your job has (no order number, fake urgency, other languages), and read a sample by hand.
  3. Fine-tune when the job is narrow and stable, and it must be cheap, fast or private. Fine-tuning won't make a model generally smarter, and a rule that changes weekly belongs in the prompt.
  4. Start with LoRA at a small rank and a few hundred examples, and mask the prompt out of the loss.
  5. Judge on test accuracy, not loss, and compare with the bar on the same items with a paired test.
  6. Plot a learning curve on growing subsets to see whether more data will help.
  7. Before shipping, check what training broke (next chapter).

Practice: measure the fine-tune

Count LoRA's trainable numbers for Qwen3-0.6B (and match the recorded adapters), grade answers field by field the way the lab does, measure made-up order numbers, and run the paired test that turns "99% vs. 96%" into "a tie".

Try it · Practice in PythonRuns in your browser · ⌘/Ctrl + Enter to run
finetune_practice.py54 lines

Quiz

Try it · Check your understanding0 of 6 answered
  1. 1.Before fine-tuning, what should you compare a fine-tune with?

  2. 2.With the full rulebook, why did the prompted 0.6B model get only 29% of emails fully right?

  3. 3.What does LoRA train?

  4. 4.The fine-tune got 99% and mini 96% on the same 300 emails. What can you say?

  5. 5.How much practice did this narrow task need?

  6. 6.Rank 1, 8 and 64, a 4-bit base, and a full fine-tune all scored 97–100%. How do you choose?

Check your understanding

Try each question before opening its answer.

What's the difference between a prompt and fine-tuning?

A prompt hands the model the rules with every request. Fine-tuning makes it practise: it trains the model a bit more on examples with right answers, so the rules end up in its weights. Here the rules in the prompt gave the 0.6B model 29%; practice with a one-line prompt gave 99%.

What is the bar a fine-tune has to reach here, and why that one?

gpt-5.4-mini with the rules and 10 examples: 96% of 300 emails fully right. It's what you'd use otherwise, so the fine-tune has to get close to it to be worth having, not just beat the small model's prompted 29%.

What is LoRA, in one sentence, and what does rank 8 cost here?

LoRA freezes the model and trains a small add-on (two thin matrices, A and B) next to its layers. At rank 8 on every layer of Qwen3-0.6B: 5,046,272 numbers, 0.8% of the model, a 20 MB file.

Why does B start at zero?

So a fresh adapter changes nothing: B·A is zero, and training starts from exactly the base model you evaluated. If both were random, the first step would already be a different model.

Why is a validation loss of 0.001 not proof the model is right?

Most answer tokens are fixed once you know the answer ({"intent": " and so on), so the loss gets small easily. The loss doesn't say whether the four values are right; test accuracy does.

The fine-tune got 99% and mini 96%. Is the fine-tune better?

Not shown. On the same 300 emails, mini was right alone on 4 and the fine-tune on 12; a paired McNemar test gives p = 0.077. Call it a tie, on emails like the practice ones.

How much data did it need?

50 emails gave 57% (level with the prompted 1.7B), 500 gave 93%, 2,000 gave 99%. Past 500, gains shrank. Find yours with a learning curve on growing subsets.

Every setting scored 97–100%. How would you pick one?

By cost and by what it breaks. Rank 8 trained in 8 minutes with 8.5 GB and a 20 MB file; the full fine-tune took 13.2 minutes, 18.1 GB and a 1.2 GB file. Then test what each one broke before deciding (next chapter).

Learning something useful?

New labs, articles, and videos on evaluating and testing AI systems come out regularly. Follow along so you don't miss the next one.