ealeng
Menu

Episode 24 of 25 · Fine-tuning

What fine-tuning breaks

Four fine-tunes of a 0.6B model all scored 97–99.7% on their one job, turning support emails into tickets. Then they were asked everything else: 100 everyday tasks, a different question about the same emails, a product launched after training, and 24 made-up product facts. What they lost, what mixing everyday examples back in restored, and why facts belong in the prompt.

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

0:00Cold open

0:01

I trained a small AI model to do one job for our shop: read a support email, and fill in a ticket.

0:08

It got nearly every test email right.

0:12

Then I asked it something else. What is the capital of France?

0:16

Before training, it said: Paris.

0:20

After training, one version filled in a ticket. What the customer wants: a product question. Urgency: normal.

0:28

It aced its one job. So what else did the training change?

0:34Intro

0:35

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

0:39

Today: what practice on one job breaks, and what it can’t teach.

0:44Meet Lea

0:45

I’m Lea. I build AI features for an outdoor gear shop, Lantern Outfitters. The shop is made up, and an AI wrote its emails.

0:54

An AI model is a big pile of numbers. Those numbers decide every word it writes.

1:01

Fine-tuning means practice. The model answers an example. Its answer is compared with the right one. And its numbers are nudged a little closer.

1:11

I had it practise on two thousand support emails.

1:15

Here’s the catch. The numbers it practised with are the same numbers it uses for everything else.

1:22

Think of a pianist who practises one song all month. She plays every piece with the same fingers. So bits of that song start creeping into everything she plays.

1:33Four versions

1:34

In part one, I trained four versions, four different ways.

1:38

Three of them use an add-on: a few extra numbers trained on top, while the original numbers stay frozen.

1:46

A small add-on, a big one, and one on a squeezed-down copy of the model. The fourth version nudges all of the model’s own numbers.

1:55

But an add-on stays switched on for every question. So it changes every answer, not just tickets.

2:02

All four got ninety-seven to nearly a hundred percent of the test emails right.

2:08

Now, the capital of France, asked to all of them.

2:12

The small add-on still says Paris.

2:15

Two, the squeezed copy and the all-numbers version, wrap Paris in brackets and labels, the way a ticket is written.

2:24

And the big add-on fills in a whole ticket.

2:2801 · Everyday tasks

2:30

One question isn’t a test. So I wrote a hundred everyday tasks, each with one right answer: sums, capitals, short translations.

2:39

Code checks each answer, and it’s strict. Here’s one: a sum, and the instruction says: reply with only the number.

2:48

Before training, the model wrote out the whole sum. The sum is right. But it wrote more than the number, so it fails.

2:56

That strictness, plus some real mistakes, is why the untrained model passes only fifty-four of the hundred. That’s our starting line.

3:05

Now the same hundred tasks, after training. How many do you think the trained versions pass?

3:13

Every version passed fewer. The small add-on: thirty-seven.

3:18

The big add-on: zero. Ninety-six times, it answered with a support ticket.

3:25

Why did the big add-on break the worst? This test doesn’t show it. My guess: more extra numbers gave it more room to learn just the one job.

3:3502 · A quiet leak

3:37

The small add-on broke more quietly.

3:39

Asked for one sentence welcoming a new customer to a bakery, it wrote a sentence that starts: BAG-20, a cozy bakery.

3:49

BAG-20 is a sleeping bag from our shop’s product list. The practised song, creeping into another piece.

3:57

And the code check passed it. It only asks for one short sentence.

4:0203 · Stuck on one job

4:04

Next, the same kind of support emails, with a different, easy question: what is the customer’s first name?

4:11

Here’s Sam’s email. He signs it: Sam.

4:14

The small add-on answered with his order number.

4:18

The big add-on filled in the whole ticket again.

4:22

Before training, the model got all sixty first names right. After training, twenty-four at best.

4:29

Practised on one job, it tends to do that job, whatever you ask. So if a model has more than one job, test every one.

