Training Checkpoints¶
Resumable training state, so a Colab disconnect costs minutes rather than the whole run.
Two different kinds of checkpoint¶
TorchLingo has two modules with "checkpoint" in the name. They answer different questions, and picking the wrong one is the easy mistake:
torchlingo.checkpoint |
torchlingo.training_checkpoint |
|
|---|---|---|
| Saves | a finished model for inference | training state for resuming |
| Contents | weights, architecture config, tokenizers | weights, optimizer, scheduler, epoch, step, loss history |
| Use when | you are done training and want a portable file | you are mid-training and might get interrupted |
A training checkpoint is not a substitute for the other: it does not carry the tokenizers, so it is not portable to someone else's machine. Save both.
Quick Start¶
from torchlingo.training import train_model
from torchlingo.training_checkpoint import (
TrainingCheckpointer, default_checkpoint_dir, mount_drive
)
mount_drive() # mounts Google Drive in Colab; a no-op anywhere else
checkpointer = TrainingCheckpointer(
"my-experiment",
checkpoint_dir=default_checkpoint_dir("my-experiment"),
save_every_seconds=300,
)
result = train_model(model, train_loader, val_loader,
num_epochs=20, checkpointer=checkpointer)
Run that cell again after a disconnect and training resumes from the last save.
There is no separate "resume" call: train_model checks for a checkpoint and
picks up from it when one exists.
In Colab, point it at Drive
default_checkpoint_dir does this for you: a folder under MyDrive when
running in Colab, a local checkpoints/ directory otherwise. A checkpoint
written to the Colab runtime's own disk dies with the runtime, which defeats
the purpose.
What gets saved¶
Two files, and only two:
| File | Written |
|---|---|
latest.pt |
every automatic save, overwritten in place |
best.pt |
only when validation loss improves |
Keeping exactly two is deliberate. On Drive, a growing pile of numbered checkpoints is how a student silently fills their quota and starts getting write failures mid-run.
Saving the optimizer matters more than it looks. Adam carries per-parameter moment estimates; resuming from weights alone throws those away and the loss visibly jumps as the optimizer rebuilds them. The scheduler is saved for the same reason.
When it saves¶
- Every
save_every_seconds(default 600), or everysave_every_stepsif you set that instead. Set either to0to disable that trigger. - Always at the end of an epoch, regardless of the interval, because an epoch is the natural resume point and the cost is small relative to one.
Failure behavior¶
Writes go to a temporary file and are then moved into place. A runtime that dies
mid-write cannot leave a half-written latest.pt — which would fail at load
time, exactly when the work it was protecting is already gone.
If a checkpoint cannot be loaded (truncated, or written by a different model
architecture), train_model reports it and starts fresh rather than raising.
Failing to resume should cost you the history, not the ability to train.
The Colab path is not covered by CI
GitHub's runners have no Google Drive to mount, so is_colab, mount_drive
and the Drive-backed paths cannot be exercised automatically. Everything
else here is tested, including save, resume, interval logic and corruption
handling. The Drive integration has been written and reviewed carefully but
has not been run in a live Colab session — if you use it there, please report
what happens.
API Reference¶
training_checkpoint
¶
Resumable training checkpoints, and surviving a Colab disconnect.
A Colab session ends when the browser tab closes, the runtime idles out, or the laptop lid shuts. Without periodic checkpoints that takes the whole training run with it, which is a bad first experience for a student and an avoidable one.
This is not the same thing as :mod:torchlingo.checkpoint. That module saves
a finished model for inference: weights, architecture config and tokenizers in
one portable file. This module saves training state so a run can pick up where
it stopped: optimizer, scheduler, epoch, step, and the loss history. The two
answer different questions and deliberately stay separate.
Typical usage in Colab. Skipped under --doctest-modules because it is an
illustration rather than a runnable example: model and loader are the
reader's own, and mount_drive() returns a different answer inside Colab than
outside it, so there is no single correct output to assert.
>>> from torchlingo.training_checkpoint import ( # doctest: +SKIP
... TrainingCheckpointer, default_checkpoint_dir, mount_drive
... )
>>> mount_drive() # doctest: +SKIP
>>> checkpointer = TrainingCheckpointer( # doctest: +SKIP
... "my-experiment", checkpoint_dir=default_checkpoint_dir("my-experiment")
... )
>>> result = train_model(model, loader, checkpointer=checkpointer) # doctest: +SKIP
Re-running that same cell after a disconnect resumes from the last save.
Note
The Colab and Google Drive paths here cannot be exercised by CI — GitHub runners have no Drive to mount, so everything in this module is tested except the part that actually talks to Drive.
That gap was closed by hand instead. Verified in a live Colab session on
2026-09-23: Drive mounts, checkpoints land under MyDrive, and an
interrupted runtime resumes from the last save rather than restarting from
epoch 0 — which is the behaviour the module exists for, and the one no unit
test here can demonstrate.
TrainingCheckpointer
¶
TrainingCheckpointer(experiment_name: str, checkpoint_dir: Path | str | None = None, save_every_seconds: float = 600.0, save_every_steps: int = 0, verbose: bool = True)
Save and restore training state, periodically and on demand.
Writes two files: latest.pt, overwritten each save, and best.pt,
written only when the caller says this is the best result so far. Keeping
exactly two is deliberate — on Drive, a growing pile of numbered checkpoints
is how a student silently fills their quota.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
experiment_name
|
str
|
Name the checkpoints are filed under. |
required |
checkpoint_dir
|
Path | str
|
Where to write. Defaults to
:func: |
None
|
save_every_seconds
|
float
|
Minimum wall-clock gap between automatic saves. Set to 0 to disable time-based saving. |
600.0
|
save_every_steps
|
int
|
Minimum step gap between automatic saves. Set to 0 to disable step-based saving. |
0
|
verbose
|
bool
|
Print when saving, loading and resuming. |
True
|
Attributes:
| Name | Type | Description |
|---|---|---|
checkpoint_dir |
Path
|
Resolved directory, created on construction. |
state |
CheckpointState
|
Current training progress. |
Examples:
>>> import tempfile
>>> with tempfile.TemporaryDirectory() as tmp:
... ckpt = TrainingCheckpointer("demo", checkpoint_dir=tmp, verbose=False)
... ckpt.has_checkpoint()
False
Source code in src/torchlingo/training_checkpoint.py
path_for
¶
Return the path of the latest or best checkpoint.
Source code in src/torchlingo/training_checkpoint.py
has_checkpoint
¶
save
¶
save(model: Module, optimizer: Optimizer | None = None, scheduler: Any = None, *, is_best: bool = False) -> Path
Write the current training state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module
|
Model whose weights are saved. |
required |
optimizer
|
Optimizer
|
Saved so the run resumes with its momentum and step counts intact, not just its weights. |
None
|
scheduler
|
optional
|
Learning-rate scheduler, saved for the same reason. |
None
|
is_best
|
bool
|
Also write |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
Path |
Path
|
The path written. |
Source code in src/torchlingo/training_checkpoint.py
load
¶
load(model: Module, optimizer: Optimizer | None = None, scheduler: Any = None, which: str = 'latest', map_location: Any = 'cpu') -> CheckpointState
Restore training state in place and return it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module
|
Model to load weights into. |
required |
optimizer
|
Optimizer
|
Restored when present in the file. |
None
|
scheduler
|
optional
|
Restored when present in the file. |
None
|
which
|
str
|
|
'latest'
|
map_location
|
optional
|
Passed through to |
'cpu'
|
Returns:
| Name | Type | Description |
|---|---|---|
CheckpointState |
CheckpointState
|
The restored progress. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If no such checkpoint exists. |
Source code in src/torchlingo/training_checkpoint.py
should_save
¶
Report whether enough time or steps have passed to save again.
Source code in src/torchlingo/training_checkpoint.py
update
¶
update(*, epoch: int | None = None, global_step: int | None = None, train_loss: float | None = None, val_loss: float | None = None, metrics: dict[str, Any] | None = None) -> None
Record progress without writing anything to disk.
Source code in src/torchlingo/training_checkpoint.py
maybe_save
¶
maybe_save(model: Module, optimizer: Optimizer | None = None, scheduler: Any = None, *, global_step: int | None = None) -> Path | None
Save only if :meth:should_save says it is time.
This is what the training loop calls every step; the interval logic lives here so the loop stays readable.
Returns:
| Type | Description |
|---|---|
Path | None
|
Path | None: The path written, or None if nothing was due. |
Source code in src/torchlingo/training_checkpoint.py
CheckpointState
dataclass
¶
CheckpointState(epoch: int = 0, global_step: int = 0, best_val_loss: float = float('inf'), train_losses: list[float] = list(), val_losses: list[float] = list(), metrics: dict[str, Any] = dict(), timestamp: str = (lambda: isoformat())(), experiment_name: str = 'torchlingo')
Training progress, everything needed to resume a run.
Attributes:
| Name | Type | Description |
|---|---|---|
epoch |
int
|
Last completed epoch, 0-indexed. |
global_step |
int
|
Total optimizer steps taken. |
best_val_loss |
float
|
Best validation loss seen so far. |
train_losses |
list[float]
|
Training loss per epoch. |
val_losses |
list[float]
|
Validation loss per epoch. |
metrics |
dict
|
Any extra values the caller wants carried along. |
timestamp |
str
|
UTC ISO-8601 time the state was created. |
experiment_name |
str
|
Name the checkpoints are filed under. |
to_dict
¶
from_dict
classmethod
¶
from_dict(data: dict[str, Any]) -> CheckpointState
Rebuild state from a dictionary, ignoring unknown keys.
Unknown keys are dropped rather than raising, so a checkpoint written by a newer version still loads.
Source code in src/torchlingo/training_checkpoint.py
is_colab
¶
Report whether the current process is running inside Google Colab.
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True when the Colab runtime module is importable. |
Examples:
Source code in src/torchlingo/training_checkpoint.py
mount_drive
¶
Mount Google Drive, if running in Colab and not already mounted.
Safe to call unconditionally: outside Colab it does nothing and returns False, so notebooks do not need to branch on the environment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mount_point
|
Path | str
|
Where Drive should be mounted. |
DRIVE_MOUNT_POINT
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if Drive is mounted when this returns, False otherwise. |
Source code in src/torchlingo/training_checkpoint.py
default_checkpoint_dir
¶
Pick a sensible checkpoint directory for the current environment.
In Colab this is a folder on Drive, so checkpoints outlive the runtime. Anywhere else it is a local directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
experiment_name
|
str
|
Used as the final path segment. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Path |
Path
|
Directory to write checkpoints into. Not created here. |