PyTorch Model Checkpointing

Checkpointing saves the state of your model and training progress to disk at regular intervals. If training crashes, you restart from the last checkpoint instead of the beginning. Checkpointing also lets you save the best-performing model during training so you can always recover peak performance even if later epochs cause overfitting.

What to Save in a Checkpoint

Minimum (inference only):
  model.state_dict()   ← all weights and biases

Full training checkpoint (resumable):
  model.state_dict()
  optimizer.state_dict()   ← optimizer momentum, learning rate state
  scheduler.state_dict()   ← scheduler progress
  epoch                    ← where to resume from
  best_val_loss            ← for early stopping logic

state_dict — What It Is

A state dict is an ordered dictionary that maps each layer name to its parameters (weights and biases). It does not store the model architecture — only the learned values. You must recreate the model class before loading a state dict into it.

import torch.nn as nn

model = nn.Sequential(
    nn.Linear(4, 8),
    nn.ReLU(),
    nn.Linear(8, 2)
)

sd = model.state_dict()
for k, v in sd.items():
    print(f"{k}: {v.shape}")
# 0.weight: torch.Size([8, 4])
# 0.bias:   torch.Size([8])
# 2.weight: torch.Size([2, 8])
# 2.bias:   torch.Size([2])

Saving a Model

import torch

# Save weights only (for inference)
torch.save(model.state_dict(), 'model_weights.pth')

# Save full checkpoint (for resuming training)
checkpoint = {
    'epoch': 25,
    'model_state': model.state_dict(),
    'optimizer_state': optimizer.state_dict(),
    'scheduler_state': scheduler.state_dict(),
    'best_val_loss': 0.142
}
torch.save(checkpoint, 'checkpoint_epoch25.pth')

Loading a Model

# Load weights only
model = MyModel()   # must recreate architecture first
model.load_state_dict(torch.load('model_weights.pth'))
model.eval()

# Load full checkpoint to resume training
checkpoint = torch.load('checkpoint_epoch25.pth')
model.load_state_dict(checkpoint['model_state'])
optimizer.load_state_dict(checkpoint['optimizer_state'])
scheduler.load_state_dict(checkpoint['scheduler_state'])
start_epoch = checkpoint['epoch'] + 1
best_val_loss = checkpoint['best_val_loss']

print(f"Resuming from epoch {start_epoch}")

Save Best Model During Training

best_val_loss = float('inf')

for epoch in range(100):
    train_one_epoch(model, train_loader, optimizer, loss_fn)

    val_loss = evaluate(model, val_loader, loss_fn)

    if val_loss < best_val_loss:
        best_val_loss = val_loss
        torch.save(model.state_dict(), 'best_model.pth')
        print(f"  ✓ Saved best model at epoch {epoch+1} (val_loss={val_loss:.4f})")

# After training: load the best-performing checkpoint
model.load_state_dict(torch.load('best_model.pth'))

Periodic Checkpointing

for epoch in range(100):
    train_one_epoch(model, train_loader, optimizer, loss_fn)

    # Save every 10 epochs
    if (epoch + 1) % 10 == 0:
        ckpt = {
            'epoch': epoch,
            'model_state': model.state_dict(),
            'optimizer_state': optimizer.state_dict()
        }
        torch.save(ckpt, f'checkpoint_epoch{epoch+1}.pth')
        print(f"  Saved checkpoint at epoch {epoch+1}")

Loading on a Different Device

If a checkpoint was saved on a GPU and you want to load it on a CPU (or a different GPU), use the map_location argument.

# Saved on GPU, loading on CPU
model.load_state_dict(
    torch.load('model_weights.pth', map_location=torch.device('cpu'))
)

# Load to current device automatically
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.load_state_dict(
    torch.load('model_weights.pth', map_location=device)
)

Checkpoint Naming Strategy

Good naming conventions:
  best_model.pth               ← always the best so far
  checkpoint_epoch_025.pth     ← periodic saves with zero-padded epoch
  checkpoint_final.pth         ← saved at end of training

Folder approach for long experiments:
  checkpoints/
  ├── best.pth
  ├── epoch_010.pth
  ├── epoch_020.pth
  └── epoch_030.pth

Full Checkpoint Pattern — End to End

import torch
import os

def save_checkpoint(model, optimizer, epoch, val_loss, path):
    torch.save({
        'epoch': epoch,
        'model_state': model.state_dict(),
        'optimizer_state': optimizer.state_dict(),
        'val_loss': val_loss
    }, path)

def load_checkpoint(path, model, optimizer):
    ckpt = torch.load(path)
    model.load_state_dict(ckpt['model_state'])
    optimizer.load_state_dict(ckpt['optimizer_state'])
    return ckpt['epoch'], ckpt['val_loss']

# Resume if checkpoint exists
start_epoch = 0
ckpt_path   = 'latest.pth'

if os.path.exists(ckpt_path):
    start_epoch, _ = load_checkpoint(ckpt_path, model, optimizer)
    print(f"Resumed from epoch {start_epoch}")

for epoch in range(start_epoch, 100):
    # ... training ...
    save_checkpoint(model, optimizer, epoch, val_loss, ckpt_path)

Summary

Checkpointing saves model weights and training state to disk. Use torch.save(model.state_dict(), path) for inference-only saves. Save the full checkpoint dictionary — model, optimizer, scheduler states, epoch, and best metric — when you need to resume training. Save the best model separately whenever validation loss improves. Load checkpoints with model.load_state_dict(torch.load(path)) after recreating the model architecture. Use map_location when loading across different devices. Checkpointing protects weeks of training from hardware failures and gives you the best-performing model at the end regardless of when overfitting starts.

Leave a Comment

Your email address will not be published. Required fields are marked *