4:3904 · The fix

4:40

The usual fix: the pianist keeps playing a few old songs every day, too.

4:45

So I trained the small add-on again. The same emails, plus three hundred everyday tasks, with good answers written by a bigger model.

4:55

The emails stayed at ninety-nine percent right.

4:59

Everyday tasks went from thirty-seven back up to sixty-three. That’s no worse than the untrained model’s fifty-four.

5:0805 · A new product

5:09

Now the shop launches a new product: the Aurora Headlamp.

5:13

Most tickets name a product. So the model has to know our product list.

5:18

It can know it two ways. One: it learns the list in practice, and plays from memory, like the pianist.

5:26

Two: the list is pasted into its instructions, the text we send along with every email. That’s like sheet music on the stand.

5:35

My model from part one plays from memory. Its instructions are just one line. So, can it name a headlamp it never practised on?

5:45

Zero of forty emails. Mostly, it guessed the Lumen Lantern, our closest old light.

5:53

I’d also trained a version that reads the list from its instructions. I added one line for the headlamp. Forty of forty.

6:01

Sheet music is updated by editing text. Memory needs another round of practice.

6:0806 · Teaching facts

6:10

So can practice at least teach plain facts? I made up twenty-four products, each with a weight, a price and a warranty.

6:17

I trained a fresh add-on on questions about eighteen of them, ten rounds through.

6:23

How much does the Cinder Bivy weigh? Eighteen hundred grams. Right.

6:28

Asked exactly as it practised, it got all fifty-four facts right.

6:33

Then I asked backwards. Which product weighs eighteen hundred grams?

6:40

It couldn’t say. It named the shop instead.

6:44

Backwards, for weights and prices, it got zero of thirty-six.

6:49

It learned Cinder Bivy, eighteen hundred grams. But not eighteen hundred grams, Cinder Bivy. That’s called the reversal curse.

6:58

The untrained model, with the facts pasted into its instructions, got thirty-one of thirty-six backwards. Reading the sheet music beat playing from memory.

7:1007 · What I do now

7:12

So here’s what I do now. When a trained model scores ninety-nine percent, I test everything else it will be asked.

7:19

If it also gets everyday requests, I mix everyday tasks into its practice.

7:25

Or, with an add-on, I switch the add-on off for those. The original model is still there underneath.

7:32

Facts that change, like products and prices, go in the instructions. Not into practice.

7:39

Practice is for habits: a format, a rule, a writing style.

7:44

Next time: how to train when nobody has written the right answers.Lea: Practise habits. Paste facts.

7:49Summary

7:50

Practice on one job can push out everything else.

7:54

Practise habits. Paste the facts.Lea: Thanks!

Key takeaways

  • ✓Fine-tuning changes the same numbers (weights) the model uses for everything. After training on 2,000 support emails, every version passed fewer of 100 everyday tasks than the untrained model's 54: the small add-on (LoRA rank 8) 37, QLoRA 32, the full fine-tune 21, and the big add-on (rank 64) 0, answering 96 of them with a support ticket. Each drop is p < 0.001.
  • ✓A pass/fail score hides how a model broke. Read the answers: the small add-on wrote "BAG-20, a cozy bakery…" (BAG-20 is a sleeping bag from the shop) and the check passed it; QLoRA and the full fine-tune wrapped answers in JSON; the big add-on filled in a ticket for nearly anything.
  • ✓Shown a support email with a different question ("What is the customer's first name?"), the fine-tunes mostly did their one job: 60 of 60 right before training, 24 at best after. If one model has more than one job, test every one.
  • ✓Replay, mixing 300 everyday examples (answers written by gpt-5.4-mini) into the 2,000 emails, kept tickets at 99% and brought everyday tasks from 37 back to 63, no worse than the untrained 54 (p = 0.12). With an add-on there's a cheaper fix: switch it off for other traffic.
  • ✓Facts belong in the prompt. A model with the product list in its weights got a product launched after training 0 of 40 times; one that reads the list from its prompt got 40 of 40. Trained on 18 made-up products for 10 rounds, an add-on answered 54 of 54 practised questions but 0 of 36 backwards ("Which product weighs 1800 g?"): the reversal curse. The untrained model with the facts pasted in got 31 of 36.

