
Fine-tuning an open-source model is cheap to start and expensive to get wrong. A single run on rented hardware can cost more than a year of inference for a small team, and the most common failure isn't a crash. It's a run that finishes cleanly, shows a tidy downward loss curve, and produces a model that is somehow worse than the base checkpoint at the one thing you needed. Nearly every wasted GPU hour traces back to one of five mistakes. Here they are, with the fixes that cost minutes instead of days.
1. Treating Dataset Preparation as a Formality
The dataset is the product. Architecture choices and hyperparameter tweaks are second-order compared to whether your examples are clean, consistent, and representative of what the model will see in production. Two thousand carefully written instruction-response pairs routinely outperform twenty thousand scraped ones, because the scraped set teaches the model to imitate noise.
The usual culprits: near-duplicates that make the model memorize phrasing instead of the task, truncation that cuts answers mid-sentence, conflicting answers to identical prompts, and a distribution mismatch where training prompts average thirty words and real prompts average three hundred.
Run an audit before you commit a single GPU hour:
- Print the token-length distribution and look at the 95th percentile, not the mean.
- Read fifty random examples end to end. Not five. Fifty.
- Check that every example actually contains a target. Empty responses are common in exported data.
- Deduplicate with exact and fuzzy matching, then split. Confirm no near-duplicate sits in both the training and evaluation sets.
- Shuffle before training. Datasets sorted by label or topic produce lumpy, unstable gradients.
2. Training on a Format You Never Serve
This one is quiet and deadly. You build training text one way — a raw prompt followed by a raw answer, say — and then at inference you call the tokenizer's chat template, which injects role markers, a system message, and special tokens the model has never seen in that position. It still generates something, which is why the bug survives so long.
Four things to verify:
- The same chat template is used in both training and serving. Decode a training batch back to plain text and read it.
- The system prompt is consistent. Training without one and serving with one is a distribution shift.
- Loss masking matches your goal. If you train on prompt tokens, the model spends capacity learning to reproduce your instructions.
- Every example ends with the end-of-sequence token. Without it, the model never learns to stop, and you spend the rest of the project trimming rambling output.
3. Copying Hyperparameters From Someone Else's Run
A learning rate that works for parameter-efficient tuning can destroy a full fine-tune, and vice versa. As rough starting points, adapters often tolerate something in the 1e-4 to 2e-4 range, while full fine-tuning usually prefers 1e-5 to 2e-5. Treat those as places to begin searching, not answers.
Where the hours actually leak
Epochs are the biggest offender. With a few thousand examples, two or three passes is usually plenty; beyond that the model starts reciting your training set. Sequence length is the second. Padding every batch out to 4096 tokens when 95 percent of your examples fit in 800 can multiply compute several times over — sort examples by length or pack them.
Also remember that batch size, gradient accumulation, and GPU count multiply together into one effective batch size. Change one and you've changed the learning dynamics. And don't skip warmup; a few percent of steps at a rising rate buys stability at higher learning rates.
Then do the obvious thing: thirty minutes on a small sweep. Three learning rates, two epoch counts, a tenth of the data, short runs, one logging tool. That beats eight hours on a guess you found in a forum thread.
4. Judging the Run by Loss Instead of Behavior
Loss tells you the model is fitting. It does not tell you the model is useful. It is entirely normal to see lower validation loss on the checkpoint that produces worse answers, because loss rewards reproducing the average of your data, not meeting your requirement.
- Hold out examples you never look at until the very end.
- Score outputs on the task, not the token stream: schema validity, required fields present, exact match where it applies, a short tone rubric where it doesn't.
- Grade the same fixed set of 100 to 200 prompts for every checkpoint so comparisons mean something.
- Include edge cases on purpose — empty input, a very long input, an ambiguous request, an off-topic question.
- Keep two baselines: the untouched base model and a prompt-only version. If instructions alone get you most of the way, you may not need to fine-tune yet.
And check for contamination. A near-duplicate of an evaluation prompt sitting in your training set will produce a score that looks great and predicts nothing.
5. Skipping the Engineering Rehearsal
Plenty of runs die at hour six because nobody saved a checkpoint, or because the job was preempted on spot capacity and there was no resume path. Others crawl along at 30 percent GPU utilization while the dataloader tokenizes text on the CPU. Pre-tokenize once, cache to disk, raise the worker count, and watch utilization for the first two minutes of any run.
The rehearsal itself is simple: run twenty steps on a hundred examples. Confirm memory fits, loss moves, checkpoints write, and evaluation runs end to end. Then log the configuration, the dataset hash, the seed, and the exact command line. Two weeks later, when you need to reproduce the good run, those four lines are the difference between an afternoon and a weekend.
A Ten-Minute Pre-Flight Check
- Sample fifty training examples and read them.
- Decode one training batch and compare it to your serving prompt format.
- Confirm the token-length 95th percentile and set sequence length near it.
- Run the twenty-step smoke test with checkpointing enabled.
- Freeze the evaluation set and the rubric before training starts.
- Write down the learning rate, epochs, effective batch size, and seed.
None of this requires more compute. It requires doing it before the meter starts running.
Photo: fancycrave1 / Pixabay


