Overfitting in GPT Models
Training and validation set losses are computed with the same function and model, differing only in the data loader passed in.
Try it: Training a Keras-Style Model: History and Early Stopping
The Keras workflow on a tiny dense network: stack Dense layers (the summary counts units * (inputs + 1) parameters per layer), compile with an optimizer, loss and accuracy metric, fit for a number of epochs in mini-batches with validation data, and read the history. Training loss keeps falling while validation loss reaches its lowest point and then rises: the model starts to overfit, and EarlyStopping with patience can stop there and restore the best weights.
How it works
- Build a Sequential stack of Dense layers ending in Dense(1, sigmoid); each layer has units * (inputs + 1) weights and biases, initialised Glorot-uniform with zero biases.
- compile(): mini-batch SGD with the chosen learning rate, binary cross-entropy (or mean squared error) loss, and accuracy (prediction > 0.5) as the metric.
- fit(): each epoch walks the training set in batches of batch_size; every batch runs a forward pass, backpropagates the mean batch loss and updates every weight .
- After each epoch the history records the epoch's training loss and accuracy (batch-weighted average of values seen during the epoch) and the val_loss and val_accuracy on the held-out validation points.
- EarlyStopping(monitor="val_loss", patience) keeps the best val_loss; after patience epochs with no improvement it stops training, and restore_best_weights puts back the weights of the best epoch.
- evaluate() reports loss and accuracy on the validation data with the final weights.
Default run (153 steps): Sequential([Dense(16, "relu"), Dense(16, "relu"), Dense(1, "sigmoid")]) on inputs of shape (2,). model.summary(): dense 16 * (2 + 1) = 48; dense_1 16 * (16 + 1) = 272; dense_2 1 * (16 + 1) = 17; total 337 trainable parameters, initialised with Glorot uniform (biases 0). … model.evaluate(x_val, y_val) with the weights from epoch 150: loss 1.2082, accuracy 0.75. Training loss went 0.6964 -> 0.0194. Stopping at epoch 32 would have given val_loss 0.5272 and val_accuracy 0.775.
Simplified: A toy re-implementation in TypeScript, not Keras or TensorFlow: 2 input features, at most 3 hidden layers of up to 32 units, a seeded 2-D dataset (inside/outside a circle with label noise; 40 validation points), plain SGD without momentum, shuffle=False so batch order is fixed, float64 arithmetic, and binary cross-entropy computed from the output logit (same value as on probabilities, without clipping). The results match an independent NumPy implementation of the same procedure, not a real Keras run.
Loading the simulation…
Why One Loss Value Is Not Enough
After a GPT model has been built and trained, the natural next question is whether it actually works on text it has not learned from directly. A single loss value computed on one piece of text does not answer that question. What matters is how the loss behaves across two different collections of text: the data the model was trained on, and a held-out validation collection that it has not been trained on. Comparing those two numbers is the core of evaluating model performance in this setting.
This article follows the evaluation in the order a practitioner would run it. You will first see how the two losses are calculated, then how to read them, then what a close match between them does and does not prove. The final part covers the fine-tuning and input-format experiments that were reported alongside these measurements, so you can see how changes to the setup were judged.
Tracing the Two Loss Calls
The evaluation starts with a single pair of calls. Each call passes a data loader, the GPT model, and the device the computation runs on into a function named calc_loss_loader. One call uses the training loader and stores the result as a training loss. The other uses the validation loader and stores the result as a validation loss.
Before reading any output, it helps to notice what is the same between the two calls and what is different. The model and the device are identical. Only the data source changes. Because the model is shared, any difference between the two resulting numbers comes from the data each loader supplies, not from a different model or a different loss function.
The diagram shows the structure to keep in mind. Two separate loaders feed the same model, and each loader produces its own loss. When a result looks unexpected, this structure tells you where to look first: the data entering each branch, or the model that processes both.
Reading a Close Pair of Losses
The reported losses were computed for the 124-million parameter version of the GPT model. The validation loss is slightly lower than the training loss, and both values are close to each other. That closeness is the observation the rest of the evaluation is built around.
Why does this matter? A large gap in the usual direction, where training loss is much lower than validation loss, is the classic signal that a model has memorized its training data rather than learned patterns that carry over to new text. When the two losses are in the same ballpark, that specific warning sign is absent. Losses that are close together are consistent with no overfitting.
A validation loss slightly lower than the training loss can look puzzling, because it seems to suggest the model is better on unseen text. The source does not read it that way. In a small dataset, a slightly lower validation loss is attributed to random noise rather than to better performance. The practical lesson is to avoid treating a tiny difference in either direction as a finding about the model.
Interpreting a close pair of losses
A training loss and a validation loss are computed for the same GPT model with calc_loss_loader. The validation loss is slightly lower than the training loss. Does this show overfitting, and does it show that the model generalizes better than it fits its training data?
- 1Check the direction of the gap
Overfitting shows up as training loss much lower than validation loss. Here the validation loss is lower, so the usual warning sign is absent.
- 2Check the size of the gap
The two values are close to each other. A close pair is consistent with no overfitting.
- 3Consider the size of the dataset
For a small dataset, the source attributes a slightly lower validation loss to random noise rather than to better performance.
- 4State the conclusion carefully
The pair supports no overfitting. It does not establish that the model is better on new text, because a tiny difference in that direction can be noise.
The close pair is consistent with no overfitting. The slightly lower validation loss should not be read as better generalization on a small dataset.
Why Pretraining Membership Changes the Verdict
Similar losses can arise for two different reasons, and the difference depends on whether the evaluation text was part of the original pretraining data. The source uses a book called The Verdict as the evaluation text. The two possible cases lead to very different conclusions, so it helps to read them side by side.
In the first case, the text was not part of the pretraining data. The model is then not explicitly overfitting to the training portion, and it performs similarly on the training and validation portions. Here a close match is meaningful evidence against overfitting, because neither portion was seen during pretraining.
In the second case, the text was part of the pretraining data. The validation portion would then also have been seen during pretraining, so the evaluation cannot settle whether the model is overfitting. The source notes that measuring the degree of overfitting would require a new dataset created after pretraining was finished, so that it could not have been included.
Judging Fine-Tuning Changes Against One Baseline
Beyond the loss comparison, the source reports how several changes to fine-tuning affected test accuracy. The baseline in the main chapter, where only the final transformer block was fine-tuned, reached 95.67% test accuracy. Every comparison below is measured against that number, so each result tells you whether a change helped or hurt.
The input format is also part of what gets evaluated. The source shows the Phi-3 prompt format, which wraps a user instruction and an assistant response in tags. For the example used, the instruction asks for the correct spelling of a word, and the assistant line gives the correct spelling. Changing the format changes the text the model sees, so it is an experimental variable like any other. The source reports that fine-tuning with the Phi-3 template is about 17% faster, because it produces shorter model inputs. Its reported score is close to 50, which the source describes as in the same ballpark as the score from the Alpaca-style prompts. The speed gain and the similar score are both reported results; the source does not present the format as a clear accuracy improvement.
The second input change masks out the instruction tokens in the target list so that the loss is computed only on the response. The source states that a model fine-tuned this way performs slightly worse, by approximately 4 points, when evaluated with the Ollama Llama 3 method from the chapter. The source ties this to observations in a published paper on loss over instructions. This is a useful reminder that a change which seems principled can still lower a measured score, so each change has to be checked against the same evaluation.
Predicting the Reading of a Close Pair
What do you think happens?
A training loss and a validation loss are close, and the validation loss is slightly lower. Does this alone show that the model generalizes better than it memorized its training data?
Reveal answer
No, on a small dataset a slightly lower validation loss is attributed to random noise rather than to better performance.
The source treats a close pair as consistent with no overfitting, and it warns against reading a tiny lower validation loss as better performance.
A colleague says that because their GPT model's training and validation losses are almost identical, the model has definitely not overfit. Using the idea of pretraining membership, explain what additional question you would ask about the evaluation text before accepting that conclusion, and describe what kind of new dataset would be needed if the answer makes the comparison inconclusive.
Hints
- Ask whether the validation portion was part of the original pretraining data.
- Think about which dataset could not have been included in pretraining.
Keeping the Evaluation Honest
The core of evaluating a GPT model is comparing its loss on training text with its loss on held-out validation text, both computed by the same function and model. The reading of that comparison depends on what the model has already seen. Each reported change to the setup has to be judged against the same baseline before anyone credits it with a result.
Key Takeaways
- Training and validation losses are computed with the same loss function and model, and only the data loader passed in differs.
- Close training and validation losses are consistent with no overfitting, and a slightly lower validation loss on a small dataset can be random noise.
- A close match cannot prove the absence of overfitting when the evaluation text may have been in pretraining; a new post-pretraining dataset is needed to measure overfitting in that case.
- Fine-tuning and input-format changes should each be judged against the same baseline, such as the 95.67% reference, since some changes improved accuracy while others lowered it.