3.3.7Deep Learning Frameworks

Saving and loading models (checkpoints)

2,603 words12 min readdifficulty · medium

The Core Problem: What Do We Actually Need to Save?

When training a neural network, we're updating millions (or billions) of parameters via gradient descent. The training state consists of:

  1. Model Parameters: The weights W\mathbf{W} and biases b\mathbf{b} for every layer
  2. Optimizer State: Momentum bufers (SGD with momentum), running averages (Adam's mtm_t, vtv_t), learning rate schedules
  3. Epoch/Step Counter: Where we are in training
  4. Random Seeds: For reproducibility
  5. Model Architecture (optional): The blueprint to reconstruct the network

Why all of this? Parameters alone define the model's predictions, but optimizer state is needed to continue training smoothly. If you only save weights and later resume with a fresh optimizer, you lose accumulated momentum—like a car suddenly starting from zero velocity instead of cruising speed.


Framework-Specific Approaches

PyTorch: State Dictionaries

Why this design? Separation of architecture (code) and parameters (data). Your model class can evolve, but old checkpoints remain loadable if layer names match.

import torch
import torch.nn as nn
 
# Define model
model = nn.Sequential(
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 10)
)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
 
# After training for N epochs...
checkpoint = {
    'epoch': epoch,
    'model_state_dict': model.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
    'loss': train_loss,
    'accuracy': val_acc
}
torch.save(checkpoint, 'model_epoch_10.pt')

Why this step? We bundle everything needed to resume training into a single dict, then serialize with torch.save (uses Python's pickle under the hood).

Loading back:

checkpoint = torch.load('model_epoch_10.pt')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
start_epoch = checkpoint['epoch'] + 1

Why load_state_dict and not direct assignment? It validates tensor shapes and missing/unexpected keys, catching architecture mismatches.


TensorFlow/Keras: Two Paradigms

1. TensorFlow Checkpoints (.ckpt)

Saves only weights as binary files + an index:

model = tf.keras.Sequential([...])
checkpoint_path = "training_1/cp-{epoch:04d}.ckpt"
checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
    filepath=checkpoint_path,
    save_weights_only=True,
    save_freq='epoch'
)
model.fit(x_train, y_train, epochs=10, callbacks=[checkpoint_callback])
 
# Load
model.load_weights('training_1/cp-010.ckpt')

Why save_weights_only=True? Faster saves, smaller files. Requires you to define the model architecture separately.

2. SavedModel Format (Full Model)

model.save('my_model')  # Creates a directory with architecture + weights
loaded_model = tf.keras.models.load_model('my_model')

Why this format? Bundles architecture, weights, and even the computational graph—no need for the original code. Ideal for production deployment with TensorFlow Serving.


Checkpoint Strategies: When and What to Save

Strategy 1: Save Only the Best Model

# PyTorch example
best_acc = 0.0
for epoch in range(num_epochs):
    val_acc = evaluate(model, val_loader)
    if val_acc > best_acc:
        best_acc = val_acc
        torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'accuracy': val_acc
        }, 'best_model.pt')

Why this works? Most experiments only need the peak performance model. Intermediate checkpoints are wasted storage.

Strategy 2: Save Every N Epochs + Keep Last K

# Keras callback
checkpoint_callback = ModelCheckpoint(
    filepath='model_{epoch:02d}_{val_loss:.2f}.h5',
    save_best_only=False,
    save_freq='epoch',
    period=5  # Every 5 epochs
)

Then manually prune old checkpoints or use a rolling window (keep only the last 3).

Why? Long training runs benefit from fallback points. If validation loss spikes at epoch 47, you can reload from epoch 45.

Strategy 3: Save on Plateau or Early Stopping

from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
 
early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)
checkpoint = ModelCheckpoint('checkpoint.h5', monitor='val_loss', save_best_only=True)

Why patience=10? Validation metrics are noisy. A single bad epoch shouldn't trigger a stop. Patience gives the model time to escape local minima.


Derivation: Why Optimizer State Matters

Consider SGD with momentum:

vt=βvt1+(1β)θLθt=θt1αvt\begin{align} \mathbf{v}_t &= \beta \mathbf{v}_{t-1} + (1-\beta) \nabla_\theta \mathcal{L} \\ \theta_t &= \theta_{t-1} - \alpha \mathbf{v}_t \end{align}

Where:

  • vt\mathbf{v}_t is the velocity (moving average of gradients)
  • β\beta is the momentum coefficient (typically 0.9)
  • α\alpha is the learning rate

What happens if you only save θt\theta_t and restart?

At resume, vt\mathbf{v}_t resets to 0\mathbf{0}. The first step becomes vanilla SGD:

θt+1=θtαθL\theta_{t+1} = \theta_t - \alpha \nabla_\theta \mathcal{L}

Why is this bad? Momentum accumulates gradient information over many steps. Resetting it means:

  1. Loss of directional inertia: The model "forgets" which direction was promising
  2. Slower convergence: Takes ~1/(1β)1/(1-\beta) steps to rebuild momentum (11 steps for β=0.9\beta=0.9)
  3. Unstable training: If you're in a sharp valley, the first few steps might diverge

The fix: Save optimizer.state_dict() which includes vt\mathbf{v}_t for every parameter.


Example 1: Resuming Training After Crash

Figure — Saving and loading models (checkpoints)
import torch
import torch.nn as nn
 
# Training loop with checkpointing
def train_with_checkpoints(model, optimizer, train_loader, num_epochs, checkpoint_dir):
    start_epoch = 0
    # Try to resume from latest checkpoint
    checkpoint_files = sorted(Path(checkpoint_dir).glob('checkpoint_*.pt'))
    if checkpoint_files:
        latest = checkpoint_files[-1]
        checkpoint = torch.load(latest)
        model.load_state_dict(checkpoint['model_state_dict'])
        optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
        start_epoch = checkpoint['epoch'] + 1
        print(f"Resumed from epoch {start_epoch}")
    for epoch in range(start_epoch, num_epochs):
        for batch in train_loader:
            loss = train_step(model, batch, optimizer)
        # Save checkpoint every epoch
        torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'loss': loss.item()
        }, f'{checkpoint_dir}/checkpoint_{epoch:04d}.pt')

Why start_epoch adjustment? Prevents repeating work. If we crashed at epoch 7, we pick up at epoch 8.

Why sort and take [-1]? Files might be listed out of order. Sorting by name (with zero-paded numbers) ensures we load the truly latest checkpoint.


Example 2: Exporting for Inference (No Optimizer)

# Training complete, save inference-only checkpoint
torch.save({
    'model_state_dict': model.state_dict(),
    'accuracy': final_test_acc,
    'class_names': ['cat', 'dog', 'bird']
}, 'production_model.pt')
 
# Load in production
checkpoint = torch.load('production_model.pt', map_location='cpu')
model = MyModel()  # Architecture must match
model.load_state_dict(checkpoint['model_state_dict'])
model.eval()  # Disable dropout/batchnorm training mode
 
# Inference
with torch.no_grad():
    predictions = model(new_data)

Why map_location='cpu'? The model was trained on GPU (CUDA). Loading directly on a CPU server would fail without this flag.

Why model.eval()? Dropout and BatchNorm behave differently during training (stochastic) vs. inference (deterministic). Forgetting this is a common deployment bug.



Advanced: Distributed Training Checkpoints

When training across multiple GPUs, each device holds a shard of the model. Checkpointing requires coordination:

# PyTorch DistributedDataParallel
if torch.distributed.get_rank() == 0:  # Only master process saves
    torch.save({
        'model_state_dict': model.module.state_dict(),  # .module unwraps DP
        'optimizer_state_dict': optimizer.state_dict()
    }, 'checkpoint.pt')

Why only rank 0? All GPUs have identical parameters (synchronized via AllReduce). Saving redundantly wastes I/O.

Why .module? DP wraps your model in a DistributedDataParallel object. The actual model is model.module.


Recall Explain This to a 12-Year-Old (Feynman)

Imagine you're building a massive LEGO castle over a week. Each day, you add towers, walls, moats. Now, what if your little sibling knocks it over on day 5? If you took photos each evening (checkpoints), you can rebuild to day 4's state in an hour instead of starting from scratch.

In deep learning, the castle is your model's "brain" (its weights). Training is like slowly adjusting millions of tiny LEGO pieces to make the castle perfect. Checkpoints are those photos—saved files of all the pieces' positions. If your computer crashes (sibling attack!), you reload the photo and keep building. Without checkpoints, a crash means starting over, which could cost days of computer time.

The optimizer state is like your memory of "where I was planning to add the next tower." If you lose that memory, you'll rebuild fine, but you might waste time rediscovering your plan.


Connections

  • Overfitting and Regularization: Checkpoints enable early stopping by saving the model before validation loss diverges.
  • Learning Rate Schedules: LR schedulers depend on epoch counters; checkpoints must preserve this state.
  • Distributed Training: Multi-GPU setups require synchronized checkpointing to avoid data corruption.
  • Model Deployment: Production models are loaded from checkpoints; architecture mismatches cause runtime failures.
  • Transfer Learning: Pretrained models (BERT, ResNet) are just checkpoints from someone else's training run.

#flashcards/ai-ml

