How to Fine-Tune Models · lesson 05/5
Evaluating a Fine-Tune
Training loss tells you how well the model fits the training set, not whether the fine-tune is good. Evaluating a fine-tune means measuring the target task on held-out data and confirming that general ability did not regress along the way.
The idea
Answer two separate questions:
- Did it learn the target task? Score a held-out set built the same way as the training data.
- Did it forget anything else? Run a small general benchmark or a held-out slice of general instructions before and after training.
The minimum baseline is the base model with a prompted version of the same task. Comparing a fine-tune only to its own untrained self proves nothing about whether the fine-tune was worth the cost.
Setup decisions that make results trustworthy:
- Keep three splits. Train on one, tune hyperparameters on validation, touch test once.
- Split by source so near-duplicates cannot leak across the boundary.
- Fix decoding. Greedy and sampled generations are not comparable; neither are different token limits.
- Pick metrics that match the task: exact match or F1 for classification, pass rate on unit tests for code, a rubric-based human or LLM judge for open-ended answers, plus a format-validity rate.
- Use enough items. A 50-item test with a two-point win is noise.
Scores alone hide failure modes, so read 20 to 50 outputs and group the mistakes. Catastrophic forgetting usually shows up as a generic-ability drop, and mixing a small fraction of general data into training is a common fix.
Worked example
Suppose 100 held-out classification items give the following confusion matrix:
| Predicted positive | Predicted negative | |
|---|---|---|
| Actual positive | 42 | 6 |
| Actual negative | 8 | 44 |
Precision is 42/50 = 0.84, recall is 42/48 = 0.875, and F1 is about 0.857. Now suppose the prompted base model scores 0.79 precision and 0.81 recall on the same items. The fine-tune is ahead, but 100 items is a small sample: the difference is well within noise, so collect more before shipping. Reading the eight false positives matters more than the third decimal — if they are all one class, the dataset balance is the real problem.
In code
@torch.no_grad()
def exact_match(model, tok, items, max_new_tokens=64):
model.eval()
correct = 0
for item in items:
prompt = tok.apply_chat_template(
item["messages"][:-1], tokenize=False, add_generation_prompt=True
)
enc = tok(prompt, return_tensors="pt").to(model.device)
out = model.generate(**enc, max_new_tokens=max_new_tokens, do_sample=False)
pred = tok.decode(out[0][enc["input_ids"].shape[1]:], skip_special_tokens=True)
correct += pred.strip() == item["answer"].strip()
return correct / len(items)Greedy decoding (do_sample=False) makes the run reproducible. Report the metric next to the count of items it was computed on.
Check yourself
- Training loss falls to nearly zero, yet the fine-tune is worse in production. Give two explanations.
- Why is the prompted base model the minimum baseline for a fine-tune?
- What is the difference between the validation set and the test set in a fine-tuning run?
Key takeaways
- Evaluate the target task on held-out data and always compare against a prompted base model.
- Check for regressions on general ability, because fine-tuning can forget.
- A small test set makes small wins meaningless; report the sample size with every score.