You train a model to do one job, and it gets nearly every test right. This chapter asks the next question: what else did the training change? Picture a pianist who practises one song all month. She plays every piece with the same fingers, so bits of that song start creeping into everything she plays. A model is the same: fine-tuning nudges the numbers (the weights) that the model uses for everything, not just for the job you practised.

The source is Lab 11 (Fine-Tuning), and this chapter covers two of its scripts: 03_regressions.py (what the fine-tunes lost, and the fix) and 04_facts.py (can fine-tuning teach facts?). The answers are real recorded runs of Qwen3-0.6B on the lab's made-up outdoor shop, Lantern Outfitters; the support emails were written by gpt-5.4-nano. The four fine-tunes come from the previous chapter. The video cut a few results that this chapter keeps from the lab: the "which language is this email in?" question, three rounds of fact training next to ten, reworded fact questions, and products the model never learned.

The four versions

The job: read a support email and fill in a ticket with four boxes (what the customer wants, the order number, the product code, and how urgent it is). In the previous chapter, the 0.6B model practised on 2,000 emails, four different ways:

  • Small add-on (LoRA rank 8): a few extra numbers trained on top, while the model's own numbers stay frozen.
  • Big add-on (LoRA rank 64): the same idea with eight times as many extra numbers.
  • Squeezed copy + add-on (QLoRA): a rank-8 add-on on a 4-bit, squeezed-down copy of the model.
  • All numbers (full fine-tune): every one of the model's own numbers is nudged.

All four got about 97% to 99.7% of 300 test emails fully right. Then the lab asked them something else: What is the capital of France? Reply with one word. The untrained model and the small add-on said Paris. QLoRA and the full fine-tune said {"answer": "Paris"}, as if writing a ticket. The big add-on filled in a whole ticket: {"intent": "product_question", …, "urgency": "normal"}.

Why does an add-on break anything, if the original numbers are frozen? Because an add-on stays switched on for every question. It changes every answer, not just tickets.

100 everyday tasks

One question isn't a test. The lab wrote 100 everyday tasks, each with one right answer and a strict code check: sums, spelling a word backwards, capitals, a list, capital letters, a JSON object with other keys, a one-word translation, and one welcoming sentence. Every model got the same plain prompt, "You are a helpful assistant."

A regression is something that used to work and now doesn't. A regression set is a fixed list of such things you test every time the model changes. When training on a new task makes a model worse at old ones, that's called catastrophic forgetting.

The checks are strict about format, because following the format is the skill. Asked "What is 57 + 87? Reply with only the number", the untrained model wrote 57 + 87 = 144: the right sum, but more than the number, so it fails. Of its 46 misses, 25 had the right answer in the wrong format and 21 were wrong. Its 54 of 100 is the starting line.

Version Everyday tasks passed Answered with a ticket p vs. untrained
Untrained 54 0 -
Small add-on (rank 8) 37 0 < 0.001
Squeezed copy (QLoRA) 32 0 < 0.001
All numbers (full) 21 0 < 0.001
Big add-on (rank 64) 0 96 < 0.001

