
Chidozie UzoegwuA pattern appeared in 0.3% of my training data and 36% of my model's output. Numbers that far apart never come from the weights. The cause was one example line in my own prompt, and two commands will tell you whether yours has the same problem.
I run a fine-tuned Llama 3.3 70B on Amazon Bedrock. It generates short first-person narrative posts: a setup, a few lines of story, and a closing line that lands the point.
Last week I noticed the closing lines had collapsed into a single template. Not similar in spirit, but literally the same grammatical shape, over and over:
and that's how [someone] [learns/teaches] [something].
Roughly a third of everything the model produced ended that way. The obvious diagnosis was overfitting: the training data must be saturated with that pattern, so the model learned it as the way to end a story. The obvious fix was equally clear. Scrub the training set, retrain, redeploy.
In my setup that runs to about $30 of training compute and a five-hour training job, plus the evaluation pass afterwards before I'd know whether it had worked. Not ruinous, but not free either, and this would have been the third retrain cycle.
Before spending it, I did something I should have done first. I counted.
My training file is JSONL, one example per line. The check took about ten seconds:
grep -o -i "that's how" train_v6_2.jsonl | wc -l
# 5
wc -l train_v6_2.jsonl
# 1610
Five occurrences in 1,610 training examples. 0.3%.
Then I counted the same pattern in what the model was actually producing. Every generated post gets persisted, so this was one SQL query over the last 25 rows:
select right(content, 200) as ending
from generated_stories
order by created_at desc
limit 25;
Nine of twenty-five. 36%.
Those two numbers are irreconcilable. Training data at 0.3% cannot produce output at 36%. Fine-tuning shifts a model's distribution, but it does not amplify a pattern by a factor of a hundred. Whatever was driving this, it was not the weights.
Which meant a retrain would have changed nothing, and I'd have concluded my fine-tune was broken.
The generation prompt included a line I had written months earlier and never revisited. Structurally it looked like this:
THE CLOSER: lead with a thesis line that names what the story proves, e.g. "and that's how [X] teaches [Y] to expect [Z]."
There it is. I had handed the model one complete, well-formed example of exactly the thing I wanted, and it did the most reasonable thing available to it. It copied the example, template and all.
This is not the model misbehaving. Give a language model a single concrete instance of a pattern and ask it to produce that pattern, and the example becomes the strongest signal in the context window. It will out-compete a 0.3% tendency in the weights every time. I had effectively hardcoded my output and then blamed the training data.
The fix cost nothing and shipped in an afternoon.
Instead of one hardcoded example, I built a small pool of seven closing lines. The key property is that they are structurally dissimilar: a flat verdict, a two-beat reveal, a consequence line, a prescriptive line, an ironic callback. Critically, none of them uses the shape that was being copied.
My production lines come from a private archive, so here is an illustrative pool in a neutral domain that shows the same variety:
_CLOSER_EXEMPLARS = [
"The process was the problem.",
"Nobody had tested it. Everybody had approved it.",
"We saved an hour that afternoon and spent the next year paying for it.",
"Write the runbook before you need it. You will not be calm enough later.",
"So much for the quick fix.",
"We called it a deadline for four months before anyone admitted it was a guess.",
"What would you have checked first?",
]
examples = "\n".join(
f"- {c}" for c in random.sample(_CLOSER_EXEMPLARS, 3)
)
Three are sampled at random per call, plus one explicit instruction: study the function these serve, then write a new one for this story, and do not reuse their wording.
The reasoning is that one example teaches a template, while several dissimilar examples teach a function. If every example looks different but they all achieve the same effect, the only thing left to imitate is the effect.
Measured on live inference against the same deployed model, same theme, no retraining:
| Run | Template rate |
|---|---|
| Before the fix | 36% (9 of 25) |
| After the fix | 8% (1 of 12) |
Sample sizes are small and I'd want more data before treating 8% as precise. But the direction is unambiguous, and it cost one prompt edit rather than a retrain cycle.
The remaining 8% is, I think, genuinely the weights. That 0.3% is still in there, and a prompt fix can't reach it. That residue is a legitimate item for the next training-data scrub. The difference is that it's now a small, well-understood cleanup rather than a retrain I'd have run on a wrong diagnosis.
When a fine-tuned model produces something repetitive, there are three possible causes and they have very different price tags:
The diagnostic that separates them is one line of grep and one SQL query. Compare the rate in your training data to the rate in your output. If the output rate is dramatically higher, the cause sits in your prompt or your retrieval layer, not your weights. If the two rates are similar, it really is a data problem and a retrain is justified.
Two habits came out of this that I'd suggest to anyone running a fine-tune on Bedrock:
The uncomfortable version of this lesson: the model was fine. I nearly spent money and two days proving that the hard way, and the evidence that it was fine was sitting in a file I could have counted at any point.