The Story — Save Points in a Video Game
Training on a cloud machine is the same. Machines get restarted, notebooks time out, jobs get pre-empted. tf.train.Checkpoint is your save point. It stores the model's weights, the optimizer's memory, and your step counter, so training continues as if nothing happened.
What Goes Into a Checkpoint?
A checkpoint saves every tf.Variable reachable from the objects you give it. That is why it works with anything built on tf.Module.
import tensorflow as tf
import numpy as np, os, shutil
shutil.rmtree("/tmp/ckpt", ignore_errors=True)
rng = np.random.default_rng(0)
X = rng.normal(size=(256, 3)).astype("float32")
y = (X @ np.array([[2.0], [-1.0], [0.5]], dtype="float32") + 0.1 * rng.normal(size=(256, 1))).astype("float32")
def build():
tf.random.set_seed(0)
model = tf.keras.Sequential([tf.keras.Input((3,)), tf.keras.layers.Dense(1)])
opt = tf.keras.optimizers.SGD(0.05, momentum=0.9)
step = tf.Variable(0, dtype=tf.int64, name="step")
return model, opt, step
model, opt, step = build()
ckpt = tf.train.Checkpoint(model=model, optimizer=opt, step=step)
manager = tf.train.CheckpointManager(ckpt, directory="/tmp/ckpt", max_to_keep=3)
@tf.function
def train_step():
with tf.GradientTape() as tape:
loss = tf.reduce_mean((model(X) - y) ** 2)
grads = tape.gradient(loss, model.trainable_variables)
opt.apply_gradients(zip(grads, model.trainable_variables))
step.assign_add(1)
return loss
for _ in range(50):
loss = train_step()
if int(step) % 10 == 0:
path = manager.save(checkpoint_number=step)
print(f"step {int(step):3d} loss {float(loss):.4f} saved {os.path.basename(path)}")
print("kept:", [os.path.basename(p) for p in manager.checkpoints])
print("files:", sorted(os.listdir("/tmp/ckpt")))
ckpt-N.index says which variable is where. ckpt-N.data-00000-of-00001 holds the actual numbers. The small checkpoint text file remembers which one is the latest. With max_to_keep=3, older saves are deleted automatically.
The Crash — and the Resume
We pretend the machine restarted: we throw away every Python object and build a brand-new model and optimizer. Then we restore.
del model, opt, step, ckpt, manager # "the machine restarted"
model, opt, step = build() # fresh, random objects
ckpt = tf.train.Checkpoint(model=model, optimizer=opt, step=step)
manager = tf.train.CheckpointManager(ckpt, "/tmp/ckpt", max_to_keep=3)
print("latest:", os.path.basename(manager.latest_checkpoint))
status = ckpt.restore(manager.latest_checkpoint)
print("step after restore:", int(step))
@tf.function
def train_step():
with tf.GradientTape() as tape:
loss = tf.reduce_mean((model(X) - y) ** 2)
grads = tape.gradient(loss, model.trainable_variables)
opt.apply_gradients(zip(grads, model.trainable_variables))
step.assign_add(1)
return loss
for _ in range(10):
loss = train_step()
status.assert_consumed() # every saved value found a home
print(f"continued to step {int(step)}, loss {float(loss):.5f}")
print("weights:", model.layers[0].kernel.numpy().ravel().round(3))
The step counter continued from 50, and the loss is still tiny, so the learned weights came back. The optimizer's momentum was restored too. assert_consumed() checks that every saved value was matched to a variable. Use expect_partial() instead when you deliberately restore only part of a checkpoint (for example, just the model for inference).
Look Inside a Checkpoint
for name, shape in tf.train.list_variables(manager.latest_checkpoint):
if not name.startswith("_CHECKPOINTABLE") and "OBJECT_CONFIG" not in name:
print(f"{name:70s} {shape}")
The names are paths through your objects. Each variable is stored once, under the first path TensorFlow finds. Here the Dense kernel [3, 1] and bias [1] show up as optimizer/_trainable_variables/0 and /1, because the optimizer also points to them. _variables/2 and /3 are the momentum slots, and step is our counter. Restore matches values to variables by this object structure, not by Python variable names.
The Standard Pattern: Restore If Possible
ckpt = tf.train.Checkpoint(model=model, optimizer=opt, step=step)
manager = tf.train.CheckpointManager(ckpt, "ckpts/run1", max_to_keep=5)
ckpt.restore(manager.latest_checkpoint) # does nothing if there is none yet
print("starting at step", int(step))
while int(step) < total_steps:
train_step()
if int(step) % 500 == 0:
manager.save(checkpoint_number=step)
Try It — Crash and Resume Simulator
Set how often to save and how many checkpoints to keep, then press Train. Press ⚡ Crash! at any moment. You lose the work since the last save. Press Resume to restore the latest checkpoint and continue.
/ckpts on disk
log
Checkpoint or SavedModel?
| tf.train.Checkpoint | tf.saved_model.save / model.export | |
|---|---|---|
| Stores | Variable values only | Variables and the computation graph (tf.functions) |
| Needs your Python code to load? | Yes — rebuild the objects, then restore | No — works in C++, TF Serving, LiteRT |
| Keeps optimizer state | Yes (if you include it) | Usually not needed |
| Best for | Resuming training, keeping the best epoch | Deploying the finished model |
Keep a second manager in a "best" folder with max_to_keep=1. Save to it only when validation loss improves. At the end, restore it — that is "restore best weights" that also survives a crash.
Golden Rules
CheckpointManager with max_to_keep so the disk does not fill up.restore(manager.latest_checkpoint). It is safe when no checkpoint exists.