Saving and Loading Model Checkpoints
Fine-tuning a saved GPT model starts by restoring two pieces of state: the model and the optimizer, both stored in one checkpoint file.
Why Training Gets Resumed
Training a large language model is a long process, and a training run does not always finish in one sitting. Training a GPT model is expensive, so you rarely want to throw away the work already done. Instead of treating every training session as a fresh start, the progress made so far can be stored and then picked up again later. This picking up is called fine-tuning a saved model: you continue training for more passes over the data, starting from a saved point rather than from scratch.
The source describes the whole job in one sentence: load the model and the optimizer that were saved in the main chapter. This article unpacks what that sentence requires, one piece at a time, and explains why the order of the steps matters. The central question is simple. When you stop training and come back later, what exactly has to survive the break, and what has to be restored before training can carry on from where it left off?
Following the Weights Through a Break
Saving is the act of writing the current weights to storage. Saving model weights stores the learned values of a model so that they can be used later. Loading is the reverse: reading previously saved weights back in, so the model starts from that stored state rather than from scratch. The source gives both operations the same purpose, which is to continue training an LLM.
The two operations work best when you think of them as a pair. A save without a later load stores the progress but never puts it back to work. A load is only meaningful when there is a saved set of weights to restore. The diagram below traces the weights through one full break in training, from their random starting values to one extra epoch after they have been restored.
The important comparison is between the restored weights and a randomly initialized model. A randomly initialized model starts with arbitrary values and has learned nothing yet. The restored model carries the values that were learned before the break. When training resumes from the restored state, it continues from the point where the saved weights left off rather than from those random initial values.
The Two Parts Inside One Checkpoint
A saved checkpoint holds two separate pieces of state, both stored together in one checkpoint file. The first is the state of the GPT model, which is its learned parameters. The second is the state of the optimizer, which in this workflow is torch.optim.AdamW. The optimizer keeps its own internal state alongside the model. Restoring only the model would leave the optimizer behaving as though it had never run, so the optimizer's saved state has to come back too.
The loading code reads the checkpoint into a single object using torch.load(). That object is a dictionary, and you pull two entries out of it by key. The key model_state_dict belongs to the model, and the key optimizer_state_dict belongs to the optimizer. These two keys are the whole contract between saving and loading. If something goes wrong when you resume, they are the first place to look.
Model state: the learned parameters of the GPT model, stored under the key model_state_dict. Optimizer state: the internal state of the AdamW optimizer, stored under the key optimizer_state_dict. Checkpoint: the single file that holds both of these together.
Matching Checkpoint Entries to Load Calls
Each entry must reach a specific destination, and one call cannot serve both. The entry model_state_dict feeds model.load_state_dict(), which fills the model. The entry optimizer_state_dict feeds optimizer.load_state_dict(), which fills the optimizer. The mapping is one entry to one call, and it is the wiring you need to hold in your head before reading any resume code.
A mismatch shows up at the loading call that receives the wrong entry. If a key is wrong, or the entry does not match the object it is loaded into, the failure appears where that load call runs, not earlier in the script. That is why the two keys and the two load calls are the places to check first.
What do you think happens?
Which checkpoint entry should be passed to optimizer.load_state_dict()?
Reveal answer
optimizer_state_dict
The model entry belongs to the model and the optimizer entry belongs to the optimizer. Each entry must reach its own destination, so one call cannot serve both.
Rebuilding the Model and Optimizer First
The saved state is a set of values, not a running model. Something has to exist to receive those values. The loading sequence therefore creates a fresh GPTModel using the configuration named GPT_CONFIG_124M, and only then calls model.load_state_dict() with the model_state_dict entry. Creating the object first gives the loading call a structure to fill in.
The optimizer follows the same pattern. It is constructed first, as torch.optim.AdamW applied to the parameters of the freshly loaded model, with a learning rate of 5e-4 and a weight decay of 0.1. Only after that is optimizer.load_state_dict() called with the optimizer_state_dict entry. The order matters because the optimizer is built from the model's parameters, so the model must already exist at that point.
torch.load()reads the whole checkpoint into one dictionary- The model is constructed before its saved values are loaded into it
- The optimizer is built from the restored model's parameters, then given its saved state
- Training resumes from the restored state for one more epoch
Resuming a Run Step by Step
Resuming Fine-Tuning from a Saved Checkpoint
A training run saved a checkpoint file holding model_state_dict and optimizer_state_dict. Describe the steps needed to continue training for one more epoch from that saved point.
- 1Read the checkpoint
Call
torch.load()to read the file into a single dictionary. Nothing is trained yet at this point. - 2Rebuild the model
Create a fresh GPTModel using GPT_CONFIG_124M. This gives the saved values a structure to fill.
- 3Restore the model
Call
model.load_state_dict()with themodel_state_dictentry. The model now holds the learned parameters from the save. - 4Rebuild the optimizer
Create torch.optim.AdamW from the restored model's parameters, with learning rate 5e-4 and weight decay 0.1.
- 5Restore the optimizer
Call
optimizer.load_state_dict()with theoptimizer_state_dictentry, so the optimizer keeps its internal state. - 6Continue training
Call
train_simple_functionwithnum_epochs=1. Training starts from the restored state, not from random initial values.
The model and the optimizer both begin from their saved states, so one additional epoch of training picks up where the earlier run stopped.
Training One More Epoch
Once the model and optimizer are restored, the final step is to continue training. The source calls for train_simple_function with num_epochs=1. That single call trains the model for another epoch, starting from the restored state. Over that one epoch, the weights are updated from their starting values, which are the restored values, toward whatever values the continued training produces at its end.
Mistakes That Make a Resumed Run Start Over
Common mistake Restoring only the model and skipping the optimizer
What it looks likeThe script calls model.load_state_dict() with model_state_dict but never calls optimizer.load_state_dict().
Why it failsThe optimizer keeps its own internal state alongside the model. Without its saved state, the optimizer behaves as though it had never run.
Do this insteadRestore both entries: model_state_dict into the model and optimizer_state_dict into the optimizer.
Common mistake Loading a state into an object that has not been built yet
What it looks likeThe script tries to load the saved values before creating the GPTModel or the optimizer.
Why it failsThe saved state is a set of values, not a running model, so nothing exists yet to receive them.
Do this insteadConstruct the GPTModel from GPT_CONFIG_124M first, then load model_state_dict, then construct the optimizer and load optimizer_state_dict.
Common mistake Building the optimizer before the model exists
Why it failsThe optimizer is created from the parameters of the freshly loaded model, so the model must already exist.
Do this insteadBuild the model first, load its state, and only then create torch.optim.AdamW from model.parameters().
Common mistake Swapping the two checkpoint entries
What it looks likePassing optimizer_state_dict to model.load_state_dict() and model_state_dict to optimizer.load_state_dict().Why it failsEach entry must reach its own destination, and one call cannot serve both.
Do this insteadMap model_state_dict to model.load_state_dict() and optimizer_state_dict to optimizer.load_state_dict().Common mistake Saving the weights without ever loading them back, so the stored progress is never put to work
| Aspect | Correctly resumed run | Run with weights or optimizer not restored |
|---|---|---|
| Model weights | Start from the saved learned values | Start from fresh or random initial values |
| Optimizer | Keeps its saved internal state | Behaves as though it had never run |
| Training behavior | Continues from the point where the earlier run stopped | Behaves as if training started over |
| Load order | Model and optimizer are built before their states are loaded | A state is loaded before its object exists, or a piece is skipped |
What differs between a correctly resumed run and one where state was not restored
Practice Resuming Fine-Tuning
A colleague saved a checkpoint with model_state_dict and optimizer_state_dict. Write the resume sequence that rebuilds the GPT model from GPT_CONFIG_124M, restores the model, builds the AdamW optimizer with learning rate 5e-4 and weight decay 0.1, restores the optimizer, and then trains for one additional epoch. Say which checkpoint entry feeds each load call.
Hints
- Start with
torch.load()to read the checkpoint into one dictionary. - Construct the GPT model before calling
load_state_dicton it. - Build the AdamW optimizer from the restored model's parameters before loading its state.
- The training call uses
num_epochs=1.
Key Takeaways
- A checkpoint holds two pieces of state, the model and the optimizer, and both must be restored before training continues.
- The checkpoint is read with
torch.load(). Themodel_state_dictentry feedsmodel.load_state_dict(), and theoptimizer_state_dictentry feedsoptimizer.load_state_dict(). - The model is rebuilt from GPT_CONFIG_124M and the AdamW optimizer is built from its parameters before their saved states are loaded, because a saved state is a set of values that needs an object to fill.
- Saving stores learned weights and loading reads them back, and the two work as a pair. Training then continues with
train_simple_functionandnum_epochs=1 from the restored state.