Your PyTorch loop runs. The first batches look ordinary. Then the loss becomes NaN, or training loss keeps falling while validation loss turns upward. The final checkpoint exists, but nobody can explain why that particular model was selected.
The five training calls are correct. The run is not yet a controlled experiment.
Lesson details
- Module
- Deep learning
- Lesson
- 4 · Training mechanics
- Difficulty
- Beginner
- Learning time
- 75 minutes
Before you start
- A PyTorch forward, loss, zero_grad, backward, and step loop
- A train, validation, and protected test split
Skills you will gain
- Count optimiser updates from dataset size, batch size, epochs, and accumulation steps
- Compare SGD, momentum, Adam, and AdamW without treating one as a universal winner
- Build and explain a learning-rate warmup and decay rule
- Read training and validation curves without selecting on the final test set
- Use gradient clipping as a measured safeguard rather than a hidden repair
- Save and restore the best validation checkpoint with enough state to resume
Why this matters
Training mechanics are the operating decisions around the loop: how examples form updates, how large each update may be, when its size changes, which evidence selects a checkpoint, and what you record when something breaks.
Plain-English mental model
Step 1 — Count updates, not only epochs
A batchBatchA small group of training examples processed together in one forward and backward pass. Batches make computation practical and provide an estimate of the average training signal; their size affects memory use, speed, and the noisiness of updates. is the group of examples used for one forward and backward calculation. In the ordinary loop, one batch produces one optimiser update.
An epochEpochOne pass through the training examples as defined by a data loader. An epoch is an accounting unit, not proof that a model has learned enough or will work on new data. is one pass through the training loader. It measures data passes, not a fixed amount of learning. Changing batch size changes the number of updates inside the same epoch.
For N training examples and batch size B, when the last smaller batch is kept:
batches per epoch = ceiling(N / B)
updates = batches per epoch × epochs
With 1,000 examples and batch size 100, one epoch contains ten batches and ordinarily ten updates. Six epochs contain 60 updates.
from math import ceil
examples = 1_000
batch_size = 100
epochs = 6
batches_per_epoch = ceil(examples / batch_size)
updates = batches_per_epoch * epochs
print('batches per epoch:', batches_per_epoch)
print('updates:', updates)Batch size affects memory use, the amount of batch-to-batch gradient variation, and the number of updates. It does not make a split valid, repair leakage, or guarantee better generalisation.
Step 2 — Choose an update rule and a step size
An optimiserOptimiserAn algorithm that changes trainable parameters using their gradients and settings such as a learning rate. In PyTorch an optimiser owns the update rule; calling backward calculates gradients, while calling step applies an update. turns gradients into parameter updates. A learning rateLearning rateA positive setting that controls the size of an optimiser’s parameter update. Too large can overshoot or diverge; too small can make training impractically slow. It must be evaluated with the full training and validation process. controls the update scale.
Plain stochastic gradient descent, usually shortened to SGD, uses the current batch gradient. Momentum keeps a moving average of recent gradients, which can reduce repeated zigzagging. Adam keeps moving averages of gradients and squared gradients, then adapts the update scale by coordinate. AdamW applies weight decay separately from that adaptive gradient update.
These are different starting hypotheses, not a universal ranking. Optimiser state also matters when you resume: momentum and Adam-family methods remember earlier gradients.
For a single weight, the simplest update is:
new weight = old weight − learning rate × gradient
If weight = 1.0 and gradient = 0.5, a rate of 0.1 produces 0.95. A rate of 1.0 produces 0.5. The larger move is not automatically better. It may move quickly, overshoot, oscillate, or make later values non-finite.
Step 3 — Warm up, then decay for a stated reason
A learning-rate scheduleLearning-rate scheduleA rule that changes an optimiser’s learning rate during training, such as reducing it after validation progress plateaus. It is a hyperparameter strategy that needs protected validation evidence. changes the rate according to a declared rule.
Warmup begins below the chosen rate and increases over early updates. This can make early updates less abrupt while activations and optimiser state are still settling.
Decay reduces the rate later. Large early steps can make progress; smaller later steps can refine rather than repeatedly cross a narrow region.
Warmup and decay are not automatic repairs. Their duration and shape are more hyperparameters. Log the rate used for every update, and compare the scheduled run with a clear baseline.
This pure-Python schedule warms up for three updates and then decays linearly to one tenth of the starting rate:
def scheduled_rate(step: int, total_steps: int, peak: float, warmup: int) -> float:
if step <= warmup:
return peak * step / warmup
decay_progress = (step - warmup) / (total_steps - warmup)
return peak * (1.0 - 0.9 * decay_progress)
for step in range(1, 9):
rate = scheduled_rate(step, total_steps=8, peak=0.08, warmup=3)
print(step, f'{rate:.3f}')Important lines
step <= warmup- The first three optimiser updates increase from a smaller rate to the declared peak.
(step - warmup) / (total_steps - warmup)- Later updates are mapped onto a progress value from zero to one.
1.0 - 0.9 * decay_progress- The multiplier falls from 1.0 to 0.1, so the last rate is one tenth of the peak.
In PyTorch, call most schedulers after optimizer.step(). A metric-driven scheduler such as ReduceLROnPlateau receives a validation measure. Never give it final test loss: that would use the test set to configure training.
Step 4 — Read curves as evidence, not decoration
A learning curve records a measure across training progress. Plot training and validation measures against optimiser updates or epochs, and log the learning rate beside them.
- Training and validation losses both fall: the run is improving on these measures so far.
- Training loss falls while validation loss rises: the run has passed its best validation point, the split is unrepresentative, or evaluation differs from training in another important way.
- Both curves stay flat: the model may not receive useful gradients, the rate may be too small, targets or masks may be wrong, or the task may not be learnable from these inputs.
- Loss jumps or becomes
NaN: inspect data, arithmetic, gradients, and the most recent configuration change before adding a safeguard.
The lab below uses a deterministic one-parameter illustration. Its training and validation sets deliberately prefer slightly different values, so the best validation point can occur before the final update. The exact numbers do not describe a real neural network.
- Updates / epoch
- 8
- Effective batch
- 32
- Best validation
- 0.18 @ 1.5
- Clipped updates
- off
Training kept improving after validation passed its best point. The best checkpoint is earlier.
Try three controlled comparisons:
- Keep the defaults and note the best validation epoch.
- Set a high rate and choose Constant rate. Observe whether the run becomes unstable.
- Return to the default rate. Change only batch size or accumulation count and inspect the update count before interpreting the curves.
The chart uses a solid training line and a dashed validation line, so its meaning does not depend on colour.
Step 5 — Accumulate gradients when one batch does not fit
A micro-batch is the small batch that fits through the model once. Gradient accumulationGradient accumulationA technique that adds gradients from several micro-batches before one optimiser step. It creates a larger effective batch without holding all examples in memory at once; mean losses normally need scaling by the accumulation count. combines gradients from several micro-batches before one optimiser update.
If a micro-batch contains 16 examples and you accumulate four micro-batches, one update uses an effective batch size of 64 examples on one process:
effective batch = micro-batch size × accumulation steps
When the loss function returns a mean for each micro-batch, divide each loss by the number of accumulation steps before backward(). Then the stored gradients approximate the mean over the effective batch instead of a sum four times larger.
accumulation_steps = 4
optimizer.zero_grad()
for micro_step, (features, targets) in enumerate(train_loader, start=1):
loss = loss_fn(model(features), targets) / accumulation_steps
loss.backward()
if micro_step % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
Handle an incomplete final accumulation group deliberately. In distributed training, the effective global batch also depends on the number of worker processes. Do not compare two runs only by epoch when their update counts or effective batches differ.
Try it — break the scaling on purpose
This browser lab uses two simple gradient values. Run it, then set scale_each_micro_batch = False. The update gradient doubles from the intended average 3.0 to the sum 6.0.
Accumulation reduces activation-memory pressure because each micro-batch is smaller. It does not reproduce every behaviour of one physically larger batch: batch-dependent layers, randomness, distributed communication, and numerical order can differ.
Step 6 — Clip gradients, but keep the evidence
A gradient clipGradient clippingA technique that limits the size of gradients before an optimiser update, commonly by scaling them down when their overall norm exceeds a threshold. It can prevent a rare extreme update from destabilising training, but does not explain why gradients became extreme. limits gradient size before the optimiser update. Norm clipping measures all parameter gradients together and rescales them when their combined norm exceeds a threshold.
loss.backward()
gradient_norm = torch.nn.utils.clip_grad_norm_(
model.parameters(),
max_norm=1.0,
error_if_nonfinite=True,
)
optimizer.step()
print('norm before clipping:', gradient_norm.item())
clip_grad_norm_ changes gradients in place and returns their total norm before clipping. Log that value. If almost every update clips, the threshold may be hiding a scale, loss, rate, or data problem.
With automatic mixed precision, unscale the gradients before inspecting or clipping them. Otherwise the clipping threshold is applied to temporarily scaled values rather than the gradients the optimiser should use.
Clipping can prevent one unusually large update from dominating a run. It cannot explain why the gradient became large, make a NaN finite, or prove the model is learning the intended task.
Step 7 — Debug the first divergence, not the final wreckage
Non-finite means NaN, positive infinity, or negative infinity. Find the first value or update that becomes non-finite. Later failures are often consequences.
Use this order:
- Preserve the failing configuration, code revision, data version, and first failing batch.
- Return to the last known-good run and reproduce its result.
- Check input features, targets, loss, parameters, and gradients with
torch.isfinite. - Verify shapes, target ranges, masks, loss reduction, training/evaluation modes, and gradient clearing.
- Try to overfit one tiny batch. If loss cannot fall there, debug the training mechanics before adding more data.
- Log the learning rate and gradient norm before clipping.
- Change one factor and rerun the smallest reproduction.
torch.autograd.detect_anomaly() can report the forward operation connected to a failing backward operation and can raise when backward produces NaN. It slows execution, so use it on the small debugging reproduction rather than leaving it on for ordinary training.
Different symptoms suggest different first checks:
- Flat from the start: confirm parameters require gradients, the optimiser owns them,
step()runs, and the loss depends on the model output. - Wild but finite: inspect rate, batch variation, loss scale, gradient norm, and data outliers.
- Sudden non-finite value: inspect the first failing operation, division or logarithm inputs, mixed-precision scaling, and the preceding update.
- Training improves, validation worsens: audit split and evaluation code first; then compare an earlier checkpoint or a controlled generalisation change.
Step 8 — Stop and save with a declared validation rule
Early stoppingEarly stoppingA predefined rule that stops training when a validation measure has not improved for a chosen number of checks. It uses validation data to select duration and must not repeatedly expose the final test set. stops training after a validation measure has not improved for a chosen patience: a number of validation checks you are willing to wait.
Choose before final testing:
- the validation measure;
- whether lower or higher is better;
- the minimum change that counts as improvement;
- the patience;
- how often validation runs.
Save the best validation checkpointCheckpointA saved training artifact containing model state and supporting context such as optimiser state, epoch, configuration, and metrics. A checkpoint makes a run recoverable or investigable; it is not deployment evidence by itself., not merely the final epoch. A recoverable checkpoint normally contains:
- model state;
- optimiser state;
- scheduler state;
- epoch and optimiser-update number;
- best validation measure and selection rule;
- model and preprocessing configuration;
- data and split versions;
- code revision and relevant package versions;
- random seeds where reproducibility requires them.
if validation_loss < best_validation_loss - minimum_change:
best_validation_loss = validation_loss
checks_without_improvement = 0
torch.save(
{
'model_state': model.state_dict(),
'optimizer_state': optimizer.state_dict(),
'scheduler_state': scheduler.state_dict(),
'epoch': epoch,
'update': update,
'best_validation_loss': best_validation_loss,
'selection_rule': {
'minimum_change': minimum_change,
'patience': patience,
},
},
'best-validation.pt',
)
else:
checks_without_improvement += 1
Before the one final test evaluation, restore the selected checkpoint. Repeatedly testing different runs on the same test set turns that test set into configuration data.
Real-world AI engineering example
Common mistakes
Comparing epochs when batch or accumulation changed.
Fix — Record optimiser updates, examples processed, and effective batch as well as epochs.
Treating a larger batch as automatically better.
Fix — Measure memory, throughput, update count, and validation behaviour for the actual task.
Calling warmup and decay a repair.
Fix — Compare the schedule with a known baseline and log the rate used at each update.
Accumulating mean losses without dividing by the accumulation count.
Fix — Scale each micro-batch loss so the stored gradient represents the intended effective-batch mean.
Clipping every update without logging the pre-clip norm.
Fix — Record how often clipping activates and investigate scale, rate, loss, and data.
Changing optimiser, rate, batch, model, and data together.
Fix — Return to a reproducible baseline and change one controlled factor.
Selecting the final checkpoint by default.
Fix — Restore the best checkpoint under the declared validation rule.
Using the final test set for schedules or early stopping.
Fix — Use validation data for configuration; keep the test set protected for the final estimate.
Knowledge check
Knowledge check
Practice activity
Practice · 30–40 minutes
Make one training run recoverable
Goal: Create a run record that another engineer can select, resume, and investigate.
- Choose dataset size, batch size, epoch count, and accumulation count; calculate expected optimiser updates.
- Declare the validation measure, minimum improvement, patience, and validation frequency.
- Log epoch, update, training loss, validation loss, learning rate, and gradient norm before clipping.
- Save model, optimiser, and scheduler state whenever the validation rule improves.
- Restore the selected checkpoint before one final test evaluation.
- Write down one controlled response for a flat curve and one for a non-finite loss.
Expected result: Your record explains how many updates occurred, which settings produced them, why one checkpoint won, and how to reproduce the first failure.
Optional challenge: Add a small assertion that stops immediately when the loss or gradient norm is not finite.
Reveal solution
Example: 2,048 training examples with batch size 32 create 64 micro-batches per epoch. With four-way accumulation, that is 16 optimiser updates per epoch and an effective batch of 128 examples on one process. Over ten epochs the run expects 160 updates. A valid selection rule might be: “save when validation loss improves by at least 0.001; stop after five non-improving checks.” The exact values are task-specific. The important part is declaring and recording them before final testing.
Exercises
A loader has 1,000 examples, batch size 100, and does not drop its final batch. How many ordinary optimiser updates occur in one epoch and in six epochs?
Reveal solution
There are ceil(1000 / 100) = 10 batches and therefore 10 ordinary updates per epoch. Six epochs produce 10 × 6 = 60 updates. This assumes one optimiser step per batch and no gradient accumulation.
You have 16-example micro-batches and accumulate four of them before each optimiser step. What is the effective batch size for one process, and why should the loss normally be divided by four before each backward call?
Reveal solution
One update combines 16 × 4 = 64 examples. Dividing each mean loss by four makes the accumulated gradient correspond to the average across those four micro-batches. Without that scaling, the gradient sum is four times larger, changing the effective update size.
Training loss becomes `NaN` immediately after a learning-rate increase. Give a controlled recovery plan that preserves evidence.
Reveal solution
Keep the failing configuration and data version. Reproduce it on the same first failing batch if possible. Check inputs, targets, loss, parameters, and gradients for non-finite values. Restore the last known-good rate, confirm the run is finite, then retry one change. Log learning rate and the gradient norm before any clipping. Use anomaly detection only for the small debugging reproduction because it is slow.
Key takeaways
- Count optimiser updates and effective batch size; epochs alone hide important differences.
- Treat optimiser, rate, schedule, accumulation, and clipping as logged hypotheses.
- Use solid training evidence and separate validation evidence to diagnose and select a run.
- Preserve the first failure, inspect non-finite values, and change one factor from a known baseline.
- Restore the best validation checkpoint before one protected final test evaluation.
Flashcards
Flashcards
1 of 8Revision and interview questions
- Calculate updates and effective batch for 4,096 examples, batch size 16, four accumulation steps, and five epochs.
- Compare SGD, momentum, Adam, and AdamW without claiming one is universally best.
- Explain why loss scaling matters during gradient accumulation.
- Design a controlled investigation for a run that is flat from the first update.
- State an early-stopping and checkpoint rule that protects final test evidence.
One-page sketchnote summary
Print me · one-page revision sheet
Training mechanics: make the run explain itself
Main idea
A trustworthy training run makes every update countable, every configuration change visible, every failure reproducible, and every selected checkpoint explainable.
Core terms
- Batch — one forward/backward group
- Epoch — one loader pass
- Update — one optimiser step
- Schedule — a rate rule
- Accumulation — micro-batches per update
- Clipping — a measured gradient cap
- Checkpoint — recoverable state
One picture
Code pattern
batch → backward
accumulate? → clip? → step
validate → log → save bestWatch out
- Do not compare epochs alone
- Do not tune on final test data
- Do not hide unstable gradients
- Do not change every setting at once
Remember
- Count updates
- Log the current rate
- Read both curves
- Preserve the first failure
- Save best validation state
Three quick questions
- How large is the effective batch?
- When does the optimiser step?
- Which data selects the checkpoint?
Lesson checklist
0 of 8 completeResources
- docsPyTorch — Optimizers and learning-rate schedulersOfficial reference for optimiser state, scheduler choices, and the required step ordering.
- docsPyTorch — Automatic mixed precision examplesOfficial examples showing gradient accumulation and why scaled gradients must be unscaled before clipping.
- docsPyTorch — Automatic differentiation and anomaly detectionOfficial reference for tracing a failing backward operation and detecting non-finite gradients during debugging.