What is a checkpoint in deep learning? :: A saved snapshot of a model's parameters (weights, biases), optimizer state, and training metadata (epoch, loss) to disk, allowing training resumption or deployment.

Why save optimizer state in addition to model weights?
Optimizers like Adam maintain running averages (momentum bufers). Without them, resuming training loses accumulated gradient information, slowing convergence and potentially destabilizing the run.
What is the difference between torch.save(model) and torch.save(model.state_dict()?
Saving the entire model pickles the Python object (architecture + weights), tightly coupling it to the code version. Saving the state dict stores only parameters, allowing architecture evolution as long as layer names match.
Why use model.eval() in PyTorch before inference?
It disables training-specific behaviors like Dropout (which randomly zeros activations) and sets BatchNorm to use running statistics instead of batch statistics. Forgetting this causes incorrect predictions.
What does strict=False do in model.load_state_dict(checkpoint, strict=False)?
It allows loading checkpoints with missing or unexpected keys (e.g., after adding/removing layers), copying only matching parameters. Useful for architecture changes, but risky—missing weights initialize randomly.
In distributed training, why does only rank 0 save the checkpoint?
All GPUs hold identical parameters (synchronized via AllReduce after each step). Saving from multiple ranks would write the same data redundantly, wasting I/O bandwidth and risking file corruption from concurrent writes.
What is the purpose of map_location='cpu' when loading a PyTorch checkpoint?
It loads GPU-trained models onto CPU devices. Without it, PyTorch tries to load tensors onto the original CUDA device, failing if no GPU is available.
What is TensorFlow's SavedModel format used for?
It bundles model architecture, weights, and computation graph into a directory, enabling deployment without the original Python code. Ideal for production serving with TensorFlow Serving orFLite.
Why might saving checkpoints every epoch be wasteful?
Large models (e.g., 500M parameters ≈ 2GB) saved 100 times consume 200GB. Most experiments only need the best model, making intermediate checkpoints dead weight.
What does "patience" mean in early stopping?
The number of epochs to wait for improvement before stopping training. E.g., patience=10 means "if validation loss doesn't improve for 10 consecutive epochs, stop." Prevents stopping on temporary plateaus.

Concept Map

snapshots

includes

includes

includes

includes

enables

enables

preserves

needed for

PyTorch uses

Keras uses

separates

loaded via

Checkpoints

Training State

Model Parameters W and b

Optimizer State

Epoch Step Counter

Random Seeds

Resume Training

Deploy Best Model

Accumulated Momentum

State Dicts

TF Checkpoints .ckpt

Architecture from Data

load_state_dict validates shapes

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Deep learning mein checkpoints ka concept bahut simple hai lekin bahut powerful. Jab ap ek bada model train karte ho—jaise image classification ke liye ResNet ya language model ke liye GPT—toh yeh training 2-3 din tak chal sakti hai. Agar bech mein light chali jaye, system crash ho, ya apko kisi urgent kaam ke liye training rok dena pade, toh checkpoint apko bachata hai. Yeh basically ek "save point" hai jaise video game mein hota hai—ap wahan se dobara shuru kar sakte ho bina starting se repeat kiye.

Checkpoint mein teen chezein save hoti hain: pehli, model ke sare weights aur biases (yeh woh numbers hain jo neurons ko connect karte hain); dosri, optimizer ki state (jaise Adam optimizer mein momentum bufers jo yad rakhte hain ki gradients kis direction mein ja rahe the); aur tesri, metadata jaise epoch number aur loss values. Agar aap sirf weights save karoge aur optimizer state nahi, toh training resume karne par model ko fir se momentum build karna padega, jisse 10-15 epochs waste ho sakte hain. Isliye PyTorch mein torch.save() se pora dictionary save karte hain jismein sab kuch hota hai.

Production mein jab model deploy karna ho, tab usually sirf weights save karte hain kyunki optimizer ki zarurat nahi hoti—bas inference karna hai. Lekin deployment se pehle ek aur important step hai: model.eval() call karna, warna Dropout layers active rahenge aur predictions galat ayenge. TensorFlow mein SavedModel format use karte hain jo architectureur weights dono ko bundle kar deta hai, toh bad mein code change hone par bhi model load ho sakta hai.

Ek smart strategy yeh hai ki har epoch pe checkpoint mat save karo (kyunki disk space waste hoga), balki ya toh best validation accuracy wala model save karo, ya har5-10 epochs mein ek checkpoint rakho aur purane delete karte raho. Isse ap storage bachate ho aur zarurat padne par bhi recovery point mil jaata hai. Checkpointing ka matlab hai ki aapki mehnat kabhi waste nahi hoti—chahe koi bhi emergency aa jaye.

Go deeper — visual, from zero

Test yourself — Deep Learning Frameworks

Connections