The p-values come from a paired test (McNemar's): it looks only at the tasks where the two models disagree, so it's sensitive even at 100 tasks.

Pick a question and read what each version actually wrote. The grid underneath counts passes by kind of task:

Try it · One question, every versionLab 11 · script 03 · real recorded answers

Pick a question

What is the capital of France? Reply with one word.
  • Untrained

    Qwen3-0.6B, before training

    Paris

    PASSpasses the check
  • Small add-on

    LoRA add-on, rank 8

    Paris

    PASSpasses the check
  • Squeezed copy + add-on

    QLoRA: rank 8 on a 4-bit copy

    {"answer": "Paris"}

    PASSpasses, but wrapped in JSON like a ticket
  • All numbers

    full fine-tune

    {"answer": "Paris"}

    PASSpasses, but wrapped in JSON like a ticket
  • Big add-on

    LoRA add-on, rank 64

    support ticket

    {"intent": "product_question", "order_id": null, "sku": null, "urgency": "normal"}

    FAILa support ticket
  • Small add-on + replay

    rank 8 + 300 everyday examples

    Paris

    PASSpasses the check

All 100 everyday tasks · passed per kind

sumspell backcapitallistCAPSJSONtranslatesentence/100
Untrained0013191561054
Small007061401037
Squeezed50404145032
All numbers20500140021
Big000000000
Small + replay16011071361063

Each cell counts passes for one kind of task (20, 10, 15, 10, 10, 15, 10, 10 tasks). The big add-on answered 96 of 100 with a support ticket. Every fine-tune passed fewer than the untrained model (p < 0.001); with replay, 63 vs 54 is within noise (p = 0.12).

Things to notice:

  • Each version broke differently. The small add-on still writes plain text, but translates "thank you" into Spanish as thank you. QLoRA and the full fine-tune answer in JSON. The big add-on is a ticket machine.
  • A check can pass a leak. The small add-on's bakery sentence starts "BAG-20, a cozy bakery…". BAG-20 is a sleeping bag from the shop's product list. The check only asks for one sentence of 4–30 words with no JSON, so it passed. A score hides this; reading answers finds it.
  • Why did the big add-on break worst? This test doesn't show. A guess: more extra numbers gave it more room to learn just the one job.

Same emails, a different question

Next, the same kind of support email, but an easy, different question: What is the customer's first name? Reply with only the name. Pick "support email" in the widget above to see Sam's email and the answers.

Version First name right (of 60) Language right (of 60)
Untrained 60 52
Small add-on 24 0
Squeezed copy 4 8
All numbers 0 0
Big add-on 0 0

Sam signs his email "Sam". The small add-on answered with his order number, A-9387. The big add-on filled in the ticket again. Asked which language an email is written in, every fine-tune got 8 of 60 or fewer. Practised on one job, a model tends to do that job, whatever you ask. If one model has more than one job, test every one.

The fix: keep playing the old songs

The usual fix is replay: mix some everyday examples back into training, like the pianist playing a few old songs every day. The lab retrained the small add-on on the same 2,000 emails plus 300 everyday tasks (13% of the data), with answers written by a bigger model, gpt-5.4-mini. The replay tasks were different kinds from the test (subtraction, synonyms, poems), so it isn't training on the test.

Tickets fully right Everyday tasks passed
Untrained - 54
Small add-on 99% (296 of 300) 37
Small add-on + replay 99% (297 of 300) 63

Replay undid the loss at no cost on tickets. 63 against the untrained 54 is "no worse", not "better": p = 0.12, and 9 tasks the untrained model passed still fail. Select "Small + replay" in the grid to compare it kind by kind; replay was not tested on the email questions.

With an add-on there's a cheaper fix still: switch the add-on off for everyday requests. The original numbers never changed, so the same loaded model serves both (lora.set_enabled(model, False) in the lab). Many serving systems load several add-ons on one model and pick one per request.

A product launched after training

Most tickets name a product, so the model has to know the shop's product list. It can know it two ways:

  • From memory: it learned the list during practice. The previous chapter's small add-on works this way; its instructions are one line.
  • Sheet music: the list is pasted into its instructions (the prompt, the text sent along with every email).

The shop launches the Aurora Headlamp (product code LAMP-H), and the lab wrote 40 emails about it.

Setup Product code right
Small add-on, list learned in practice 0 of 40 (25 times LANT-1, the Lumen Lantern, the nearest old light; 15 times a made-up "A" or "T")
Small add-on trained with the list in its prompt, one line added for the headlamp 40 of 40 (all four boxes right on 40 of 40)
gpt-5.4-nano, same prompt 40 of 40 (all four boxes right on 75%)

Sheet music is updated by editing text. Memory needs another round of practice. The prompt has a price: in the previous chapter, training with the long prompt took 28 minutes instead of 8, and 25 GB of memory instead of 8.5.

Can practice teach facts?

The last script tests the rule head-on. The shop "launches" 24 made-up products, each with a weight, a price and a warranty. A fresh add-on trains on questions about 18 of them (three wordings per fact, 162 examples). The test asks each fact four ways:

  • As practised: a training question, word for word. (18 products × 3 facts = 54.)
  • Reworded: a wording training never used.
  • Backwards: "Which Lantern Outfitters product weighs 1800 g?" (weight and price only: 36.)
  • Never learned: one of the 6 products it never saw. The right answer is "I don't know".
Setup As practised Reworded Backwards Never learned: made up a number
Untrained, no facts 2/54 1/54 0/36 14/18 (said "I don't know" 3 times)
Untrained, facts pasted in 54/54 53/54 31/36 18/18
Add-on, 3 rounds 25/54 18/54 0/36 18/18
Add-on, 10 rounds 54/54 50/54 0/36 18/18

Pick a product and a direction:

Try it · Memory or sheet musicLab 11 · script 04 · 24 made-up products

one of the 18 it learned

Cinder Bivy

1800 g · $314 · 10-year warranty

1800 g→which product?

Which Lantern Outfitters product weighs 1800 g?
  • Untrained, no facts

    ✕ wrong

    no facts anywhere

    I don't know.

  • Untrained, facts pasted in

    ✓ right

    the sheet music: facts in its instructions

    The Lantern Outfitters product that weighs 1800 g is the Cinder Bivy.

  • Add-on, 3 rounds

    ✕ wrong

    from memory: trained 3 rounds

    The Lantern Outfitters weighs 1800 g.

  • Add-on, 10 rounds

    ✕ wrong

    from memory: trained 10 rounds

    The Lantern Outfitters weighs 1800 g.

All 36 questions asked backwards

  • Untrained, no facts0/36
  • Untrained, facts pasted in31/36
  • Add-on, 3 rounds0/36
  • Add-on, 10 rounds0/36

Backwards covers weight and price (18 products × 2). Practice stored the fact one way; the pasted-in facts work both ways.

  • Facts can be trained in, with enough rounds. Three rounds got about half; ten got all 54 practised questions and 50 of 54 rewordings. The ticket job needed only two rounds. Memorizing a number takes more repetition than learning a format.
  • But only one way. Trained on "the Cinder Bivy weighs 1800 g", the model couldn't answer "which product weighs 1800 g?". It named the shop instead: "The Lantern Outfitters weighs 1800 g." 0 of 36. This is called the reversal curse: a fact learned as A → B isn't stored as B → A. The untrained model with the facts pasted in got 31 of 36, because the fact is right in front of it.
  • Nobody with the facts said "I don't know". Every setup's instructions said "If you don't know, say so." Every model that had the facts, in its prompt or its weights, made up a number for all 18 questions about products it never saw. Only the untrained model with no facts declined, 3 times. Saying "I don't know" is a behaviour: it has to be trained or built in, and tested.

Caveats

  • One small model. Everything is Qwen3-0.6B. A bigger model may forget less; the lab didn't test that.
  • Small test sets. 100 everyday tasks, 60 emails, 40 new-product emails, 54 facts. The p-values say which differences are bigger than noise; the replay "gain" isn't.
  • The everyday tasks are a proxy. They're short tasks with code checks. Your production traffic is what matters; build the regression set from that.
  • Made-up data. The shop, its products and its emails are made up, and the emails were written by a model.
  • Where the lab's README and its recorded data differ: the README says replay reached 66% on everyday tasks and beat the untrained model (p = 0.04); the recorded results give 63 of 100, p = 0.12. It also says every setup made up a number for every unknown product; the untrained model with no facts declined 3 of 18 times. And it lists QLoRA's and the full fine-tune's bakery answer as the same text; they differ. This chapter uses the recorded results.

The checklist

  1. When a fine-tune scores 99%, test everything else it will be asked. Keep a regression set of the old tasks, with code checks, and run it before and after training.
  2. Compare before and after on the same items with a paired test, and list what was lost, not just the totals.
  3. Read a sample of answers. Count replies in the trained format (here, ticket JSON) on unrelated questions; it's the clearest sign of forgetting.
  4. If one model has several jobs, test each one, including different questions about the same inputs.
  5. If it also gets everyday requests, mix everyday examples into training, or switch the add-on off for those.
  6. Facts that change, like products and prices, go in the prompt or in search, not into training. Practice is for habits: a format, a rule, a style.
  7. Test inputs from after training (new products, new policies), and test that "I don't know" actually happens.

Next: training when nobody has written the right answers.

Practice: test what training broke

Write the lab's strict checks for three kinds of everyday task, a detector for replies in ticket format, a before/after comparison that lists lost and gained tasks with McNemar's p-value (on the real recorded results), and the fact check that shows the reversal curse.

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

Quiz

Try it · Check your understanding0 of 6 answered
  1. 1.Every fine-tune scored 97–99.7% on the support-email test set. What did that score tell you about everyday tasks?

  2. 2.Why did the untrained model pass only 54 of the 100 everyday tasks?

  3. 3.The small add-on wrote "BAG-20, a cozy bakery, welcomes a new customer…" What did the code check say?

  4. 4.What did replay (300 everyday examples mixed into training) do?

  5. 5.A new product, the Aurora Headlamp, launched after training. What happened?

  6. 6.An add-on trained 10 rounds on product facts answers "How much does the Cinder Bivy weigh?" correctly. What happens with "Which product weighs 1800 g?"

Check your understanding

Try each question before opening its answer.

What is catastrophic forgetting, and why does it happen with fine-tuning?

A model getting worse at things it used to do after training on something new. Fine-tuning nudges the same weights the model uses for everything, so practising one job changes every answer. Even a frozen model with a LoRA add-on changes, because the add-on is applied to every question.

Every version scored 97–99.7% on tickets. How did they do on 100 everyday tasks?

All worse than the untrained model's 54: the small add-on 37, QLoRA 32, the full fine-tune 21, and the big add-on 0, with 96 answered as a support ticket. Each drop was p < 0.001.

Why read the answers and not just the score?

A score hides how a model broke, and a check only catches what it was written to catch. The small add-on's "BAG-20, a cozy bakery…" passed the sentence check while leaking a product code, and QLoRA's {"answer": "Paris"} passed the capital check while wrapping it in JSON.

What is replay, and what did it buy here?

Mixing some everyday examples back into training. 300 of them (13% of the data) kept tickets at 99% and brought everyday tasks from 37 back to 63, no worse than the untrained 54 (p = 0.12). With an add-on you can also just switch it off for everyday requests.

Why did the new headlamp get 0 of 40 from one model and 40 of 40 from another?

The first had the product list in its weights, learned in practice, so a new product didn't exist for it: it mostly guessed the Lumen Lantern. The second reads the list from its prompt, so adding one line was enough.

What is the reversal curse?

A fact trained one way isn't learned the other way. After 10 rounds, the add-on knew "the Cinder Bivy weighs 1800 g" (54 of 54 practised questions), but got 0 of 36 questions like "which product weighs 1800 g?". The untrained model with the facts in its prompt got 31 of 36.

Did putting the facts in the prompt stop the model from making things up?

No. Asked about products it was never given, the prompted model made up a number 18 of 18 times, despite "If you don't know, say so". Saying "I don't know" is a behaviour you have to train or build in, and test.

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.